You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openoffice.apache.org by wa...@apache.org on 2020/10/28 15:42:37 UTC

[openoffice-org] branch main updated: Migrating scripts to assets

This is an automated email from the ASF dual-hosted git repository.

wave pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/openoffice-org.git


The following commit(s) were added to refs/heads/main by this push:
     new f946232  Migrating scripts to assets
f946232 is described below

commit f946232b1f98c3358b029d563b46e8bd48b5c000
Author: Dave Fisher <da...@davefisher.tech>
AuthorDate: Wed Oct 28 08:41:31 2020 -0700

    Migrating scripts to assets
---
 .../ajax/2.2.1.fake/simile-ajax-api-debug.js       | 4092 ++++++++++++++++++++
 .../ajax/2.2.1.fake/simile-ajax-api.js             |    7 +
 .../ajax/2.2.1.fake/simile-ajax-bundle.css         |  181 +
 .../ajax/2.2.1.fake/simile-ajax-bundle.js          |    1 +
 .../ajax/2.2.1/content/history.html                |    7 +
 .../ajax/2.2.1/images/bubble-arrow-point-down.png  |  Bin 0 -> 3654 bytes
 .../ajax/2.2.1/images/bubble-arrow-point-left.png  |  Bin 0 -> 3455 bytes
 .../ajax/2.2.1/images/bubble-arrow-point-right.png |  Bin 0 -> 3529 bytes
 .../ajax/2.2.1/images/bubble-arrow-point-up.png    |  Bin 0 -> 3413 bytes
 .../ajax/2.2.1/images/bubble-bottom-left.png       |  Bin 0 -> 3232 bytes
 .../ajax/2.2.1/images/bubble-bottom-right.png      |  Bin 0 -> 3291 bytes
 .../ajax/2.2.1/images/bubble-bottom.png            |  Bin 0 -> 3119 bytes
 .../ajax/2.2.1/images/bubble-left.png              |  Bin 0 -> 3601 bytes
 .../ajax/2.2.1/images/bubble-right.png             |  Bin 0 -> 3620 bytes
 .../ajax/2.2.1/images/bubble-top-left.png          |  Bin 0 -> 3115 bytes
 .../ajax/2.2.1/images/bubble-top-right.png         |  Bin 0 -> 3241 bytes
 .../ajax/2.2.1/images/bubble-top.png               |  Bin 0 -> 3095 bytes
 .../ajax/2.2.1/images/close-button.png             |  Bin 0 -> 624 bytes
 .../ajax/2.2.1/images/copy.png                     |  Bin 0 -> 196 bytes
 .../ajax/2.2.1/images/message-bottom-left.png      |  Bin 0 -> 1339 bytes
 .../ajax/2.2.1/images/message-bottom-right.png     |  Bin 0 -> 2074 bytes
 .../ajax/2.2.1/images/message-left.png             |  Bin 0 -> 717 bytes
 .../ajax/2.2.1/images/message-right.png            |  Bin 0 -> 1892 bytes
 .../ajax/2.2.1/images/message-top-left.png         |  Bin 0 -> 918 bytes
 .../ajax/2.2.1/images/message-top-right.png        |  Bin 0 -> 1604 bytes
 .../ajax/2.2.1/scripts/ajax.js                     |   45 +
 .../ajax/2.2.1/scripts/data-structure.js           |  447 +++
 .../ajax/2.2.1/scripts/date-time.js                |  452 +++
 .../ajax/2.2.1/scripts/debug.js                    |   94 +
 .../ajax/2.2.1/scripts/dom.js                      |  344 ++
 .../ajax/2.2.1/scripts/graphics.js                 |  653 ++++
 .../ajax/2.2.1/scripts/history.js                  |  220 ++
 .../ajax/2.2.1/scripts/html.js                     |  274 ++
 .../ajax/2.2.1/scripts/jquery-1.3.2.min.js         |   23 +
 .../ajax/2.2.1/scripts/json.js                     |  129 +
 .../ajax/2.2.1/scripts/platform.js                 |  114 +
 .../ajax/2.2.1/scripts/signal.js                   |   43 +
 .../ajax/2.2.1/scripts/string.js                   |   43 +
 .../ajax/2.2.1/scripts/units.js                    |   64 +
 .../ajax/2.2.1/scripts/window-manager.js           |  414 ++
 .../ajax/2.2.1/scripts/xmlhttp.js                  |  137 +
 .../ajax/2.2.1/simile-ajax-api.js                  |  214 +
 .../ajax/2.2.1/simile-ajax-bundle.js               | 2908 ++++++++++++++
 .../ajax/2.2.1/styles/graphics-ie6.css             |   77 +
 .../ajax/2.2.1/styles/graphics.css                 |  171 +
 .../ajax/2.2.2/compile-epilog.js                   |   16 +
 .../ajax/2.2.2/compile-prolog.js                   |    1 +
 .../ajax/2.2.2/content/history.html                |    7 +
 .../ajax/2.2.2/images/bubble-arrow-point-down.png  |  Bin 0 -> 3654 bytes
 .../ajax/2.2.2/images/bubble-arrow-point-left.png  |  Bin 0 -> 3455 bytes
 .../ajax/2.2.2/images/bubble-arrow-point-right.png |  Bin 0 -> 3529 bytes
 .../ajax/2.2.2/images/bubble-arrow-point-up.png    |  Bin 0 -> 3413 bytes
 .../ajax/2.2.2/images/bubble-bottom-left.png       |  Bin 0 -> 3232 bytes
 .../ajax/2.2.2/images/bubble-bottom-right.png      |  Bin 0 -> 3291 bytes
 .../ajax/2.2.2/images/bubble-bottom.png            |  Bin 0 -> 3119 bytes
 .../ajax/2.2.2/images/bubble-left.png              |  Bin 0 -> 3601 bytes
 .../ajax/2.2.2/images/bubble-right.png             |  Bin 0 -> 3620 bytes
 .../ajax/2.2.2/images/bubble-top-left.png          |  Bin 0 -> 3115 bytes
 .../ajax/2.2.2/images/bubble-top-right.png         |  Bin 0 -> 3241 bytes
 .../ajax/2.2.2/images/bubble-top.png               |  Bin 0 -> 3095 bytes
 .../ajax/2.2.2/images/close-button.png             |  Bin 0 -> 624 bytes
 .../ajax/2.2.2/images/copy.png                     |  Bin 0 -> 196 bytes
 .../ajax/2.2.2/images/message-bottom-left.png      |  Bin 0 -> 1339 bytes
 .../ajax/2.2.2/images/message-bottom-right.png     |  Bin 0 -> 2074 bytes
 .../ajax/2.2.2/images/message-left.png             |  Bin 0 -> 717 bytes
 .../ajax/2.2.2/images/message-right.png            |  Bin 0 -> 1892 bytes
 .../ajax/2.2.2/images/message-top-left.png         |  Bin 0 -> 918 bytes
 .../ajax/2.2.2/images/message-top-right.png        |  Bin 0 -> 1604 bytes
 .../api.simile-widgets.org/ajax/2.2.2/loader.js    |   34 +
 .../ajax/2.2.2/scripts/ajax.js                     |   45 +
 .../ajax/2.2.2/scripts/data-structure.js           |  447 +++
 .../ajax/2.2.2/scripts/date-time.js                |  452 +++
 .../ajax/2.2.2/scripts/debug.js                    |   94 +
 .../ajax/2.2.2/scripts/dom.js                      |  344 ++
 .../ajax/2.2.2/scripts/graphics.js                 |  653 ++++
 .../ajax/2.2.2/scripts/history.js                  |  220 ++
 .../ajax/2.2.2/scripts/html.js                     |  274 ++
 .../ajax/2.2.2/scripts/jquery-1.4.2.min.js         |  154 +
 .../ajax/2.2.2/scripts/json.js                     |  129 +
 .../ajax/2.2.2/scripts/platform.js                 |  114 +
 .../ajax/2.2.2/scripts/remoteLog.js                |   24 +
 .../ajax/2.2.2/scripts/signal.js                   |   43 +
 .../ajax/2.2.2/scripts/string.js                   |   43 +
 .../ajax/2.2.2/scripts/units.js                    |   73 +
 .../ajax/2.2.2/scripts/window-manager.js           |  414 ++
 .../ajax/2.2.2/scripts/xmlhttp.js                  |  137 +
 .../ajax/2.2.2/simile-ajax-api.js                  |  218 ++
 .../ajax/2.2.2/simile-ajax-bundle-debug.js         | 2911 ++++++++++++++
 .../ajax/2.2.2/simile-ajax-bundle.js               | 3387 ++++++++++++++++
 .../ajax/2.2.2/styles/graphics-ie6.css             |   77 +
 .../ajax/2.2.2/styles/graphics.css                 |  171 +
 .../ajax/2.2.3/compile-epilog.js                   |   16 +
 .../ajax/2.2.3/compile-prolog.js                   |    1 +
 .../ajax/2.2.3/content/history.html                |    7 +
 .../ajax/2.2.3/images/bubble-arrow-point-down.png  |  Bin 0 -> 3654 bytes
 .../ajax/2.2.3/images/bubble-arrow-point-left.png  |  Bin 0 -> 3455 bytes
 .../ajax/2.2.3/images/bubble-arrow-point-right.png |  Bin 0 -> 3529 bytes
 .../ajax/2.2.3/images/bubble-arrow-point-up.png    |  Bin 0 -> 3413 bytes
 .../ajax/2.2.3/images/bubble-bottom-left.png       |  Bin 0 -> 3232 bytes
 .../ajax/2.2.3/images/bubble-bottom-right.png      |  Bin 0 -> 3291 bytes
 .../ajax/2.2.3/images/bubble-bottom.png            |  Bin 0 -> 3119 bytes
 .../ajax/2.2.3/images/bubble-left.png              |  Bin 0 -> 3601 bytes
 .../ajax/2.2.3/images/bubble-right.png             |  Bin 0 -> 3620 bytes
 .../ajax/2.2.3/images/bubble-top-left.png          |  Bin 0 -> 3115 bytes
 .../ajax/2.2.3/images/bubble-top-right.png         |  Bin 0 -> 3241 bytes
 .../ajax/2.2.3/images/bubble-top.png               |  Bin 0 -> 3095 bytes
 .../ajax/2.2.3/images/close-button.png             |  Bin 0 -> 624 bytes
 .../ajax/2.2.3/images/copy.png                     |  Bin 0 -> 196 bytes
 .../ajax/2.2.3/images/message-bottom-left.png      |  Bin 0 -> 1339 bytes
 .../ajax/2.2.3/images/message-bottom-right.png     |  Bin 0 -> 2074 bytes
 .../ajax/2.2.3/images/message-left.png             |  Bin 0 -> 717 bytes
 .../ajax/2.2.3/images/message-right.png            |  Bin 0 -> 1892 bytes
 .../ajax/2.2.3/images/message-top-left.png         |  Bin 0 -> 918 bytes
 .../ajax/2.2.3/images/message-top-right.png        |  Bin 0 -> 1604 bytes
 .../api.simile-widgets.org/ajax/2.2.3/loader.js    |   34 +
 .../ajax/2.2.3/scripts/ajax.js                     |   45 +
 .../ajax/2.2.3/scripts/data-structure.js           |  447 +++
 .../ajax/2.2.3/scripts/date-time.js                |  452 +++
 .../ajax/2.2.3/scripts/debug.js                    |   94 +
 .../ajax/2.2.3/scripts/dom.js                      |  355 ++
 .../ajax/2.2.3/scripts/graphics.js                 |  653 ++++
 .../ajax/2.2.3/scripts/history.js                  |  220 ++
 .../ajax/2.2.3/scripts/html.js                     |  274 ++
 .../ajax/2.2.3/scripts/json.js                     |  129 +
 .../ajax/2.2.3/scripts/platform.js                 |  114 +
 .../ajax/2.2.3/scripts/remoteLog.js                |   24 +
 .../ajax/2.2.3/scripts/signal.js                   |   43 +
 .../ajax/2.2.3/scripts/string.js                   |   43 +
 .../ajax/2.2.3/scripts/units.js                    |   73 +
 .../ajax/2.2.3/scripts/window-manager.js           |  414 ++
 .../ajax/2.2.3/scripts/xmlhttp.js                  |  137 +
 .../ajax/2.2.3/simile-ajax-api.js                  |  219 ++
 .../ajax/2.2.3/simile-ajax-bundle-debug.js         | 1574 ++++++++
 .../ajax/2.2.3/simile-ajax-bundle.js               | 1574 ++++++++
 .../ajax/2.2.3/styles/graphics-ie6.css             |   77 +
 .../ajax/2.2.3/styles/graphics.css                 |  171 +
 .../ajax/2.2.4/simile-ajax-api-debug.js            | 4092 ++++++++++++++++++++
 .../ajax/2.2.4/simile-ajax-api.js                  |    7 +
 .../ajax/2.2.4/simile-ajax-bundle.css              |  181 +
 .../ajax/2.2.4/simile-ajax-bundle.js               |    1 +
 .../ajax/images/bubble-right.png                   |   21 +
 .../ajax/images/close-button.png                   |   21 +
 assets/scripts/api.simile-widgets.org/readme.txt   |    3 +
 .../timeline/2.3.1/ext/geochrono/geochrono-api.js  |   92 +
 .../timeline/2.3.1/ext/planning/planning-api.js    |   92 +
 .../timeline/2.3.1/images/blue-circle.png          |  Bin 0 -> 534 bytes
 .../timeline/2.3.1/images/bubble-bottom-arrow.png  |  Bin 0 -> 1087 bytes
 .../timeline/2.3.1/images/bubble-bottom-left.png   |  Bin 0 -> 754 bytes
 .../timeline/2.3.1/images/bubble-bottom-right.png  |  Bin 0 -> 803 bytes
 .../timeline/2.3.1/images/bubble-bottom.png        |  Bin 0 -> 1141 bytes
 .../timeline/2.3.1/images/bubble-left-arrow.png    |  Bin 0 -> 880 bytes
 .../timeline/2.3.1/images/bubble-left.png          |  Bin 0 -> 5670 bytes
 .../timeline/2.3.1/images/bubble-right-arrow.png   |  Bin 0 -> 941 bytes
 .../timeline/2.3.1/images/bubble-right.png         |  Bin 0 -> 5686 bytes
 .../timeline/2.3.1/images/bubble-top-arrow.png     |  Bin 0 -> 793 bytes
 .../timeline/2.3.1/images/bubble-top-left.png      |  Bin 0 -> 560 bytes
 .../timeline/2.3.1/images/bubble-top-right.png     |  Bin 0 -> 676 bytes
 .../timeline/2.3.1/images/bubble-top.png           |  Bin 0 -> 905 bytes
 .../timeline/2.3.1/images/close-button.png         |  Bin 0 -> 624 bytes
 .../timeline/2.3.1/images/copyright-vertical.png   |  Bin 0 -> 1695 bytes
 .../timeline/2.3.1/images/copyright.png            |  Bin 0 -> 1400 bytes
 .../timeline/2.3.1/images/dark-blue-circle.png     |  Bin 0 -> 551 bytes
 .../timeline/2.3.1/images/dark-green-circle.png    |  Bin 0 -> 513 bytes
 .../timeline/2.3.1/images/dark-red-circle.png      |  Bin 0 -> 497 bytes
 .../timeline/2.3.1/images/dull-blue-circle.png     |  Bin 0 -> 539 bytes
 .../timeline/2.3.1/images/dull-green-circle.png    |  Bin 0 -> 539 bytes
 .../timeline/2.3.1/images/dull-red-circle.png      |  Bin 0 -> 532 bytes
 .../timeline/2.3.1/images/gray-circle.png          |  Bin 0 -> 513 bytes
 .../timeline/2.3.1/images/green-circle.png         |  Bin 0 -> 544 bytes
 .../timeline/2.3.1/images/message-bottom-left.png  |  Bin 0 -> 1339 bytes
 .../timeline/2.3.1/images/message-bottom-right.png |  Bin 0 -> 2074 bytes
 .../timeline/2.3.1/images/message-left.png         |  Bin 0 -> 717 bytes
 .../timeline/2.3.1/images/message-right.png        |  Bin 0 -> 1892 bytes
 .../timeline/2.3.1/images/message-top-left.png     |  Bin 0 -> 918 bytes
 .../timeline/2.3.1/images/message-top-right.png    |  Bin 0 -> 1604 bytes
 .../timeline/2.3.1/images/progress-running.gif     |  Bin 0 -> 1002 bytes
 .../timeline/2.3.1/images/red-circle.png           |  Bin 0 -> 538 bytes
 .../timeline/2.3.1/images/top-bubble.png           |  Bin 0 -> 6092 bytes
 .../timeline/2.3.1/scripts/band.js                 |  733 ++++
 .../timeline/2.3.1/scripts/compact-painter.js      | 1041 +++++
 .../timeline/2.3.1/scripts/decorators.js           |  184 +
 .../timeline/2.3.1/scripts/detailed-painter.js     |  691 ++++
 .../timeline/2.3.1/scripts/ether-painters.js       |  576 +++
 .../timeline/2.3.1/scripts/ethers.js               |  305 ++
 .../timeline/2.3.1/scripts/event-utils.js          |   64 +
 .../timeline/2.3.1/scripts/ext/japanese-eras.js    |  395 ++
 .../timeline/2.3.1/scripts/labellers.js            |   91 +
 .../timeline/2.3.1/scripts/original-painter.js     |  674 ++++
 .../timeline/2.3.1/scripts/overview-painter.js     |  258 ++
 .../timeline/2.3.1/scripts/sources.js              |  567 +++
 .../timeline/2.3.1/scripts/themes.js               |  180 +
 .../timeline/2.3.1/scripts/timeline.js             |  645 +++
 .../timeline/2.3.1/scripts/units.js                |   68 +
 .../timeline/2.3.1/styles/ethers.css               |  120 +
 .../timeline/2.3.1/styles/events.css               |   45 +
 .../timeline/2.3.1/styles/timeline.css             |   65 +
 .../timeline/2.3.1/timeline-api.js                 |  280 ++
 .../timeline/2.3.1/timeline-bundle.css             |  229 ++
 .../timeline/2.3.1/timeline-bundle.js              | 2835 ++++++++++++++
 .../timeplot/1.1/images/copyright.png              |  Bin 0 -> 4291 bytes
 .../timeplot/1.1/images/line_left.png              |  Bin 0 -> 340 bytes
 .../timeplot/1.1/images/line_right.png             |  Bin 0 -> 340 bytes
 .../timeplot/1.1/images/progress-running.gif       |  Bin 0 -> 1002 bytes
 .../timeplot/1.1/lib/excanvas.js                   |  785 ++++
 .../timeplot/1.1/lib/firebug/errorIcon.png         |  Bin 0 -> 457 bytes
 .../timeplot/1.1/lib/firebug/firebug.css           |  209 +
 .../timeplot/1.1/lib/firebug/firebug.html          |   23 +
 .../timeplot/1.1/lib/firebug/firebug.js            |  672 ++++
 .../timeplot/1.1/lib/firebug/firebugx.js           |   10 +
 .../timeplot/1.1/lib/firebug/infoIcon.png          |  Bin 0 -> 524 bytes
 .../timeplot/1.1/lib/firebug/warningIcon.png       |  Bin 0 -> 516 bytes
 .../timeplot/1.1/locales/en/locale.js              |    0
 .../timeplot/1.1/scripts/color.js                  |  152 +
 .../timeplot/1.1/scripts/geometry.js               |  879 +++++
 .../timeplot/1.1/scripts/math.js                   |  193 +
 .../timeplot/1.1/scripts/plot.js                   |  400 ++
 .../timeplot/1.1/scripts/processor.js              |  122 +
 .../timeplot/1.1/scripts/sources.js                |  371 ++
 .../timeplot/1.1/scripts/timeplot.js               |  543 +++
 .../timeplot/1.1/styles/timeplot.css               |   95 +
 .../timeplot/1.1/timeplot-api.js                   |  189 +
 .../timeplot/1.1/timeplot-bundle.css               |   95 +
 .../timeplot/1.1/timeplot-bundle.js                | 1050 +++++
 assets/scripts/entourage.js                        |   80 +
 assets/scripts/google-analytics.js                 |   14 +
 assets/scripts/jquery-1.11.2.min.js                |    4 +
 assets/scripts/ooo.js                              |  340 ++
 assets/scripts/sorttable.js                        |  498 +++
 assets/scripts/tableDecorate.js                    |   48 +
 229 files changed, 50154 insertions(+)

diff --git a/assets/scripts/api.simile-widgets.org/ajax/2.2.1.fake/simile-ajax-api-debug.js b/assets/scripts/api.simile-widgets.org/ajax/2.2.1.fake/simile-ajax-api-debug.js
new file mode 100644
index 0000000..d926e66
--- /dev/null
+++ b/assets/scripts/api.simile-widgets.org/ajax/2.2.1.fake/simile-ajax-api-debug.js
@@ -0,0 +1,4092 @@
+(function(root, factory) {
+    if (typeof define === "function" && define.amd) {
+        define(factory);
+    } else {
+        root.SimileAjax = factory();
+    }
+}(this, function() {
+
+/**
+ * almond 0.2.5 Copyright (c) 2011-2012, The Dojo Foundation All Rights Reserved.
+ * Available via the MIT or new BSD license.
+ * see: http://github.com/jrburke/almond for details
+ */
+//Going sloppy to avoid 'use strict' string cost, but strict practices should
+//be followed.
+/*jslint sloppy: true */
+/*global setTimeout: false */
+
+var requirejs, require, define;
+(function (undef) {
+    var main, req, makeMap, handlers,
+        defined = {},
+        waiting = {},
+        config = {},
+        defining = {},
+        hasOwn = Object.prototype.hasOwnProperty,
+        aps = [].slice;
+
+    function hasProp(obj, prop) {
+        return hasOwn.call(obj, prop);
+    }
+
+    /**
+     * Given a relative module name, like ./something, normalize it to
+     * a real name that can be mapped to a path.
+     * @param {String} name the relative name
+     * @param {String} baseName a real name that the name arg is relative
+     * to.
+     * @returns {String} normalized name
+     */
+    function normalize(name, baseName) {
+        var nameParts, nameSegment, mapValue, foundMap,
+            foundI, foundStarMap, starI, i, j, part,
+            baseParts = baseName && baseName.split("/"),
+            map = config.map,
+            starMap = (map && map['*']) || {};
+
+        //Adjust any relative paths.
+        if (name && name.charAt(0) === ".") {
+            //If have a base name, try to normalize against it,
+            //otherwise, assume it is a top-level require that will
+            //be relative to baseUrl in the end.
+            if (baseName) {
+                //Convert baseName to array, and lop off the last part,
+                //so that . matches that "directory" and not name of the baseName's
+                //module. For instance, baseName of "one/two/three", maps to
+                //"one/two/three.js", but we want the directory, "one/two" for
+                //this normalization.
+                baseParts = baseParts.slice(0, baseParts.length - 1);
+
+                name = baseParts.concat(name.split("/"));
+
+                //start trimDots
+                for (i = 0; i < name.length; i += 1) {
+                    part = name[i];
+                    if (part === ".") {
+                        name.splice(i, 1);
+                        i -= 1;
+                    } else if (part === "..") {
+                        if (i === 1 && (name[2] === '..' || name[0] === '..')) {
+                            //End of the line. Keep at least one non-dot
+                            //path segment at the front so it can be mapped
+                            //correctly to disk. Otherwise, there is likely
+                            //no path mapping for a path starting with '..'.
+                            //This can still fail, but catches the most reasonable
+                            //uses of ..
+                            break;
+                        } else if (i > 0) {
+                            name.splice(i - 1, 2);
+                            i -= 2;
+                        }
+                    }
+                }
+                //end trimDots
+
+                name = name.join("/");
+            } else if (name.indexOf('./') === 0) {
+                // No baseName, so this is ID is resolved relative
+                // to baseUrl, pull off the leading dot.
+                name = name.substring(2);
+            }
+        }
+
+        //Apply map config if available.
+        if ((baseParts || starMap) && map) {
+            nameParts = name.split('/');
+
+            for (i = nameParts.length; i > 0; i -= 1) {
+                nameSegment = nameParts.slice(0, i).join("/");
+
+                if (baseParts) {
+                    //Find the longest baseName segment match in the config.
+                    //So, do joins on the biggest to smallest lengths of baseParts.
+                    for (j = baseParts.length; j > 0; j -= 1) {
+                        mapValue = map[baseParts.slice(0, j).join('/')];
+
+                        //baseName segment has  config, find if it has one for
+                        //this name.
+                        if (mapValue) {
+                            mapValue = mapValue[nameSegment];
+                            if (mapValue) {
+                                //Match, update name to the new value.
+                                foundMap = mapValue;
+                                foundI = i;
+                                break;
+                            }
+                        }
+                    }
+                }
+
+                if (foundMap) {
+                    break;
+                }
+
+                //Check for a star map match, but just hold on to it,
+                //if there is a shorter segment match later in a matching
+                //config, then favor over this star map.
+                if (!foundStarMap && starMap && starMap[nameSegment]) {
+                    foundStarMap = starMap[nameSegment];
+                    starI = i;
+                }
+            }
+
+            if (!foundMap && foundStarMap) {
+                foundMap = foundStarMap;
+                foundI = starI;
+            }
+
+            if (foundMap) {
+                nameParts.splice(0, foundI, foundMap);
+                name = nameParts.join('/');
+            }
+        }
+
+        return name;
+    }
+
+    function makeRequire(relName, forceSync) {
+        return function () {
+            //A version of a require function that passes a moduleName
+            //value for items that may need to
+            //look up paths relative to the moduleName
+            return req.apply(undef, aps.call(arguments, 0).concat([relName, forceSync]));
+        };
+    }
+
+    function makeNormalize(relName) {
+        return function (name) {
+            return normalize(name, relName);
+        };
+    }
+
+    function makeLoad(depName) {
+        return function (value) {
+            defined[depName] = value;
+        };
+    }
+
+    function callDep(name) {
+        if (hasProp(waiting, name)) {
+            var args = waiting[name];
+            delete waiting[name];
+            defining[name] = true;
+            main.apply(undef, args);
+        }
+
+        if (!hasProp(defined, name) && !hasProp(defining, name)) {
+            throw new Error('No ' + name);
+        }
+        return defined[name];
+    }
+
+    //Turns a plugin!resource to [plugin, resource]
+    //with the plugin being undefined if the name
+    //did not have a plugin prefix.
+    function splitPrefix(name) {
+        var prefix,
+            index = name ? name.indexOf('!') : -1;
+        if (index > -1) {
+            prefix = name.substring(0, index);
+            name = name.substring(index + 1, name.length);
+        }
+        return [prefix, name];
+    }
+
+    /**
+     * Makes a name map, normalizing the name, and using a plugin
+     * for normalization if necessary. Grabs a ref to plugin
+     * too, as an optimization.
+     */
+    makeMap = function (name, relName) {
+        var plugin,
+            parts = splitPrefix(name),
+            prefix = parts[0];
+
+        name = parts[1];
+
+        if (prefix) {
+            prefix = normalize(prefix, relName);
+            plugin = callDep(prefix);
+        }
+
+        //Normalize according
+        if (prefix) {
+            if (plugin && plugin.normalize) {
+                name = plugin.normalize(name, makeNormalize(relName));
+            } else {
+                name = normalize(name, relName);
+            }
+        } else {
+            name = normalize(name, relName);
+            parts = splitPrefix(name);
+            prefix = parts[0];
+            name = parts[1];
+            if (prefix) {
+                plugin = callDep(prefix);
+            }
+        }
+
+        //Using ridiculous property names for space reasons
+        return {
+            f: prefix ? prefix + '!' + name : name, //fullName
+            n: name,
+            pr: prefix,
+            p: plugin
+        };
+    };
+
+    function makeConfig(name) {
+        return function () {
+            return (config && config.config && config.config[name]) || {};
+        };
+    }
+
+    handlers = {
+        require: function (name) {
+            return makeRequire(name);
+        },
+        exports: function (name) {
+            var e = defined[name];
+            if (typeof e !== 'undefined') {
+                return e;
+            } else {
+                return (defined[name] = {});
+            }
+        },
+        module: function (name) {
+            return {
+                id: name,
+                uri: '',
+                exports: defined[name],
+                config: makeConfig(name)
+            };
+        }
+    };
+
+    main = function (name, deps, callback, relName) {
+        var cjsModule, depName, ret, map, i,
+            args = [],
+            usingExports;
+
+        //Use name if no relName
+        relName = relName || name;
+
+        //Call the callback to define the module, if necessary.
+        if (typeof callback === 'function') {
+
+            //Pull out the defined dependencies and pass the ordered
+            //values to the callback.
+            //Default to [require, exports, module] if no deps
+            deps = !deps.length && callback.length ? ['require', 'exports', 'module'] : deps;
+            for (i = 0; i < deps.length; i += 1) {
+                map = makeMap(deps[i], relName);
+                depName = map.f;
+
+                //Fast path CommonJS standard dependencies.
+                if (depName === "require") {
+                    args[i] = handlers.require(name);
+                } else if (depName === "exports") {
+                    //CommonJS module spec 1.1
+                    args[i] = handlers.exports(name);
+                    usingExports = true;
+                } else if (depName === "module") {
+                    //CommonJS module spec 1.1
+                    cjsModule = args[i] = handlers.module(name);
+                } else if (hasProp(defined, depName) ||
+                           hasProp(waiting, depName) ||
+                           hasProp(defining, depName)) {
+                    args[i] = callDep(depName);
+                } else if (map.p) {
+                    map.p.load(map.n, makeRequire(relName, true), makeLoad(depName), {});
+                    args[i] = defined[depName];
+                } else {
+                    throw new Error(name + ' missing ' + depName);
+                }
+            }
+
+            ret = callback.apply(defined[name], args);
+
+            if (name) {
+                //If setting exports via "module" is in play,
+                //favor that over return value and exports. After that,
+                //favor a non-undefined return value over exports use.
+                if (cjsModule && cjsModule.exports !== undef &&
+                        cjsModule.exports !== defined[name]) {
+                    defined[name] = cjsModule.exports;
+                } else if (ret !== undef || !usingExports) {
+                    //Use the return value from the function.
+                    defined[name] = ret;
+                }
+            }
+        } else if (name) {
+            //May just be an object definition for the module. Only
+            //worry about defining if have a module name.
+            defined[name] = callback;
+        }
+    };
+
+    requirejs = require = req = function (deps, callback, relName, forceSync, alt) {
+        if (typeof deps === "string") {
+            if (handlers[deps]) {
+                //callback in this case is really relName
+                return handlers[deps](callback);
+            }
+            //Just return the module wanted. In this scenario, the
+            //deps arg is the module name, and second arg (if passed)
+            //is just the relName.
+            //Normalize module name, if it contains . or ..
+            return callDep(makeMap(deps, callback).f);
+        } else if (!deps.splice) {
+            //deps is a config object, not an array.
+            config = deps;
+            if (callback.splice) {
+                //callback is an array, which means it is a dependency list.
+                //Adjust args if there are dependencies
+                deps = callback;
+                callback = relName;
+                relName = null;
+            } else {
+                deps = undef;
+            }
+        }
+
+        //Support require(['a'])
+        callback = callback || function () {};
+
+        //If relName is a function, it is an errback handler,
+        //so remove it.
+        if (typeof relName === 'function') {
+            relName = forceSync;
+            forceSync = alt;
+        }
+
+        //Simulate async callback;
+        if (forceSync) {
+            main(undef, deps, callback, relName);
+        } else {
+            //Using a non-zero value because of concern for what old browsers
+            //do, and latest browsers "upgrade" to 4 if lower value is used:
+            //http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html#dom-windowtimers-settimeout:
+            //If want a value immediately, use require('id') instead -- something
+            //that works in almond on the global level, but not guaranteed and
+            //unlikely to work in other AMD implementations.
+            setTimeout(function () {
+                main(undef, deps, callback, relName);
+            }, 4);
+        }
+
+        return req;
+    };
+
+    /**
+     * Just drops the config on the floor, but returns req in case
+     * the config return value is used.
+     */
+    req.config = function (cfg) {
+        config = cfg;
+        if (config.deps) {
+            req(config.deps, config.callback);
+        }
+        return req;
+    };
+
+    define = function (name, deps, callback) {
+
+        //This module may not have dependencies
+        if (!deps.splice) {
+            //deps is not an array, so probably means
+            //an object literal or factory function for
+            //the value. Adjust args.
+            callback = deps;
+            deps = [];
+        }
+
+        if (!hasProp(defined, name) && !hasProp(waiting, name)) {
+            waiting[name] = [name, deps, callback];
+        }
+    };
+
+    define.amd = {
+        jQuery: true
+    };
+}());
+
+define("lib/almond", function(){});
+
+define('scripts/simile-ajax-base',[],function() {
+    var SimileAjax = {
+        loaded:                 false,
+        loadingScriptsCount:    0,
+        error:                  null,
+        params:                 { "bundle": true },
+        paramTypes:             { "bundle": Boolean },
+        version:                "3.0.0",
+        jQuery:                 null, // use jQuery directly
+        urlPrefix:              null
+    };
+    
+    return SimileAjax;
+});
+
+/*==================================================
+ *  Platform Utility Functions and Constants
+ *==================================================
+ */
+
+define('scripts/platform',[],function() {
+var Platform = {};
+
+Platform.os = {
+    isMac:   false,
+    isWin:   false,
+    isWin32: false,
+    isUnix:  false
+};
+Platform.browser = {
+    isIE:           false,
+    isNetscape:     false,
+    isMozilla:      false,
+    isFirefox:      false,
+    isOpera:        false,
+    isSafari:       false,
+
+    majorVersion:   0,
+    minorVersion:   0
+};
+
+(function() {
+    var an = navigator.appName.toLowerCase();
+	var ua = navigator.userAgent.toLowerCase(); 
+    
+    /*
+     *  Operating system
+     */
+	Platform.os.isMac = (ua.indexOf('mac') != -1);
+	Platform.os.isWin = (ua.indexOf('win') != -1);
+	Platform.os.isWin32 = Platform.isWin && (   
+        ua.indexOf('95') != -1 || 
+        ua.indexOf('98') != -1 || 
+        ua.indexOf('nt') != -1 || 
+        ua.indexOf('win32') != -1 || 
+        ua.indexOf('32bit') != -1
+    );
+	Platform.os.isUnix = (ua.indexOf('x11') != -1);
+    
+    /*
+     *  Browser
+     */
+    Platform.browser.isIE = (an.indexOf("microsoft") != -1);
+    Platform.browser.isNetscape = (an.indexOf("netscape") != -1);
+    Platform.browser.isMozilla = (ua.indexOf("mozilla") != -1);
+    Platform.browser.isFirefox = (ua.indexOf("firefox") != -1);
+    Platform.browser.isOpera = (an.indexOf("opera") != -1);
+    Platform.browser.isSafari = (an.indexOf("safari") != -1);
+    
+    var parseVersionString = function(s) {
+        var a = s.split(".");
+        Platform.browser.majorVersion = parseInt(a[0]);
+        Platform.browser.minorVersion = parseInt(a[1]);
+    };
+    var indexOf = function(s, sub, start) {
+        var i = s.indexOf(sub, start);
+        return i >= 0 ? i : s.length;
+    };
+    
+    if (Platform.browser.isMozilla) {
+        var offset = ua.indexOf("mozilla/");
+        if (offset >= 0) {
+            parseVersionString(ua.substring(offset + 8, indexOf(ua, " ", offset)));
+        }
+    }
+    if (Platform.browser.isIE) {
+        var offset = ua.indexOf("msie ");
+        if (offset >= 0) {
+            parseVersionString(ua.substring(offset + 5, indexOf(ua, ";", offset)));
+        }
+    }
+    if (Platform.browser.isNetscape) {
+        var offset = ua.indexOf("rv:");
+        if (offset >= 0) {
+            parseVersionString(ua.substring(offset + 3, indexOf(ua, ")", offset)));
+        }
+    }
+    if (Platform.browser.isFirefox) {
+        var offset = ua.indexOf("firefox/");
+        if (offset >= 0) {
+            parseVersionString(ua.substring(offset + 8, indexOf(ua, " ", offset)));
+        }
+    }
+    
+    if (!("localeCompare" in String.prototype)) {
+        String.prototype.localeCompare = function (s) {
+            if (this < s) return -1;
+            else if (this > s) return 1;
+            else return 0;
+        };
+    }
+})();
+
+Platform.getDefaultLocale = function() {
+    return Platform.clientLocale;
+};
+
+    return Platform;
+});
+
+/*==================================================
+ *  Debug Utility Functions
+ *==================================================
+ */
+
+define('scripts/debug',["./simile-ajax-base"], function(SimileAjax) {
+var Debug = {
+    silent: false
+};
+
+Debug.log = function(msg) {
+    var f;
+    if ("console" in window && "log" in window.console) { // FireBug installed
+        f = function(msg2) {
+            console.log(msg2);
+        }
+    } else {
+        f = function(msg2) {
+            if (!Debug.silent) {
+                alert(msg2);
+            }
+        }
+    }
+    Debug.log = f;
+    f(msg);
+};
+
+Debug.warn = function(msg) {
+    var f;
+    if ("console" in window && "warn" in window.console) { // FireBug installed
+        f = function(msg2) {
+            console.warn(msg2);
+        }
+    } else {
+        f = function(msg2) {
+            if (!Debug.silent) {
+                alert(msg2);
+            }
+        }
+    }
+    Debug.warn = f;
+    f(msg);
+};
+
+Debug.exception = function(e, msg) {
+    var f, params = SimileAjax.parseURLParameters();
+    if (params.errors == "throw" || SimileAjax.params.errors == "throw") {
+        f = function(e2, msg2) {
+            throw(e2); // do not hide from browser's native debugging features
+        };
+    } else if ("console" in window && "error" in window.console) { // FireBug installed
+        f = function(e2, msg2) {
+            if (msg2 != null) {
+                console.error(msg2 + " %o", e2);
+            } else {
+                console.error(e2);
+            }
+            throw(e2); // do not hide from browser's native debugging features
+        };
+    } else {
+        f = function(e2, msg2) {
+            if (!Debug.silent) {
+                alert("Caught exception: " + msg2 + "\n\nDetails: " + ("description" in e2 ? e2.description : e2));
+            }
+            throw(e2); // do not hide from browser's native debugging features
+        };
+    }
+    Debug.exception = f;
+    f(e, msg);
+};
+
+Debug.objectToString = function(o) {
+    return Debug._objectToString(o, "");
+};
+
+Debug._objectToString = function(o, indent) {
+    var indent2 = indent + " ";
+    if (typeof o == "object") {
+        var s = "{";
+        for (n in o) {
+            s += indent2 + n + ": " + Debug._objectToString(o[n], indent2) + "\n";
+        }
+        s += indent + "}";
+        return s;
+    } else if (typeof o == "array") {
+        var s = "[";
+        for (var n = 0; n < o.length; n++) {
+            s += Debug._objectToString(o[n], indent2) + "\n";
+        }
+        s += indent + "]";
+        return s;
+    } else {
+        return o;
+    }
+};
+
+    return Debug;
+});
+
+/**
+ * @fileOverview XmlHttp utility functions
+ * @name SimileAjax.XmlHttp
+ */
+
+define('scripts/xmlhttp',["./debug", "./platform"], function(Debug, Platform) {
+var XmlHttp = new Object();
+
+/**
+ *  Callback for XMLHttp onRequestStateChange.
+ */
+XmlHttp._onReadyStateChange = function(xmlhttp, fError, fDone) {
+    switch (xmlhttp.readyState) {
+    // 1: Request not yet made
+    // 2: Contact established with server but nothing downloaded yet
+    // 3: Called multiple while downloading in progress
+    
+    // Download complete
+    case 4:
+        try {
+            if (xmlhttp.status == 0     // file:// urls, works on Firefox
+             || xmlhttp.status == 200   // http:// urls
+            ) {
+                if (fDone) {
+                    fDone(xmlhttp);
+                }
+            } else {
+                if (fError) {
+                    fError(
+                        xmlhttp.statusText,
+                        xmlhttp.status,
+                        xmlhttp
+                    );
+                }
+            }
+        } catch (e) {
+            Debug.exception("XmlHttp: Error handling onReadyStateChange", e);
+        }
+        break;
+    }
+};
+
+/**
+ *  Creates an XMLHttpRequest object. On the first run, this
+ *  function creates a platform-specific function for
+ *  instantiating an XMLHttpRequest object and then replaces
+ *  itself with that function.
+ */
+XmlHttp._createRequest = function() {
+    if (Platform.browser.isIE) {
+        var programIDs = [
+        "Msxml2.XMLHTTP",
+        "Microsoft.XMLHTTP",
+        "Msxml2.XMLHTTP.4.0"
+        ];
+        for (var i = 0; i < programIDs.length; i++) {
+            try {
+                var programID = programIDs[i];
+                var f = function() {
+                    return new ActiveXObject(programID);
+                };
+                var o = f();
+                
+                // We are replacing the _createXmlHttpRequest
+                // function with this inner function as we've
+                // found out that it works. This is so that we
+                // don't have to do all the testing over again
+                // on subsequent calls.
+                XmlHttp._createRequest = f;
+                
+                return o;
+            } catch (e) {
+                // silent
+            }
+        }
+        // fall through to try new XMLHttpRequest();
+    }
+
+    try {
+        var f = function() {
+            return new XMLHttpRequest();
+        };
+        var o = f();
+        
+        // We are replacing the _createXmlHttpRequest
+        // function with this inner function as we've
+        // found out that it works. This is so that we
+        // don't have to do all the testing over again
+        // on subsequent calls.
+        XmlHttp._createRequest = f;
+        
+        return o;
+    } catch (e) {
+        throw new Error("Failed to create an XMLHttpRequest object");
+    }
+};
+
+/**
+ * Performs an asynchronous HTTP GET.
+ *  
+ * @param {Function} fError a function of the form 
+     function(statusText, statusCode, xmlhttp)
+ * @param {Function} fDone a function of the form function(xmlhttp)
+ */
+XmlHttp.get = function(url, fError, fDone) {
+    var xmlhttp = XmlHttp._createRequest();
+    
+    xmlhttp.open("GET", url, true);
+    xmlhttp.onreadystatechange = function() {
+        XmlHttp._onReadyStateChange(xmlhttp, fError, fDone);
+    };
+    xmlhttp.send(null);
+};
+
+/**
+ * Performs an asynchronous HTTP POST.
+ *  
+ * @param {Function} fError a function of the form 
+     function(statusText, statusCode, xmlhttp)
+ * @param {Function} fDone a function of the form function(xmlhttp)
+ */
+XmlHttp.post = function(url, body, fError, fDone) {
+    var xmlhttp = XmlHttp._createRequest();
+    
+    xmlhttp.open("POST", url, true);
+    xmlhttp.onreadystatechange = function() {
+        XmlHttp._onReadyStateChange(xmlhttp, fError, fDone);
+    };
+    xmlhttp.send(body);
+};
+
+XmlHttp._forceXML = function(xmlhttp) {
+    try {
+        xmlhttp.overrideMimeType("text/xml");
+    } catch (e) {
+        xmlhttp.setrequestheader("Content-Type", "text/xml");
+    }
+};
+
+    return XmlHttp;
+});
+
+/*==================================================
+ *  DOM Utility Functions
+ *==================================================
+ */
+
+define('scripts/dom',["./platform"], function(Platform) {
+var DOM = new Object();
+
+DOM.registerEventWithObject = function(elmt, eventName, obj, handlerName) {
+    DOM.registerEvent(elmt, eventName, function(elmt2, evt, target) {
+        return obj[handlerName].call(obj, elmt2, evt, target);
+    });
+};
+
+DOM.registerEvent = function(elmt, eventName, handler) {
+    var handler2 = function(evt) {
+        evt = (evt) ? evt : ((event) ? event : null);
+        if (evt) {
+            var target = (evt.target) ? 
+                evt.target : ((evt.srcElement) ? evt.srcElement : null);
+            if (target) {
+                target = (target.nodeType == 1 || target.nodeType == 9) ? 
+                    target : target.parentNode;
+            }
+            
+            return handler(elmt, evt, target);
+        }
+        return true;
+    }
+    
+    if (Platform.browser.isIE) {
+        elmt.attachEvent("on" + eventName, handler2);
+    } else {
+        elmt.addEventListener(eventName, handler2, false);
+    }
+};
+
+DOM.getPageCoordinates = function(elmt) {
+    var left = 0;
+    var top = 0;
+    
+    if (elmt.nodeType != 1) {
+        elmt = elmt.parentNode;
+    }
+    
+    var elmt2 = elmt;
+    while (elmt2 != null) {
+        left += elmt2.offsetLeft;
+        top += elmt2.offsetTop;
+        elmt2 = elmt2.offsetParent;
+    }
+    
+    var body = document.body;
+    while (elmt != null && elmt != body) {
+        if ("scrollLeft" in elmt) {
+            left -= elmt.scrollLeft;
+            top -= elmt.scrollTop;
+        }
+        elmt = elmt.parentNode;
+    }
+    
+    return { left: left, top: top };
+};
+
+DOM.getSize = function(elmt) {
+	var w = this.getStyle(elmt,"width");
+	var h = this.getStyle(elmt,"height");
+	if (w.indexOf("px") > -1) w = w.replace("px","");
+	if (h.indexOf("px") > -1) h = h.replace("px","");
+	return {
+		w: w,
+		h: h
+	}
+}
+
+DOM.getStyle = function(elmt, styleProp) {
+    if (elmt.currentStyle) { // IE
+        var style = elmt.currentStyle[styleProp];
+    } else if (window.getComputedStyle) { // standard DOM
+        var style = document.defaultView.getComputedStyle(elmt, null).getPropertyValue(styleProp);
+    } else {
+    	var style = "";
+    }
+    return style;
+}
+
+DOM.getEventRelativeCoordinates = function(evt, elmt) {
+    if (Platform.browser.isIE) {
+      if (evt.type == "mousewheel") {
+        var coords = DOM.getPageCoordinates(elmt);
+        return {
+          x: evt.clientX - coords.left, 
+          y: evt.clientY - coords.top
+        };        
+      } else {
+        return {
+          x: evt.offsetX,
+          y: evt.offsetY
+        };
+      }
+    } else {
+        var coords = DOM.getPageCoordinates(elmt);
+
+        if ((evt.type == "DOMMouseScroll") &&
+          Platform.browser.isFirefox &&
+          (Platform.browser.majorVersion == 2)) {
+          // Due to: https://bugzilla.mozilla.org/show_bug.cgi?id=352179                  
+
+          return {
+            x: evt.screenX - coords.left,
+            y: evt.screenY - coords.top 
+          };
+        } else {
+          return {
+              x: evt.pageX - coords.left,
+              y: evt.pageY - coords.top
+          };
+        }
+    }
+};
+
+DOM.getEventPageCoordinates = function(evt) {
+    if (Platform.browser.isIE) {
+
+        var scrOfY = 0;
+        var scrOfX = 0;
+
+        if (document.body && (document.body.scrollLeft || document.body.scrollTop)) {
+            //DOM compliant
+            scrOfY = document.body.scrollTop;
+            scrOfX = document.body.scrollLeft;
+        } else if (document.documentElement && (document.documentElement.scrollLeft || document.documentElement.scrollTop)) {
+            //IE6 standards compliant mode
+            scrOfY = document.documentElement.scrollTop;
+            scrOfX = document.documentElement.scrollLeft;
+        }
+
+        return { x: evt.clientX + scrOfX, y: evt.clientY + scrOfY }; 
+    } else {
+        return {
+            x: evt.pageX,
+            y: evt.pageY
+        };
+    }
+};
+
+DOM.hittest = function(x, y, except) {
+    return DOM._hittest(document.body, x, y, except);
+};
+
+DOM._hittest = function(elmt, x, y, except) {
+    var childNodes = elmt.childNodes;
+    outer: for (var i = 0; i < childNodes.length; i++) {
+        var childNode = childNodes[i];
+        for (var j = 0; j < except.length; j++) {
+            if (childNode == except[j]) {
+                continue outer;
+            }
+        }
+        
+        if (childNode.offsetWidth == 0 && childNode.offsetHeight == 0) {
+            /*
+             *  Sometimes SPAN elements have zero width and height but
+             *  they have children like DIVs that cover non-zero areas.
+             */
+            var hitNode = DOM._hittest(childNode, x, y, except);
+            if (hitNode != childNode) {
+                return hitNode;
+            }
+        } else {
+            var top = 0;
+            var left = 0;
+            
+            var node = childNode;
+            while (node) {
+                top += node.offsetTop;
+                left += node.offsetLeft;
+                node = node.offsetParent;
+            }
+            
+            if (left <= x && top <= y && (x - left) < childNode.offsetWidth && (y - top) < childNode.offsetHeight) {
+                return DOM._hittest(childNode, x, y, except);
+            } else if (childNode.nodeType == 1 && childNode.tagName == "TR") {
+                /*
+                 *  Table row might have cells that span several rows.
+                 */
+                var childNode2 = DOM._hittest(childNode, x, y, except);
+                if (childNode2 != childNode) {
+                    return childNode2;
+                }
+            }
+        }
+    }
+    return elmt;
+};
+
+DOM.cancelEvent = function(evt) {
+    evt.returnValue = false;
+    evt.cancelBubble = true;
+    if ("preventDefault" in evt) {
+        evt.preventDefault();
+    }
+};
+
+DOM.appendClassName = function(elmt, className) {
+    var classes = elmt.className.split(" ");
+    for (var i = 0; i < classes.length; i++) {
+        if (classes[i] == className) {
+            return;
+        }
+    }
+    classes.push(className);
+    elmt.className = classes.join(" ");
+};
+
+DOM.createInputElement = function(type) {
+    var div = document.createElement("div");
+    div.innerHTML = "<input type='" + type + "' />";
+    
+    return div.firstChild;
+};
+
+DOM.createDOMFromTemplate = function(template) {
+    var result = {};
+    result.elmt = DOM._createDOMFromTemplate(template, result, null);
+    
+    return result;
+};
+
+DOM._createDOMFromTemplate = function(templateNode, result, parentElmt) {
+    if (templateNode == null) {
+        /*
+        var node = doc.createTextNode("--null--");
+        if (parentElmt != null) {
+            parentElmt.appendChild(node);
+        }
+        return node;
+        */
+        return null;
+    } else if (typeof templateNode != "object") {
+        var node = document.createTextNode(templateNode);
+        if (parentElmt != null) {
+            parentElmt.appendChild(node);
+        }
+        return node;
+    } else {
+        var elmt = null;
+        if ("tag" in templateNode) {
+            var tag = templateNode.tag;
+            if (parentElmt != null) {
+                if (tag == "tr") {
+                    elmt = parentElmt.insertRow(parentElmt.rows.length);
+                } else if (tag == "td") {
+                    elmt = parentElmt.insertCell(parentElmt.cells.length);
+                }
+            }
+            if (elmt == null) {
+                elmt = tag == "input" ?
+                    DOM.createInputElement(templateNode.type) :
+                    document.createElement(tag);
+                    
+                if (parentElmt != null) {
+                    parentElmt.appendChild(elmt);
+                }
+            }
+        } else {
+            elmt = templateNode.elmt;
+            if (parentElmt != null) {
+                parentElmt.appendChild(elmt);
+            }
+        }
+        
+        for (var attribute in templateNode) {
+            var value = templateNode[attribute];
+            
+            if (attribute == "field") {
+                result[value] = elmt;
+                
+            } else if (attribute == "className") {
+                elmt.className = value;
+            } else if (attribute == "id") {
+                elmt.id = value;
+            } else if (attribute == "title") {
+                elmt.title = value;
+            } else if (attribute == "type" && elmt.tagName == "input") {
+                // do nothing
+            } else if (attribute == "style") {
+                for (n in value) {
+                    var v = value[n];
+                    if (n == "float") {
+                        n = Platform.browser.isIE ? "styleFloat" : "cssFloat";
+                    }
+                    elmt.style[n] = v;
+                }
+            } else if (attribute == "children") {
+                for (var i = 0; i < value.length; i++) {
+                    DOM._createDOMFromTemplate(value[i], result, elmt);
+                }
+            } else if (attribute != "tag" && attribute != "elmt") {
+                elmt.setAttribute(attribute, value);
+            }
+        }
+        return elmt;
+    }
+}
+
+DOM._cachedParent = null;
+DOM.createElementFromString = function(s) {
+    if (DOM._cachedParent == null) {
+        DOM._cachedParent = document.createElement("div");
+    }
+    DOM._cachedParent.innerHTML = s;
+    return DOM._cachedParent.firstChild;
+};
+
+DOM.createDOMFromString = function(root, s, fieldElmts) {
+    var elmt = typeof root == "string" ? document.createElement(root) : root;
+    elmt.innerHTML = s;
+    
+    var dom = { elmt: elmt };
+    DOM._processDOMChildrenConstructedFromString(dom, elmt, fieldElmts != null ? fieldElmts : {} );
+    
+    return dom;
+};
+
+DOM._processDOMConstructedFromString = function(dom, elmt, fieldElmts) {
+    var id = elmt.id;
+    if (id != null && id.length > 0) {
+        elmt.removeAttribute("id");
+        if (id in fieldElmts) {
+            var parentElmt = elmt.parentNode;
+            parentElmt.insertBefore(fieldElmts[id], elmt);
+            parentElmt.removeChild(elmt);
+            
+            dom[id] = fieldElmts[id];
+            return;
+        } else {
+            dom[id] = elmt;
+        }
+    }
+    
+    if (elmt.hasChildNodes()) {
+        DOM._processDOMChildrenConstructedFromString(dom, elmt, fieldElmts);
+    }
+};
+
+DOM._processDOMChildrenConstructedFromString = function(dom, elmt, fieldElmts) {
+    var node = elmt.firstChild;
+    while (node != null) {
+        var node2 = node.nextSibling;
+        if (node.nodeType == 1) {
+            DOM._processDOMConstructedFromString(dom, node, fieldElmts);
+        }
+        node = node2;
+    }
+};
+
+    return DOM;
+});
+
+/**
+ * @fileOverview Graphics utility functions and constants
+ * @name SimileAjax.Graphics
+ */
+
+define('scripts/graphics',[
+    "./simile-ajax-base",
+    "./platform"
+], function(SimileAjax, Platform) {
+    var Graphics = {
+        "pngIsTranslucent": undefined,
+        "createTranslucentImage": undefined,
+        "createTranslucentImageHTML": undefined
+    };
+
+/*==================================================
+ *  Opacity, translucency
+ *==================================================
+ */
+Graphics._createTranslucentImage1 = function(url, verticalAlign) {
+    var elmt = document.createElement("img");
+    elmt.setAttribute("src", url);
+    if (verticalAlign != null) {
+        elmt.style.verticalAlign = verticalAlign;
+    }
+    return elmt;
+};
+Graphics._createTranslucentImage2 = function(url, verticalAlign) {
+    var elmt = document.createElement("img");
+    elmt.style.width = "1px";  // just so that IE will calculate the size property
+    elmt.style.height = "1px";
+    elmt.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + url +"', sizingMethod='image')";
+    elmt.style.verticalAlign = (verticalAlign != null) ? verticalAlign : "middle";
+    return elmt;
+};
+
+Graphics._createTranslucentImageHTML1 = function(url, verticalAlign) {
+    return "<img src=\"" + url + "\"" +
+        (verticalAlign != null ? " style=\"vertical-align: " + verticalAlign + ";\"" : "") +
+        " />";
+};
+Graphics._createTranslucentImageHTML2 = function(url, verticalAlign) {
+    var style = 
+        "width: 1px; height: 1px; " +
+        "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + url +"', sizingMethod='image');" +
+        (verticalAlign != null ? " vertical-align: " + verticalAlign + ";" : "");
+        
+    return "<img src='" + url + "' style=\"" + style + "\" />";
+};
+
+/**
+ * Consolidate graphics constant setting into a function dependent on
+ * SimileAjax loading.
+ * @param {Object} g Object to modify functions for.
+ */
+Graphics.initialize = function(g) {
+    /**
+     * A boolean value indicating whether PNG translucency is supported on the
+     * user's browser or not.
+     *
+     * @type Boolean
+     */
+    g.pngIsTranslucent = (!Platform.browser.isIE) || (Platform.browser.majorVersion > 6);
+    if (!g.pngIsTranslucent) {
+        includeCssFile(document, SimileAjax.urlPrefix + "styles/graphics-ie6.css");
+    }
+
+    /**
+     * Creates a DOM element for an <code>img</code> tag using the URL given.
+     * This is a convenience method that automatically includes the necessary
+     * CSS to allow for translucency, even on IE.
+     * 
+     * @function
+     * @param {String} url the URL to the image
+     * @param {String} verticalAlign the CSS value for the image's
+     *     vertical-align
+     * @return {Element} a DOM element containing the <code>img</code> tag
+     */
+    g.createTranslucentImage = g.pngIsTranslucent ?
+        g._createTranslucentImage1 :
+        g._createTranslucentImage2;
+    
+    /**
+     * Creates an HTML string for an <code>img</code> tag using the URL given.
+     * This is a convenience method that automatically includes the necessary
+     * CSS to allow for translucency, even on IE.
+     * 
+     * @function
+     * @param {String} url the URL to the image
+     * @param {String} verticalAlign the CSS value for the image's
+     *     vertical-align
+     * @return {String} a string containing the <code>img</code> tag
+     */
+    g.createTranslucentImageHTML = g.pngIsTranslucent ?
+        g._createTranslucentImageHTML1 :
+        g._createTranslucentImageHTML2;
+
+    return g;
+};
+
+/**
+ * Sets the opacity on the given DOM element.
+ *
+ * @param {Element} elmt the DOM element to set the opacity on
+ * @param {Number} opacity an integer from 0 to 100 specifying the opacity
+ */
+Graphics.setOpacity = function(elmt, opacity) {
+    if (Platform.browser.isIE) {
+        elmt.style.filter = "progid:DXImageTransform.Microsoft.Alpha(Style=0,Opacity=" + opacity + ")";
+    } else {
+        var o = (opacity / 100).toString();
+        elmt.style.opacity = o;
+        elmt.style.MozOpacity = o;
+    }
+};
+
+/*==================================================
+ *  Bubble
+ *==================================================
+ */
+
+Graphics.bubbleConfig = {
+    containerCSSClass:              "simileAjax-bubble-container",
+    innerContainerCSSClass:         "simileAjax-bubble-innerContainer",
+    contentContainerCSSClass:       "simileAjax-bubble-contentContainer",
+    
+    borderGraphicSize:              50,
+    borderGraphicCSSClassPrefix:    "simileAjax-bubble-border-",
+    
+    arrowGraphicTargetOffset:       33,  // from tip of arrow to the side of the graphic that touches the content of the bubble
+    arrowGraphicLength:             100, // dimension of arrow graphic along the direction that the arrow points
+    arrowGraphicWidth:              49,  // dimension of arrow graphic perpendicular to the direction that the arrow points
+    arrowGraphicCSSClassPrefix:     "simileAjax-bubble-arrow-",
+    
+    closeGraphicCSSClass:           "simileAjax-bubble-close",
+    
+    extraPadding:                   20
+};
+
+Graphics.getWindowDimensions = function() {
+    if (typeof window.innerHeight == 'number') {
+        return { w:window.innerWidth, h:window.innerHeight }; // Non-IE
+    } else if (document.documentElement && document.documentElement.clientHeight) {
+        return { // IE6+, in "standards compliant mode"
+            w:document.documentElement.clientWidth,
+            h:document.documentElement.clientHeight
+        };
+    } else if (document.body && document.body.clientHeight) {
+        return { // IE 4 compatible
+            w:document.body.clientWidth,
+            h:document.body.clientHeight
+        };
+    }
+};
+
+
+/**
+ * Creates a floating, rounded message bubble in the center of the window for
+ * displaying modal information, e.g. "Loading..."
+ *
+ * @param {Document} doc the root document for the page to render on
+ * @param {Object} an object with two properties, contentDiv and containerDiv,
+ *   consisting of the newly created DOM elements
+ */
+Graphics.createMessageBubble = function(doc) {
+    var containerDiv = doc.createElement("div");
+    var prefix = "simileAjax-messageBubble";
+    if (Graphics.pngIsTranslucent) {
+        var topDiv = doc.createElement("div");
+        topDiv.className = prefix + "-top";
+        containerDiv.appendChild(topDiv);
+        
+        var topRightDiv = doc.createElement("div");
+        topRightDiv.className = prefix + "-top-right";
+        topDiv.appendChild(topRightDiv);
+        
+        var middleDiv = doc.createElement("div");
+        middleDiv.className = prefix + "-middle";
+        containerDiv.appendChild(middleDiv);
+        
+        var middleRightDiv = doc.createElement("div");
+        middleRightDiv.className = prefix + "-middle-right";
+        middleDiv.appendChild(middleRightDiv);
+        
+        var contentDiv = doc.createElement("div");
+        middleRightDiv.appendChild(contentDiv);
+        
+        var bottomDiv = doc.createElement("div");
+        bottomDiv.className = prefix + "-bottom";
+        containerDiv.appendChild(bottomDiv);
+        
+        var bottomRightDiv = doc.createElement("div");
+        bottomRightDiv.className = prefix + "-bottom-right";
+        bottomDiv.appendChild(bottomRightDiv);
+    } else {
+        containerDiv.style.border = "2px solid #7777AA";
+        containerDiv.style.padding = "20px";
+        containerDiv.style.background = "white";
+        Graphics.setOpacity(containerDiv, 90);
+        
+        var contentDiv = doc.createElement("div");
+        containerDiv.appendChild(contentDiv);
+    }
+    
+    return {
+        containerDiv:   containerDiv,
+        contentDiv:     contentDiv
+    };
+};
+
+/*==================================================
+ *  Animation
+ *==================================================
+ */
+
+/**
+ * Creates an animation for a function, and an interval of values.  The word
+ * "animation" here is used in the sense of repeatedly calling a function with
+ * a current value from within an interval, and a delta value.
+ *
+ * @param {Function} f a function to be called every 50 milliseconds throughout
+ *   the animation duration, of the form f(current, delta), where current is
+ *   the current value within the range and delta is the current change.
+ * @param {Number} from a starting value
+ * @param {Number} to an ending value
+ * @param {Number} duration the duration of the animation in milliseconds
+ * @param {Function} [cont] an optional function that is called at the end of
+ *   the animation, i.e. a continuation.
+ * @return {Graphics._Animation} a new animation object
+ */
+Graphics.createAnimation = function(f, from, to, duration, cont) {
+    return new Graphics._Animation(f, from, to, duration, cont);
+};
+
+Graphics._Animation = function(f, from, to, duration, cont) {
+    this.f = f;
+    this.cont = (typeof cont == "function") ? cont : function() {};
+    
+    this.from = from;
+    this.to = to;
+    this.current = from;
+    
+    this.duration = duration;
+    this.start = new Date().getTime();
+    this.timePassed = 0;
+};
+
+/**
+ * Runs this animation.
+ */
+Graphics._Animation.prototype.run = function() {
+    var a = this;
+    window.setTimeout(function() { a.step(); }, 50);
+};
+
+/**
+ * Increments this animation by one step, and then continues the animation with
+ * <code>run()</code>.
+ */
+Graphics._Animation.prototype.step = function() {
+    this.timePassed += 50;
+    
+    var timePassedFraction = this.timePassed / this.duration;
+    var parameterFraction = -Math.cos(timePassedFraction * Math.PI) / 2 + 0.5;
+    var current = parameterFraction * (this.to - this.from) + this.from;
+    
+    try {
+        this.f(current, current - this.current);
+    } catch (e) {
+    }
+    this.current = current;
+    
+    if (this.timePassed < this.duration) {
+        this.run();
+    } else {
+        this.f(this.to, 0);
+        this["cont"]();
+    }
+};
+
+/*==================================================
+ *  CopyPasteButton
+ *
+ *  Adapted from http://spaces.live.com/editorial/rayozzie/demo/liveclip/liveclipsample/techPreview.html.
+ *==================================================
+ */
+
+/**
+ * Creates a button and textarea for displaying structured data and copying it
+ * to the clipboard.  The data is dynamically generated by the given 
+ * createDataFunction parameter.
+ *
+ * @param {String} image an image URL to use as the background for the 
+ *   generated box
+ * @param {Number} width the width in pixels of the generated box
+ * @param {Number} height the height in pixels of the generated box
+ * @param {Function} createDataFunction a function that is called with no
+ *   arguments to generate the structured data
+ * @return a new DOM element
+ */
+Graphics.createStructuredDataCopyButton = function(image, width, height, createDataFunction) {
+    var div = document.createElement("div");
+    div.style.position = "relative";
+    div.style.display = "inline";
+    div.style.width = width + "px";
+    div.style.height = height + "px";
+    div.style.overflow = "hidden";
+    div.style.margin = "2px";
+    
+    if (Graphics.pngIsTranslucent) {
+        div.style.background = "url(" + image + ") no-repeat";
+    } else {
+        div.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + image +"', sizingMethod='image')";
+    }
+    
+    var style;
+    if (Platform.browser.isIE) {
+        style = "filter:alpha(opacity=0)";
+    } else {
+        style = "opacity: 0";
+    }
+    div.innerHTML = "<textarea rows='1' autocomplete='off' value='none' style='" + style + "' />";
+    
+    var textarea = div.firstChild;
+    textarea.style.width = width + "px";
+    textarea.style.height = height + "px";
+    textarea.onmousedown = function(evt) {
+        evt = (evt) ? evt : ((event) ? event : null);
+        if (evt.button == 2) {
+            textarea.value = createDataFunction();
+            textarea.select();
+        }
+    };
+    
+    return div;
+};
+
+/*==================================================
+ *  getWidthHeight
+ *==================================================
+ */
+Graphics.getWidthHeight = function(el) {
+    // RETURNS hash {width:  w, height: h} in pixels
+    
+    var w, h;
+    // offsetWidth rounds on FF, so doesn't work for us.
+    // See https://bugzilla.mozilla.org/show_bug.cgi?id=458617
+    if (el.getBoundingClientRect == null) {
+    	// use offsetWidth
+      w = el.offsetWidth;
+      h = el.offsetHeight;
+    } else {
+    	// use getBoundingClientRect
+      var rect = el.getBoundingClientRect();
+      w = Math.ceil(rect.right - rect.left);
+    	h = Math.ceil(rect.bottom - rect.top);
+    }
+    return {
+        width:  w,
+        height: h
+    };
+};
+ 
+
+/*==================================================
+ *  FontRenderingContext
+ *==================================================
+ */
+Graphics.getFontRenderingContext = function(elmt, width) {
+    return new Graphics._FontRenderingContext(elmt, width);
+};
+
+Graphics._FontRenderingContext = function(elmt, width) {
+    this._elmt = elmt;
+    this._elmt.style.visibility = "hidden";
+    if (typeof width == "string") {
+        this._elmt.style.width = width;
+    } else if (typeof width == "number") {
+        this._elmt.style.width = width + "px";
+    }
+};
+
+Graphics._FontRenderingContext.prototype.dispose = function() {
+    this._elmt = null;
+};
+
+Graphics._FontRenderingContext.prototype.update = function() {
+    this._elmt.innerHTML = "A";
+    this._lineHeight = this._elmt.offsetHeight;
+};
+
+Graphics._FontRenderingContext.prototype.computeSize = function(text, className) {
+    // className arg is optional
+    var el = this._elmt;
+    el.innerHTML = text;
+    el.className = className === undefined ? '' : className;
+    var wh = Graphics.getWidthHeight(el);
+    el.className = ''; // reset for the next guy
+    
+    return wh;
+};
+
+Graphics._FontRenderingContext.prototype.getLineHeight = function() {
+    return this._lineHeight;
+};
+
+    return Graphics;
+});
+
+/**
+ * @fileOverview UI layers and window-wide dragging
+ * @name SimileAjax.WindowManager
+ */
+
+/**
+ *  This is a singleton that keeps track of UI layers (modal and 
+ *  modeless) and enables/disables UI elements based on which layers
+ *  they belong to. It also provides window-wide dragging 
+ *  implementation.
+ */ 
+define('scripts/window-manager',[
+    "./dom",
+    "./debug",
+    "./graphics",
+    "./simile-ajax-base"
+], function(DOM, Debug, Graphics, SimileAjax) {
+var WindowManager = {
+    _initialized:       false,
+    _listeners:         [],
+    
+    _draggedElement:                null,
+    _draggedElementCallback:        null,
+    _dropTargetHighlightElement:    null,
+    _lastCoords:                    null,
+    _ghostCoords:                   null,
+    _draggingMode:                  "",
+    _dragging:                      false,
+    
+    _layers:            []
+};
+
+WindowManager.initialize = function() {
+    if (WindowManager._initialized) {
+        return;
+    }
+    
+    DOM.registerEvent(document.body, "mousedown", WindowManager._onBodyMouseDown);
+    DOM.registerEvent(document.body, "mousemove", WindowManager._onBodyMouseMove);
+    DOM.registerEvent(document.body, "mouseup",   WindowManager._onBodyMouseUp);
+    DOM.registerEvent(document, "keydown",       WindowManager._onBodyKeyDown);
+    DOM.registerEvent(document, "keyup",         WindowManager._onBodyKeyUp);
+    
+    WindowManager._layers.push({index: 0});
+
+    // @@@ There were pieces here to assemble a no-op history listener
+    //     and add it to the SimileAjax.History listener stack, but I
+    //     suspect it was only here to make sure history initialized
+    //     before window manager.  I've simply put calls to init both
+    //     in the overall SimileAjax.load() method.  This breaks a
+    //     terrible dependency cycle that sat between them.  If I'm
+    //     wrong, another solution needs to be found.
+
+    WindowManager._initialized = true;
+};
+
+WindowManager.getBaseLayer = function() {
+    return WindowManager._layers[0];
+};
+
+WindowManager.getHighestLayer = function() {
+    return WindowManager._layers[WindowManager._layers.length - 1];
+};
+
+WindowManager.registerEventWithObject = function(elmt, eventName, obj, handlerName, layer) {
+    WindowManager.registerEvent(
+        elmt, 
+        eventName, 
+        function(elmt2, evt, target) {
+            return obj[handlerName].call(obj, elmt2, evt, target);
+        },
+        layer
+    );
+};
+
+WindowManager.registerEvent = function(elmt, eventName, handler, layer) {
+    if (layer == null) {
+        layer = WindowManager.getHighestLayer();
+    }
+    
+    var handler2 = function(elmt, evt, target) {
+        if (WindowManager._canProcessEventAtLayer(layer)) {
+            WindowManager._popToLayer(layer.index);
+            try {
+                handler(elmt, evt, target);
+            } catch (e) {
+                Debug.exception(e);
+            }
+        }
+        DOM.cancelEvent(evt);
+        return false;
+    }
+    
+    DOM.registerEvent(elmt, eventName, handler2);
+};
+
+WindowManager.pushLayer = function(f, ephemeral, elmt) {
+    var layer = { onPop: f, index: WindowManager._layers.length, ephemeral: (ephemeral), elmt: elmt };
+    WindowManager._layers.push(layer);
+    
+    return layer;
+};
+
+WindowManager.popLayer = function(layer) {
+    for (var i = 1; i < WindowManager._layers.length; i++) {
+        if (WindowManager._layers[i] == layer) {
+            WindowManager._popToLayer(i - 1);
+            break;
+        }
+    }
+};
+
+WindowManager.popAllLayers = function() {
+    WindowManager._popToLayer(0);
+};
+
+WindowManager.registerForDragging = function(elmt, callback, layer) {
+    WindowManager.registerEvent(
+        elmt, 
+        "mousedown", 
+        function(elmt, evt, target) {
+            WindowManager._handleMouseDown(elmt, evt, callback);
+        }, 
+        layer
+    );
+};
+
+WindowManager._popToLayer = function(level) {
+    while (level+1 < WindowManager._layers.length) {
+        try {
+            var layer = WindowManager._layers.pop();
+            if (layer.onPop != null) {
+                layer.onPop();
+            }
+        } catch (e) {
+        }
+    }
+};
+
+WindowManager._canProcessEventAtLayer = function(layer) {
+    if (layer.index == (WindowManager._layers.length - 1)) {
+        return true;
+    }
+    for (var i = layer.index + 1; i < WindowManager._layers.length; i++) {
+        if (!WindowManager._layers[i].ephemeral) {
+            return false;
+        }
+    }
+    return true;
+};
+
+WindowManager.cancelPopups = function(evt) {
+    var evtCoords = (evt) ? DOM.getEventPageCoordinates(evt) : { x: -1, y: -1 };
+    
+    var i = WindowManager._layers.length - 1;
+    while (i > 0 && WindowManager._layers[i].ephemeral) {
+        var layer = WindowManager._layers[i];
+        if (layer.elmt != null) { // if event falls within main element of layer then don't cancel
+            var elmt = layer.elmt;
+            var elmtCoords = DOM.getPageCoordinates(elmt);
+            if (evtCoords.x >= elmtCoords.left && evtCoords.x < (elmtCoords.left + elmt.offsetWidth) &&
+                evtCoords.y >= elmtCoords.top && evtCoords.y < (elmtCoords.top + elmt.offsetHeight)) {
+                break;
+            }
+        }
+        i--;
+    }
+    WindowManager._popToLayer(i);
+};
+
+WindowManager._onBodyMouseDown = function(elmt, evt, target) {
+    if (!("eventPhase" in evt) || evt.eventPhase == evt.BUBBLING_PHASE) {
+        WindowManager.cancelPopups(evt);
+    }
+};
+
+WindowManager._handleMouseDown = function(elmt, evt, callback) {
+    WindowManager._draggedElement = elmt;
+    WindowManager._draggedElementCallback = callback;
+    WindowManager._lastCoords = { x: evt.clientX, y: evt.clientY };
+        
+    DOM.cancelEvent(evt);
+    return false;
+};
+
+WindowManager._onBodyKeyDown = function(elmt, evt, target) {
+    if (WindowManager._dragging) {
+        if (evt.keyCode == 27) { // esc
+            WindowManager._cancelDragging();
+        } else if ((evt.keyCode == 17 || evt.keyCode == 16) && WindowManager._draggingMode != "copy") {
+            WindowManager._draggingMode = "copy";
+            
+            var img = Graphics.createTranslucentImage(SimileAjax.urlPrefix + "images/copy.png");
+            img.style.position = "absolute";
+            img.style.left = (WindowManager._ghostCoords.left - 16) + "px";
+            img.style.top = (WindowManager._ghostCoords.top) + "px";
+            document.body.appendChild(img);
+            
+            WindowManager._draggingModeIndicatorElmt = img;
+        }
+    }
+};
+
+WindowManager._onBodyKeyUp = function(elmt, evt, target) {
+    if (WindowManager._dragging) {
+        if (evt.keyCode == 17 || evt.keyCode == 16) {
+            WindowManager._draggingMode = "";
+            if (WindowManager._draggingModeIndicatorElmt != null) {
+                document.body.removeChild(WindowManager._draggingModeIndicatorElmt);
+                WindowManager._draggingModeIndicatorElmt = null;
+            }
+        }
+    }
+};
+
+WindowManager._onBodyMouseMove = function(elmt, evt, target) {
+    if (WindowManager._draggedElement != null) {
+        var callback = WindowManager._draggedElementCallback;
+        
+        var lastCoords = WindowManager._lastCoords;
+        var diffX = evt.clientX - lastCoords.x;
+        var diffY = evt.clientY - lastCoords.y;
+        
+        if (!WindowManager._dragging) {
+            if (Math.abs(diffX) > 5 || Math.abs(diffY) > 5) {
+                try {
+                    if ("onDragStart" in callback) {
+                        callback.onDragStart();
+                    }
+                    
+                    if ("ghost" in callback && callback.ghost) {
+                        var draggedElmt = WindowManager._draggedElement;
+                        
+                        WindowManager._ghostCoords = DOM.getPageCoordinates(draggedElmt);
+                        WindowManager._ghostCoords.left += diffX;
+                        WindowManager._ghostCoords.top += diffY;
+                        
+                        var ghostElmt = draggedElmt.cloneNode(true);
+                        ghostElmt.style.position = "absolute";
+                        ghostElmt.style.left = WindowManager._ghostCoords.left + "px";
+                        ghostElmt.style.top = WindowManager._ghostCoords.top + "px";
+                        ghostElmt.style.zIndex = 1000;
+                        Graphics.setOpacity(ghostElmt, 50);
+                        
+                        document.body.appendChild(ghostElmt);
+                        callback._ghostElmt = ghostElmt;
+                    }
+                    
+                    WindowManager._dragging = true;
+                    WindowManager._lastCoords = { x: evt.clientX, y: evt.clientY };
+                    
+                    document.body.focus();
+                } catch (e) {
+                    Debug.exception("WindowManager: Error handling mouse down", e);
+                    WindowManager._cancelDragging();
+                }
+            }
+        } else {
+            try {
+                WindowManager._lastCoords = { x: evt.clientX, y: evt.clientY };
+                
+                if ("onDragBy" in callback) {
+                    callback.onDragBy(diffX, diffY);
+                }
+                
+                if ("_ghostElmt" in callback) {
+                    var ghostElmt = callback._ghostElmt;
+                    
+                    WindowManager._ghostCoords.left += diffX;
+                    WindowManager._ghostCoords.top += diffY;
+                    
+                    ghostElmt.style.left = WindowManager._ghostCoords.left + "px";
+                    ghostElmt.style.top = WindowManager._ghostCoords.top + "px";
+                    if (WindowManager._draggingModeIndicatorElmt != null) {
+                        var indicatorElmt = WindowManager._draggingModeIndicatorElmt;
+                        
+                        indicatorElmt.style.left = (WindowManager._ghostCoords.left - 16) + "px";
+                        indicatorElmt.style.top = WindowManager._ghostCoords.top + "px";
+                    }
+                    
+                    if ("droppable" in callback && callback.droppable) {
+                        var coords = DOM.getEventPageCoordinates(evt);
+                        var target = DOM.hittest(
+                            coords.x, coords.y, 
+                            [   WindowManager._ghostElmt, 
+                                WindowManager._dropTargetHighlightElement 
+                            ]
+                        );
+                        target = WindowManager._findDropTarget(target);
+                        
+                        if (target != WindowManager._potentialDropTarget) {
+                            if (WindowManager._dropTargetHighlightElement != null) {
+                                document.body.removeChild(WindowManager._dropTargetHighlightElement);
+                                
+                                WindowManager._dropTargetHighlightElement = null;
+                                WindowManager._potentialDropTarget = null;
+                            }
+
+                            var droppable = false;
+                            if (target != null) {
+                                if ((!("canDropOn" in callback) || callback.canDropOn(target)) &&
+                                    (!("canDrop" in target) || target.canDrop(WindowManager._draggedElement))) {
+                                    
+                                    droppable = true;
+                                }
+                            }
+                            
+                            if (droppable) {
+                                var border = 4;
+                                var targetCoords = DOM.getPageCoordinates(target);
+                                var highlight = document.createElement("div");
+                                highlight.style.border = border + "px solid yellow";
+                                highlight.style.backgroundColor = "yellow";
+                                highlight.style.position = "absolute";
+                                highlight.style.left = targetCoords.left + "px";
+                                highlight.style.top = targetCoords.top + "px";
+                                highlight.style.width = (target.offsetWidth - border * 2) + "px";
+                                highlight.style.height = (target.offsetHeight - border * 2) + "px";
+                                Graphics.setOpacity(highlight, 30);
+                                document.body.appendChild(highlight);
+                                
+                                WindowManager._potentialDropTarget = target;
+                                WindowManager._dropTargetHighlightElement = highlight;
+                            }
+                        }
+                    }
+                }
+            } catch (e) {
+                Debug.exception("WindowManager: Error handling mouse move", e);
+                WindowManager._cancelDragging();
+            }
+        }
+        
+        DOM.cancelEvent(evt);
+        return false;
+    }
+};
+
+WindowManager._onBodyMouseUp = function(elmt, evt, target) {
+    if (WindowManager._draggedElement != null) {
+        try {
+            if (WindowManager._dragging) {
+                var callback = WindowManager._draggedElementCallback;
+                if ("onDragEnd" in callback) {
+                    callback.onDragEnd();
+                }
+                if ("droppable" in callback && callback.droppable) {
+                    var dropped = false;
+                    
+                    var target = WindowManager._potentialDropTarget;
+                    if (target != null) {
+                        if ((!("canDropOn" in callback) || callback.canDropOn(target)) &&
+                            (!("canDrop" in target) || target.canDrop(WindowManager._draggedElement))) {
+                            
+                            if ("onDropOn" in callback) {
+                                callback.onDropOn(target);
+                            }
+                            target.ondrop(WindowManager._draggedElement, WindowManager._draggingMode);
+                            
+                            dropped = true;
+                        }
+                    }
+                    
+                    if (!dropped) {
+                        // TODO: do holywood explosion here
+                    }
+                }
+            }
+        } finally {
+            WindowManager._cancelDragging();
+        }
+        
+        DOM.cancelEvent(evt);
+        return false;
+    }
+};
+
+WindowManager._cancelDragging = function() {
+    var callback = WindowManager._draggedElementCallback;
+    if ("_ghostElmt" in callback) {
+        var ghostElmt = callback._ghostElmt;
+        document.body.removeChild(ghostElmt);
+        
+        delete callback._ghostElmt;
+    }
+    if (WindowManager._dropTargetHighlightElement != null) {
+        document.body.removeChild(WindowManager._dropTargetHighlightElement);
+        WindowManager._dropTargetHighlightElement = null;
+    }
+    if (WindowManager._draggingModeIndicatorElmt != null) {
+        document.body.removeChild(WindowManager._draggingModeIndicatorElmt);
+        WindowManager._draggingModeIndicatorElmt = null;
+    }
+    
+    WindowManager._draggedElement = null;
+    WindowManager._draggedElementCallback = null;
+    WindowManager._potentialDropTarget = null;
+    WindowManager._dropTargetHighlightElement = null;
+    WindowManager._lastCoords = null;
+    WindowManager._ghostCoords = null;
+    WindowManager._draggingMode = "";
+    WindowManager._dragging = false;
+};
+
+WindowManager._findDropTarget = function(elmt) {
+    while (elmt != null) {
+        if ("ondrop" in elmt && (typeof elmt.ondrop) == "function") {
+            break;
+        }
+        elmt = elmt.parentNode;
+    }
+    return elmt;
+};
+
+    return WindowManager;
+});
+
+define('scripts/bubble',[
+    "./graphics",
+    "./window-manager"
+], function(Graphics, WindowManager) {
+/**
+ * Creates a nice, rounded bubble popup with the given page coordinates and
+ * content dimensions.  The bubble will point to the location on the page
+ * as described by pageX and pageY.  All measurements should be given in
+ * pixels.
+ *
+ * @param {Number} pageX the x coordinate of the point to point to
+ * @param {Number} pageY the y coordinate of the point to point to
+ * @param {Number} contentWidth the width of the content box in the bubble
+ * @param {Number} contentHeight the height of the content box in the bubble
+ * @param {String} orientation a string ("top", "bottom", "left", or "right")
+ *   that describes the orientation of the arrow on the bubble
+ * @return {Element} a DOM element for the newly created bubble
+ */
+Graphics.createBubbleForPoint = function(pageX, pageY, contentWidth, contentHeight, orientation) {
+    contentWidth = parseInt(contentWidth, 10); // harden against bad input bugs
+    contentHeight = parseInt(contentHeight, 10); // getting numbers-as-strings
+    
+    var bubbleConfig = Graphics.bubbleConfig;
+    var pngTransparencyClassSuffix = 
+        Graphics.pngIsTranslucent ? "pngTranslucent" : "pngNotTranslucent";
+    
+    var bubbleWidth = contentWidth + 2 * bubbleConfig.borderGraphicSize;
+    var bubbleHeight = contentHeight + 2 * bubbleConfig.borderGraphicSize;
+    
+    var generatePngSensitiveClass = function(className) {
+        return className + " " + className + "-" + pngTransparencyClassSuffix;
+    };
+    
+    /*
+     *  Render container divs
+     */
+    var div = document.createElement("div");
+    div.className = generatePngSensitiveClass(bubbleConfig.containerCSSClass);
+    div.style.width = contentWidth + "px";
+    div.style.height = contentHeight + "px";
+    
+    var divInnerContainer = document.createElement("div");
+    divInnerContainer.className = generatePngSensitiveClass(bubbleConfig.innerContainerCSSClass);
+    div.appendChild(divInnerContainer);
+    
+    /*
+     *  Create layer for bubble
+     */
+    var close = function() { 
+        if (!bubble._closed) {
+            document.body.removeChild(bubble._div);
+            bubble._doc = null;
+            bubble._div = null;
+            bubble._content = null;
+            bubble._closed = true;
+        }
+    }
+    var bubble = { _closed: false };
+    var layer = WindowManager.pushLayer(close, true, div);
+    bubble._div = div;
+    bubble.close = function() { WindowManager.popLayer(layer); }
+    
+    /*
+     *  Render border graphics
+     */
+    var createBorder = function(classNameSuffix) {
+        var divBorderGraphic = document.createElement("div");
+        divBorderGraphic.className = generatePngSensitiveClass(bubbleConfig.borderGraphicCSSClassPrefix + classNameSuffix);
+        divInnerContainer.appendChild(divBorderGraphic);
+    };
+    createBorder("top-left");
+    createBorder("top-right");
+    createBorder("bottom-left");
+    createBorder("bottom-right");
+    createBorder("left");
+    createBorder("right");
+    createBorder("top");
+    createBorder("bottom");
+    
+    /*
+     *  Render content
+     */
+    var divContentContainer = document.createElement("div");
+    divContentContainer.className = generatePngSensitiveClass(bubbleConfig.contentContainerCSSClass);
+    divInnerContainer.appendChild(divContentContainer);
+    bubble.content = divContentContainer;
+    
+    /*
+     *  Render close button
+     */
+    var divClose = document.createElement("div");
+    divClose.className = generatePngSensitiveClass(bubbleConfig.closeGraphicCSSClass);
+    divInnerContainer.appendChild(divClose);
+    WindowManager.registerEventWithObject(divClose, "click", bubble, "close");
+    
+    (function() {
+        var dims = Graphics.getWindowDimensions();
+        var docWidth = dims.w;
+        var docHeight = dims.h;
+        
+        var halfArrowGraphicWidth = Math.ceil(bubbleConfig.arrowGraphicWidth / 2);
+        
+        var createArrow = function(classNameSuffix) {
+            var divArrowGraphic = document.createElement("div");
+            divArrowGraphic.className = generatePngSensitiveClass(bubbleConfig.arrowGraphicCSSClassPrefix + "point-" + classNameSuffix);
+            divInnerContainer.appendChild(divArrowGraphic);
+            return divArrowGraphic;
+        };
+        
+        if (pageX - halfArrowGraphicWidth - bubbleConfig.borderGraphicSize - bubbleConfig.extraPadding > 0 &&
+            pageX + halfArrowGraphicWidth + bubbleConfig.borderGraphicSize + bubbleConfig.extraPadding < docWidth) {
+            
+            /*
+             *  Bubble can be positioned above or below the target point.
+             */
+            
+            var left = pageX - Math.round(contentWidth / 2);
+            left = pageX < (docWidth / 2) ?
+                Math.max(left, bubbleConfig.extraPadding + bubbleConfig.borderGraphicSize) : 
+                Math.min(left, docWidth - bubbleConfig.extraPadding - bubbleConfig.borderGraphicSize - contentWidth);
+                
+            if ((orientation && orientation == "top") || 
+                (!orientation && 
+                    (pageY 
+                        - bubbleConfig.arrowGraphicTargetOffset 
+                        - contentHeight 
+                        - bubbleConfig.borderGraphicSize 
+                        - bubbleConfig.extraPadding > 0))) {
+                
+                /*
+                 *  Position bubble above the target point.
+                 */
+                
+                var divArrow = createArrow("down");
+                divArrow.style.left = (pageX - halfArrowGraphicWidth - left) + "px";
+                
+                div.style.left = left + "px";
+                div.style.top = (pageY - bubbleConfig.arrowGraphicTargetOffset - contentHeight) + "px";
+                
+                return;
+            } else if ((orientation && orientation == "bottom") || 
+                (!orientation && 
+                    (pageY 
+                        + bubbleConfig.arrowGraphicTargetOffset 
+                        + contentHeight 
+                        + bubbleConfig.borderGraphicSize 
+                        + bubbleConfig.extraPadding < docHeight))) {
+                        
+                /*
+                 *  Position bubble below the target point.
+                 */
+                
+                var divArrow = createArrow("up");
+                divArrow.style.left = (pageX - halfArrowGraphicWidth - left) + "px";
+                
+                div.style.left = left + "px";
+                div.style.top = (pageY + bubbleConfig.arrowGraphicTargetOffset) + "px";
+                
+                return;
+            }
+        }
+        
+        var top = pageY - Math.round(contentHeight / 2);
+        top = pageY < (docHeight / 2) ?
+            Math.max(top, bubbleConfig.extraPadding + bubbleConfig.borderGraphicSize) : 
+            Math.min(top, docHeight - bubbleConfig.extraPadding - bubbleConfig.borderGraphicSize - contentHeight);
+            
+        if ((orientation && orientation == "left") || 
+            (!orientation && 
+                (pageX 
+                    - bubbleConfig.arrowGraphicTargetOffset 
+                    - contentWidth
+                    - bubbleConfig.borderGraphicSize 
+                    - bubbleConfig.extraPadding > 0))) {
+            
+            /*
+             *  Position bubble left of the target point.
+             */
+            
+            var divArrow = createArrow("right");
+            divArrow.style.top = (pageY - halfArrowGraphicWidth - top) + "px";
+            
+            div.style.top = top + "px";
+            div.style.left = (pageX - bubbleConfig.arrowGraphicTargetOffset - contentWidth) + "px";
+        } else {
+            
+            /*
+             *  Position bubble right of the target point, as the last resort.
+             */
+            
+            var divArrow = createArrow("left");
+            divArrow.style.top = (pageY - halfArrowGraphicWidth - top) + "px";
+            
+            div.style.top = top + "px";
+            div.style.left = (pageX + bubbleConfig.arrowGraphicTargetOffset) + "px";
+        }
+    })();
+    
+    document.body.appendChild(div);
+    
+    return bubble;
+};
+
+/**
+ * Creates a nice, rounded bubble popup with the given content in a div,
+ * page coordinates and a suggested width. The bubble will point to the 
+ * location on the page as described by pageX and pageY.  All measurements 
+ * should be given in pixels.
+ *
+ * @param {Element} the content div
+ * @param {Number} pageX the x coordinate of the point to point to
+ * @param {Number} pageY the y coordinate of the point to point to
+ * @param {Number} contentWidth a suggested width of the content
+ * @param {String} orientation a string ("top", "bottom", "left", or "right")
+ *   that describes the orientation of the arrow on the bubble
+ * @param {Number} maxHeight. Add a scrollbar div if bubble would be too tall.
+ *   Default of 0 or null means no maximum
+ */
+Graphics.createBubbleForContentAndPoint = function(
+       div, pageX, pageY, contentWidth, orientation, maxHeight) {
+    if (typeof contentWidth != "number") {
+        contentWidth = 300;
+    }
+    if (typeof maxHeight != "number") {
+        maxHeight = 0;
+    }
+
+    div.style.position = "absolute";
+    div.style.left = "-5000px";
+    div.style.top = "0px";
+    div.style.width = contentWidth + "px";
+    document.body.appendChild(div);
+    
+    window.setTimeout(function() {
+        var width = div.scrollWidth + 10;
+        var height = div.scrollHeight + 10;
+        var scrollDivW = 0; // width of the possible inner container when we want vertical scrolling
+        if (maxHeight > 0 && height > maxHeight) {
+          height = maxHeight;
+          scrollDivW = width - 25;
+        }  
+       
+        var bubble = Graphics.createBubbleForPoint(pageX, pageY, width, height, orientation);
+        
+        document.body.removeChild(div);
+        div.style.position = "static";
+        div.style.left = "";
+        div.style.top = "";
+        
+        // create a scroll div if needed
+        if (scrollDivW > 0) {
+          var scrollDiv = document.createElement("div");
+          div.style.width = "";
+          scrollDiv.style.width = scrollDivW + "px";
+          scrollDiv.appendChild(div);
+          bubble.content.appendChild(scrollDiv);
+        } else {
+          div.style.width = width + "px";
+          bubble.content.appendChild(div);
+        }
+    }, 200);
+};
+
+    return Graphics;
+});
+
+/**
+ * @fileOverview A collection of date/time utility functions
+ * @name SimileAjax.DateTime
+ */
+
+define('scripts/date-time',["./debug"], function(Debug) {
+var DateTime = new Object();
+
+DateTime.MILLISECOND    = 0;
+DateTime.SECOND         = 1;
+DateTime.MINUTE         = 2;
+DateTime.HOUR           = 3;
+DateTime.DAY            = 4;
+DateTime.WEEK           = 5;
+DateTime.MONTH          = 6;
+DateTime.YEAR           = 7;
+DateTime.DECADE         = 8;
+DateTime.CENTURY        = 9;
+DateTime.MILLENNIUM     = 10;
+
+DateTime.EPOCH          = -1;
+DateTime.ERA            = -2;
+
+/**
+ * An array of unit lengths, expressed in milliseconds, of various lengths of
+ * time.  The array indices are predefined and stored as properties of the
+ * DateTime object, e.g. DateTime.YEAR.
+ * @type Array
+ */
+DateTime.gregorianUnitLengths = [];
+    (function() {
+        var d = DateTime;
+        var a = d.gregorianUnitLengths;
+        
+        a[d.MILLISECOND] = 1;
+        a[d.SECOND]      = 1000;
+        a[d.MINUTE]      = a[d.SECOND] * 60;
+        a[d.HOUR]        = a[d.MINUTE] * 60;
+        a[d.DAY]         = a[d.HOUR] * 24;
+        a[d.WEEK]        = a[d.DAY] * 7;
+        a[d.MONTH]       = a[d.DAY] * 31;
+        a[d.YEAR]        = a[d.DAY] * 365;
+        a[d.DECADE]      = a[d.YEAR] * 10;
+        a[d.CENTURY]     = a[d.YEAR] * 100;
+        a[d.MILLENNIUM]  = a[d.YEAR] * 1000;
+    })();
+    
+DateTime._dateRegexp = new RegExp(
+    "^(-?)([0-9]{4})(" + [
+        "(-?([0-9]{2})(-?([0-9]{2}))?)", // -month-dayOfMonth
+        "(-?([0-9]{3}))",                // -dayOfYear
+        "(-?W([0-9]{2})(-?([1-7]))?)"    // -Wweek-dayOfWeek
+    ].join("|") + ")?$"
+);
+DateTime._timezoneRegexp = new RegExp(
+    "Z|(([-+])([0-9]{2})(:?([0-9]{2}))?)$"
+);
+DateTime._timeRegexp = new RegExp(
+    "^([0-9]{2})(:?([0-9]{2})(:?([0-9]{2})(\.([0-9]+))?)?)?$"
+);
+
+/**
+ * Takes a date object and a string containing an ISO 8601 date and sets the
+ * the date using information parsed from the string.  Note that this method
+ * does not parse any time information.
+ *
+ * @param {Date} dateObject the date object to modify
+ * @param {String} string an ISO 8601 string to parse
+ * @return {Date} the modified date object
+ */
+DateTime.setIso8601Date = function(dateObject, string) {
+    /*
+     *  This function has been adapted from dojo.date, v.0.3.0
+     *  http://dojotoolkit.org/.
+     */
+     
+    var d = string.match(DateTime._dateRegexp);
+    if(!d) {
+        throw new Error("Invalid date string: " + string);
+    }
+    
+    var sign = (d[1] == "-") ? -1 : 1; // BC or AD
+    var year = sign * d[2];
+    var month = d[5];
+    var date = d[7];
+    var dayofyear = d[9];
+    var week = d[11];
+    var dayofweek = (d[13]) ? d[13] : 1;
+
+    dateObject.setUTCFullYear(year);
+    if (dayofyear) { 
+        dateObject.setUTCMonth(0);
+        dateObject.setUTCDate(Number(dayofyear));
+    } else if (week) {
+        dateObject.setUTCMonth(0);
+        dateObject.setUTCDate(1);
+        var gd = dateObject.getUTCDay();
+        var day =  (gd) ? gd : 7;
+        var offset = Number(dayofweek) + (7 * Number(week));
+        
+        if (day <= 4) { 
+            dateObject.setUTCDate(offset + 1 - day); 
+        } else { 
+            dateObject.setUTCDate(offset + 8 - day); 
+        }
+    } else {
+        if (month) { 
+            dateObject.setUTCDate(1);
+            dateObject.setUTCMonth(month - 1); 
+        }
+        if (date) { 
+            dateObject.setUTCDate(date); 
+        }
+    }
+    
+    return dateObject;
+};
+
+/**
+ * Takes a date object and a string containing an ISO 8601 time and sets the
+ * the time using information parsed from the string.  Note that this method
+ * does not parse any date information.
+ *
+ * @param {Date} dateObject the date object to modify
+ * @param {String} string an ISO 8601 string to parse
+ * @return {Date} the modified date object
+ */
+DateTime.setIso8601Time = function (dateObject, string) {
+    /*
+     *  This function has been adapted from dojo.date, v.0.3.0
+     *  http://dojotoolkit.org/.
+     */
+    
+    var d = string.match(DateTime._timeRegexp);
+    if(!d) {
+        Debug.warn("Invalid time string: " + string);
+        return false;
+    }
+    var hours = d[1];
+    var mins = Number((d[3]) ? d[3] : 0);
+    var secs = (d[5]) ? d[5] : 0;
+    var ms = d[7] ? (Number("0." + d[7]) * 1000) : 0;
+
+    dateObject.setUTCHours(hours);
+    dateObject.setUTCMinutes(mins);
+    dateObject.setUTCSeconds(secs);
+    dateObject.setUTCMilliseconds(ms);
+    
+    return dateObject;
+};
+
+/**
+ * The timezone offset in minutes in the user's browser.
+ * @type Number
+ */
+DateTime.timezoneOffset = new Date().getTimezoneOffset();
+
+/**
+ * Takes a date object and a string containing an ISO 8601 date and time and 
+ * sets the date object using information parsed from the string.
+ *
+ * @param {Date} dateObject the date object to modify
+ * @param {String} string an ISO 8601 string to parse
+ * @return {Date} the modified date object
+ */
+DateTime.setIso8601 = function (dateObject, string){
+    /*
+     *  This function has been adapted from dojo.date, v.0.3.0
+     *  http://dojotoolkit.org/.
+     */
+     
+    var offset = null;
+    var comps = (string.indexOf("T") == -1) ? string.split(" ") : string.split("T");
+    
+    DateTime.setIso8601Date(dateObject, comps[0]);
+    if (comps.length == 2) { 
+        // first strip timezone info from the end
+        var d = comps[1].match(DateTime._timezoneRegexp);
+        if (d) {
+            if (d[0] == 'Z') {
+                offset = 0;
+            } else {
+                offset = (Number(d[3]) * 60) + Number(d[5]);
+                offset *= ((d[2] == '-') ? 1 : -1);
+            }
+            comps[1] = comps[1].substr(0, comps[1].length - d[0].length);
+        }
+
+        DateTime.setIso8601Time(dateObject, comps[1]); 
+    }
+    if (offset == null) {
+        offset = dateObject.getTimezoneOffset(); // local time zone if no tz info
+    }
+    dateObject.setTime(dateObject.getTime() + offset * 60000);
+    
+    return dateObject;
+};
+
+/**
+ * Takes a string containing an ISO 8601 date and returns a newly instantiated
+ * date object with the parsed date and time information from the string.
+ *
+ * @param {String} string an ISO 8601 string to parse
+ * @return {Date} a new date object created from the string
+ */
+DateTime.parseIso8601DateTime = function (string) {
+    try {
+        return DateTime.setIso8601(new Date(0), string);
+    } catch (e) {
+        return null;
+    }
+};
+
+/**
+ * Takes a string containing a Gregorian date and time and returns a newly
+ * instantiated date object with the parsed date and time information from the
+ * string.  If the param is actually an instance of Date instead of a string, 
+ * simply returns the given date instead.
+ *
+ * @param {Object} o an object, to either return or parse as a string
+ * @return {Date} the date object
+ */
+DateTime.parseGregorianDateTime = function(o) {
+    if (o == null) {
+        return null;
+    } else if (o instanceof Date) {
+        return o;
+    }
+    
+    var s = o.toString();
+    if (s.length > 0 && s.length < 8) {
+        var space = s.indexOf(" ");
+        if (space > 0) {
+            var year = parseInt(s.substr(0, space));
+            var suffix = s.substr(space + 1);
+            if (suffix.toLowerCase() == "bc") {
+                year = 1 - year;
+            }
+        } else {
+            var year = parseInt(s);
+        }
+            
+        var d = new Date(0);
+        d.setUTCFullYear(year);
+        
+        return d;
+    }
+    
+    try {
+        return new Date(Date.parse(s));
+    } catch (e) {
+        return null;
+    }
+};
+
+/**
+ * Rounds date objects down to the nearest interval or multiple of an interval.
+ * This method modifies the given date object, converting it to the given
+ * timezone if specified.
+ * 
+ * @param {Date} date the date object to round
+ * @param {Number} intervalUnit a constant, integer index specifying an 
+ *   interval, e.g. DateTime.HOUR
+ * @param {Number} timeZone a timezone shift, given in hours
+ * @param {Number} multiple a multiple of the interval to round by
+ * @param {Number} firstDayOfWeek an integer specifying the first day of the
+ *   week, 0 corresponds to Sunday, 1 to Monday, etc.
+ */
+DateTime.roundDownToInterval = function(date, intervalUnit, timeZone, multiple, firstDayOfWeek) {
+    var timeShift = timeZone * 
+        DateTime.gregorianUnitLengths[DateTime.HOUR];
+        
+    var date2 = new Date(date.getTime() + timeShift);
+    var clearInDay = function(d) {
+        d.setUTCMilliseconds(0);
+        d.setUTCSeconds(0);
+        d.setUTCMinutes(0);
+        d.setUTCHours(0);
+    };
+    var clearInYear = function(d) {
+        clearInDay(d);
+        d.setUTCDate(1);
+        d.setUTCMonth(0);
+    };
+    
+    switch(intervalUnit) {
+    case DateTime.MILLISECOND:
+        var x = date2.getUTCMilliseconds();
+        date2.setUTCMilliseconds(x - (x % multiple));
+        break;
+    case DateTime.SECOND:
+        date2.setUTCMilliseconds(0);
+        
+        var x = date2.getUTCSeconds();
+        date2.setUTCSeconds(x - (x % multiple));
+        break;
+    case DateTime.MINUTE:
+        date2.setUTCMilliseconds(0);
+        date2.setUTCSeconds(0);
+        
+        var x = date2.getUTCMinutes();
+        date2.setTime(date2.getTime() - 
+            (x % multiple) * DateTime.gregorianUnitLengths[DateTime.MINUTE]);
+        break;
+    case DateTime.HOUR:
+        date2.setUTCMilliseconds(0);
+        date2.setUTCSeconds(0);
+        date2.setUTCMinutes(0);
+        
+        var x = date2.getUTCHours();
+        date2.setUTCHours(x - (x % multiple));
+        break;
+    case DateTime.DAY:
+        clearInDay(date2);
+        break;
+    case DateTime.WEEK:
+        clearInDay(date2);
+        var d = (date2.getUTCDay() + 7 - firstDayOfWeek) % 7;
+        date2.setTime(date2.getTime() - 
+            d * DateTime.gregorianUnitLengths[DateTime.DAY]);
+        break;
+    case DateTime.MONTH:
+        clearInDay(date2);
+        date2.setUTCDate(1);
+        
+        var x = date2.getUTCMonth();
+        date2.setUTCMonth(x - (x % multiple));
+        break;
+    case DateTime.YEAR:
+        clearInYear(date2);
+        
+        var x = date2.getUTCFullYear();
+        date2.setUTCFullYear(x - (x % multiple));
+        break;
+    case DateTime.DECADE:
+        clearInYear(date2);
+        date2.setUTCFullYear(Math.floor(date2.getUTCFullYear() / 10) * 10);
+        break;
+    case DateTime.CENTURY:
+        clearInYear(date2);
+        date2.setUTCFullYear(Math.floor(date2.getUTCFullYear() / 100) * 100);
+        break;
+    case DateTime.MILLENNIUM:
+        clearInYear(date2);
+        date2.setUTCFullYear(Math.floor(date2.getUTCFullYear() / 1000) * 1000);
+        break;
+    }
+    
+    date.setTime(date2.getTime() - timeShift);
+};
+
+/**
+ * Rounds date objects up to the nearest interval or multiple of an interval.
+ * This method modifies the given date object, converting it to the given
+ * timezone if specified.
+ * 
+ * @param {Date} date the date object to round
+ * @param {Number} intervalUnit a constant, integer index specifying an 
+ *   interval, e.g. DateTime.HOUR
+ * @param {Number} timeZone a timezone shift, given in hours
+ * @param {Number} multiple a multiple of the interval to round by
+ * @param {Number} firstDayOfWeek an integer specifying the first day of the
+ *   week, 0 corresponds to Sunday, 1 to Monday, etc.
+ * @see DateTime.roundDownToInterval
+ */
+DateTime.roundUpToInterval = function(date, intervalUnit, timeZone, multiple, firstDayOfWeek) {
+    var originalTime = date.getTime();
+    DateTime.roundDownToInterval(date, intervalUnit, timeZone, multiple, firstDayOfWeek);
+    if (date.getTime() < originalTime) {
+        date.setTime(date.getTime() + 
+            DateTime.gregorianUnitLengths[intervalUnit] * multiple);
+    }
+};
+
+/**
+ * Increments a date object by a specified interval, taking into
+ * consideration the timezone.
+ *
+ * @param {Date} date the date object to increment
+ * @param {Number} intervalUnit a constant, integer index specifying an 
+ *   interval, e.g. DateTime.HOUR
+ * @param {Number} timeZone the timezone offset in hours
+ */
+DateTime.incrementByInterval = function(date, intervalUnit, timeZone) {
+    timeZone = (typeof timeZone == 'undefined') ? 0 : timeZone;
+
+    var timeShift = timeZone * 
+        DateTime.gregorianUnitLengths[DateTime.HOUR];
+        
+    var date2 = new Date(date.getTime() + timeShift);
+
+    switch(intervalUnit) {
+    case DateTime.MILLISECOND:
+        date2.setTime(date2.getTime() + 1)
+        break;
+    case DateTime.SECOND:
+        date2.setTime(date2.getTime() + 1000);
+        break;
+    case DateTime.MINUTE:
+        date2.setTime(date2.getTime() + 
+            DateTime.gregorianUnitLengths[DateTime.MINUTE]);
+        break;
+    case DateTime.HOUR:
+        date2.setTime(date2.getTime() + 
+            DateTime.gregorianUnitLengths[DateTime.HOUR]);
+        break;
+    case DateTime.DAY:
+        date2.setUTCDate(date2.getUTCDate() + 1);
+        break;
+    case DateTime.WEEK:
+        date2.setUTCDate(date2.getUTCDate() + 7);
+        break;
+    case DateTime.MONTH:
+        date2.setUTCMonth(date2.getUTCMonth() + 1);
+        break;
+    case DateTime.YEAR:
+        date2.setUTCFullYear(date2.getUTCFullYear() + 1);
+        break;
+    case DateTime.DECADE:
+        date2.setUTCFullYear(date2.getUTCFullYear() + 10);
+        break;
+    case DateTime.CENTURY:
+        date2.setUTCFullYear(date2.getUTCFullYear() + 100);
+        break;
+    case DateTime.MILLENNIUM:
+        date2.setUTCFullYear(date2.getUTCFullYear() + 1000);
+        break;
+    }
+
+    date.setTime(date2.getTime() - timeShift);
+};
+
+/**
+ * Returns a new date object with the given time offset removed.
+ *
+ * @param {Date} date the starting date
+ * @param {Number} timeZone a timezone specified in an hour offset to remove
+ * @return {Date} a new date object with the offset removed
+ */
+DateTime.removeTimeZoneOffset = function(date, timeZone) {
+    return new Date(date.getTime() + 
+        timeZone * DateTime.gregorianUnitLengths[DateTime.HOUR]);
+};
+
+/**
+ * Returns the timezone of the user's browser.
+ *
+ * @return {Number} the timezone in the user's locale in hours
+ */
+DateTime.getTimezone = function() {
+    var d = new Date().getTimezoneOffset();
+    return d / -60;
+};
+
+    return DateTime;
+});
+
+/*==================================================
+ *  String Utility Functions and Constants
+ *==================================================
+ */
+
+define('scripts/string',[],function() {
+    var StringUtils = {};
+
+    StringUtils.trim = function(s) {
+        return s.replace(/^\s+|\s+$/g, '');
+    };
+
+    StringUtils.startsWith = function(s, prefix) {
+        return s.length >= prefix.length && s.substr(0, prefix.length) === prefix;
+    };
+
+    StringUtils.endsWith = function(s, suffix) {
+        return s.length >= suffix.length && s.substr(s.length - suffix.length) === suffix;
+    };
+
+    StringUtils.substitute = function(s, objects) {
+        var result, start, percent, n;
+        result = "";
+        start = 0;
+        while (start < s.length - 1) {
+            percent = s.indexOf("%", start);
+            if (percent < 0 || percent === s.length - 1) {
+                break;
+            } else if (percent > start && s.charAt(percent - 1) === "\\") {
+                result += s.substring(start, percent - 1) + "%";
+                start = percent + 1;
+            } else {
+                n = parseInt(s.charAt(percent + 1));
+                if (isNaN(n) || n >= objects.length) {
+                    result += s.substring(start, percent + 2);
+                } else {
+                    result += s.substring(start, percent) + objects[n].toString();
+                }
+                start = percent + 2;
+            }
+        }
+        
+        if (start < s.length) {
+            result += s.substring(start);
+        }
+        return result;
+    };
+
+    return StringUtils;
+});
+
+/*==================================================
+ *  HTML Utility Functions
+ *==================================================
+ */
+
+define('scripts/html',[],function() {
+var HTML = new Object();
+
+HTML._e2uHash = {};
+(function() {
+    var e2uHash = HTML._e2uHash;
+    e2uHash['nbsp']= '\u00A0';
+    e2uHash['iexcl']= '\u00A1';
+    e2uHash['cent']= '\u00A2';
+    e2uHash['pound']= '\u00A3';
+    e2uHash['curren']= '\u00A4';
+    e2uHash['yen']= '\u00A5';
+    e2uHash['brvbar']= '\u00A6';
+    e2uHash['sect']= '\u00A7';
+    e2uHash['uml']= '\u00A8';
+    e2uHash['copy']= '\u00A9';
+    e2uHash['ordf']= '\u00AA';
+    e2uHash['laquo']= '\u00AB';
+    e2uHash['not']= '\u00AC';
+    e2uHash['shy']= '\u00AD';
+    e2uHash['reg']= '\u00AE';
+    e2uHash['macr']= '\u00AF';
+    e2uHash['deg']= '\u00B0';
+    e2uHash['plusmn']= '\u00B1';
+    e2uHash['sup2']= '\u00B2';
+    e2uHash['sup3']= '\u00B3';
+    e2uHash['acute']= '\u00B4';
+    e2uHash['micro']= '\u00B5';
+    e2uHash['para']= '\u00B6';
+    e2uHash['middot']= '\u00B7';
+    e2uHash['cedil']= '\u00B8';
+    e2uHash['sup1']= '\u00B9';
+    e2uHash['ordm']= '\u00BA';
+    e2uHash['raquo']= '\u00BB';
+    e2uHash['frac14']= '\u00BC';
+    e2uHash['frac12']= '\u00BD';
+    e2uHash['frac34']= '\u00BE';
+    e2uHash['iquest']= '\u00BF';
+    e2uHash['Agrave']= '\u00C0';
+    e2uHash['Aacute']= '\u00C1';
+    e2uHash['Acirc']= '\u00C2';
+    e2uHash['Atilde']= '\u00C3';
+    e2uHash['Auml']= '\u00C4';
+    e2uHash['Aring']= '\u00C5';
+    e2uHash['AElig']= '\u00C6';
+    e2uHash['Ccedil']= '\u00C7';
+    e2uHash['Egrave']= '\u00C8';
+    e2uHash['Eacute']= '\u00C9';
+    e2uHash['Ecirc']= '\u00CA';
+    e2uHash['Euml']= '\u00CB';
+    e2uHash['Igrave']= '\u00CC';
+    e2uHash['Iacute']= '\u00CD';
+    e2uHash['Icirc']= '\u00CE';
+    e2uHash['Iuml']= '\u00CF';
+    e2uHash['ETH']= '\u00D0';
+    e2uHash['Ntilde']= '\u00D1';
+    e2uHash['Ograve']= '\u00D2';
+    e2uHash['Oacute']= '\u00D3';
+    e2uHash['Ocirc']= '\u00D4';
+    e2uHash['Otilde']= '\u00D5';
+    e2uHash['Ouml']= '\u00D6';
+    e2uHash['times']= '\u00D7';
+    e2uHash['Oslash']= '\u00D8';
+    e2uHash['Ugrave']= '\u00D9';
+    e2uHash['Uacute']= '\u00DA';
+    e2uHash['Ucirc']= '\u00DB';
+    e2uHash['Uuml']= '\u00DC';
+    e2uHash['Yacute']= '\u00DD';
+    e2uHash['THORN']= '\u00DE';
+    e2uHash['szlig']= '\u00DF';
+    e2uHash['agrave']= '\u00E0';
+    e2uHash['aacute']= '\u00E1';
+    e2uHash['acirc']= '\u00E2';
+    e2uHash['atilde']= '\u00E3';
+    e2uHash['auml']= '\u00E4';
+    e2uHash['aring']= '\u00E5';
+    e2uHash['aelig']= '\u00E6';
+    e2uHash['ccedil']= '\u00E7';
+    e2uHash['egrave']= '\u00E8';
+    e2uHash['eacute']= '\u00E9';
+    e2uHash['ecirc']= '\u00EA';
+    e2uHash['euml']= '\u00EB';
+    e2uHash['igrave']= '\u00EC';
+    e2uHash['iacute']= '\u00ED';
+    e2uHash['icirc']= '\u00EE';
+    e2uHash['iuml']= '\u00EF';
+    e2uHash['eth']= '\u00F0';
+    e2uHash['ntilde']= '\u00F1';
+    e2uHash['ograve']= '\u00F2';
+    e2uHash['oacute']= '\u00F3';
+    e2uHash['ocirc']= '\u00F4';
+    e2uHash['otilde']= '\u00F5';
+    e2uHash['ouml']= '\u00F6';
+    e2uHash['divide']= '\u00F7';
+    e2uHash['oslash']= '\u00F8';
+    e2uHash['ugrave']= '\u00F9';
+    e2uHash['uacute']= '\u00FA';
+    e2uHash['ucirc']= '\u00FB';
+    e2uHash['uuml']= '\u00FC';
+    e2uHash['yacute']= '\u00FD';
+    e2uHash['thorn']= '\u00FE';
+    e2uHash['yuml']= '\u00FF';
+    e2uHash['quot']= '\u0022';
+    e2uHash['amp']= '\u0026';
+    e2uHash['lt']= '\u003C';
+    e2uHash['gt']= '\u003E';
+    e2uHash['OElig']= '';
+    e2uHash['oelig']= '\u0153';
+    e2uHash['Scaron']= '\u0160';
+    e2uHash['scaron']= '\u0161';
+    e2uHash['Yuml']= '\u0178';
+    e2uHash['circ']= '\u02C6';
+    e2uHash['tilde']= '\u02DC';
+    e2uHash['ensp']= '\u2002';
+    e2uHash['emsp']= '\u2003';
+    e2uHash['thinsp']= '\u2009';
+    e2uHash['zwnj']= '\u200C';
+    e2uHash['zwj']= '\u200D';
+    e2uHash['lrm']= '\u200E';
+    e2uHash['rlm']= '\u200F';
+    e2uHash['ndash']= '\u2013';
+    e2uHash['mdash']= '\u2014';
+    e2uHash['lsquo']= '\u2018';
+    e2uHash['rsquo']= '\u2019';
+    e2uHash['sbquo']= '\u201A';
+    e2uHash['ldquo']= '\u201C';
+    e2uHash['rdquo']= '\u201D';
+    e2uHash['bdquo']= '\u201E';
+    e2uHash['dagger']= '\u2020';
+    e2uHash['Dagger']= '\u2021';
+    e2uHash['permil']= '\u2030';
+    e2uHash['lsaquo']= '\u2039';
+    e2uHash['rsaquo']= '\u203A';
+    e2uHash['euro']= '\u20AC';
+    e2uHash['fnof']= '\u0192';
+    e2uHash['Alpha']= '\u0391';
+    e2uHash['Beta']= '\u0392';
+    e2uHash['Gamma']= '\u0393';
+    e2uHash['Delta']= '\u0394';
+    e2uHash['Epsilon']= '\u0395';
+    e2uHash['Zeta']= '\u0396';
+    e2uHash['Eta']= '\u0397';
+    e2uHash['Theta']= '\u0398';
+    e2uHash['Iota']= '\u0399';
+    e2uHash['Kappa']= '\u039A';
+    e2uHash['Lambda']= '\u039B';
+    e2uHash['Mu']= '\u039C';
+    e2uHash['Nu']= '\u039D';
+    e2uHash['Xi']= '\u039E';
+    e2uHash['Omicron']= '\u039F';
+    e2uHash['Pi']= '\u03A0';
+    e2uHash['Rho']= '\u03A1';
+    e2uHash['Sigma']= '\u03A3';
+    e2uHash['Tau']= '\u03A4';
+    e2uHash['Upsilon']= '\u03A5';
+    e2uHash['Phi']= '\u03A6';
+    e2uHash['Chi']= '\u03A7';
+    e2uHash['Psi']= '\u03A8';
+    e2uHash['Omega']= '\u03A9';
+    e2uHash['alpha']= '\u03B1';
+    e2uHash['beta']= '\u03B2';
+    e2uHash['gamma']= '\u03B3';
+    e2uHash['delta']= '\u03B4';
+    e2uHash['epsilon']= '\u03B5';
+    e2uHash['zeta']= '\u03B6';
+    e2uHash['eta']= '\u03B7';
+    e2uHash['theta']= '\u03B8';
+    e2uHash['iota']= '\u03B9';
+    e2uHash['kappa']= '\u03BA';
+    e2uHash['lambda']= '\u03BB';
+    e2uHash['mu']= '\u03BC';
+    e2uHash['nu']= '\u03BD';
+    e2uHash['xi']= '\u03BE';
+    e2uHash['omicron']= '\u03BF';
+    e2uHash['pi']= '\u03C0';
+    e2uHash['rho']= '\u03C1';
+    e2uHash['sigmaf']= '\u03C2';
+    e2uHash['sigma']= '\u03C3';
+    e2uHash['tau']= '\u03C4';
+    e2uHash['upsilon']= '\u03C5';
+    e2uHash['phi']= '\u03C6';
+    e2uHash['chi']= '\u03C7';
+    e2uHash['psi']= '\u03C8';
+    e2uHash['omega']= '\u03C9';
+    e2uHash['thetasym']= '\u03D1';
+    e2uHash['upsih']= '\u03D2';
+    e2uHash['piv']= '\u03D6';
+    e2uHash['bull']= '\u2022';
+    e2uHash['hellip']= '\u2026';
+    e2uHash['prime']= '\u2032';
+    e2uHash['Prime']= '\u2033';
+    e2uHash['oline']= '\u203E';
+    e2uHash['frasl']= '\u2044';
+    e2uHash['weierp']= '\u2118';
+    e2uHash['image']= '\u2111';
+    e2uHash['real']= '\u211C';
+    e2uHash['trade']= '\u2122';
+    e2uHash['alefsym']= '\u2135';
+    e2uHash['larr']= '\u2190';
+    e2uHash['uarr']= '\u2191';
+    e2uHash['rarr']= '\u2192';
+    e2uHash['darr']= '\u2193';
+    e2uHash['harr']= '\u2194';
+    e2uHash['crarr']= '\u21B5';
+    e2uHash['lArr']= '\u21D0';
+    e2uHash['uArr']= '\u21D1';
+    e2uHash['rArr']= '\u21D2';
+    e2uHash['dArr']= '\u21D3';
+    e2uHash['hArr']= '\u21D4';
+    e2uHash['forall']= '\u2200';
+    e2uHash['part']= '\u2202';
+    e2uHash['exist']= '\u2203';
+    e2uHash['empty']= '\u2205';
+    e2uHash['nabla']= '\u2207';
+    e2uHash['isin']= '\u2208';
+    e2uHash['notin']= '\u2209';
+    e2uHash['ni']= '\u220B';
+    e2uHash['prod']= '\u220F';
+    e2uHash['sum']= '\u2211';
+    e2uHash['minus']= '\u2212';
+    e2uHash['lowast']= '\u2217';
+    e2uHash['radic']= '\u221A';
+    e2uHash['prop']= '\u221D';
+    e2uHash['infin']= '\u221E';
+    e2uHash['ang']= '\u2220';
+    e2uHash['and']= '\u2227';
+    e2uHash['or']= '\u2228';
+    e2uHash['cap']= '\u2229';
+    e2uHash['cup']= '\u222A';
+    e2uHash['int']= '\u222B';
+    e2uHash['there4']= '\u2234';
+    e2uHash['sim']= '\u223C';
+    e2uHash['cong']= '\u2245';
+    e2uHash['asymp']= '\u2248';
+    e2uHash['ne']= '\u2260';
+    e2uHash['equiv']= '\u2261';
+    e2uHash['le']= '\u2264';
+    e2uHash['ge']= '\u2265';
+    e2uHash['sub']= '\u2282';
+    e2uHash['sup']= '\u2283';
+    e2uHash['nsub']= '\u2284';
+    e2uHash['sube']= '\u2286';
+    e2uHash['supe']= '\u2287';
+    e2uHash['oplus']= '\u2295';
+    e2uHash['otimes']= '\u2297';
+    e2uHash['perp']= '\u22A5';
+    e2uHash['sdot']= '\u22C5';
+    e2uHash['lceil']= '\u2308';
+    e2uHash['rceil']= '\u2309';
+    e2uHash['lfloor']= '\u230A';
+    e2uHash['rfloor']= '\u230B';
+    e2uHash['lang']= '\u2329';
+    e2uHash['rang']= '\u232A';
+    e2uHash['loz']= '\u25CA';
+    e2uHash['spades']= '\u2660';
+    e2uHash['clubs']= '\u2663';
+    e2uHash['hearts']= '\u2665';
+    e2uHash['diams']= '\u2666'; 
+})();
+
+HTML.deEntify = function(s) {
+    var e2uHash = HTML._e2uHash;
+    
+    var re = /&(\w+?);/;
+    while (re.test(s)) {
+        var m = s.match(re);
+        s = s.replace(re, e2uHash[m[1]]);
+    }
+    return s;
+};
+
+    return HTML;
+});
+
+define('scripts/set',[],function() {
+/**
+ * A basic set (in the mathematical sense) data structure
+ *
+ * @constructor
+ * @param {Array or Set} [a] an initial collection
+ */
+var Set = function(a) {
+    this._hash = {};
+    this._count = 0;
+    
+    if (a instanceof Array) {
+        for (var i = 0; i < a.length; i++) {
+            this.add(a[i]);
+        }
+    } else if (a instanceof Set) {
+        this.addSet(a);
+    }
+}
+
+/**
+ * Adds the given object to this set, assuming there it does not already exist
+ *
+ * @param {Object} o the object to add
+ * @return {Boolean} true if the object was added, false if not
+ */
+Set.prototype.add = function(o) {
+    if (!(o in this._hash)) {
+        this._hash[o] = true;
+        this._count++;
+        return true;
+    }
+    return false;
+}
+
+/**
+ * Adds each element in the given set to this set
+ *
+ * @param {Set} set the set of elements to add
+ */
+Set.prototype.addSet = function(set) {
+    for (var o in set._hash) {
+        this.add(o);
+    }
+}
+
+/**
+ * Removes the given element from this set
+ *
+ * @param {Object} o the object to remove
+ * @return {Boolean} true if the object was successfully removed,
+ *   false otherwise
+ */
+Set.prototype.remove = function(o) {
+    if (o in this._hash) {
+        delete this._hash[o];
+        this._count--;
+        return true;
+    }
+    return false;
+}
+
+/**
+ * Removes the elements in this set that correspond to the elements in the
+ * given set
+ *
+ * @param {Set} set the set of elements to remove
+ */
+Set.prototype.removeSet = function(set) {
+    for (var o in set._hash) {
+        this.remove(o);
+    }
+}
+
+/**
+ * Removes all elements in this set that are not present in the given set, i.e.
+ * modifies this set to the intersection of the two sets
+ *
+ * @param {Set} set the set to intersect
+ */
+Set.prototype.retainSet = function(set) {
+    for (var o in this._hash) {
+        if (!set.contains(o)) {
+            delete this._hash[o];
+            this._count--;
+        }
+    }
+}
+
+/**
+ * Returns whether or not the given element exists in this set
+ *
+ * @param {Set} o the object to test for
+ * @return {Boolean} true if the object is present, false otherwise
+ */
+Set.prototype.contains = function(o) {
+    return (o in this._hash);
+}
+
+/**
+ * Returns the number of elements in this set
+ *
+ * @return {Number} the number of elements in this set
+ */
+Set.prototype.size = function() {
+    return this._count;
+}
+
+/**
+ * Returns the elements of this set as an array
+ *
+ * @return {Array} a new array containing the elements of this set
+ */
+Set.prototype.toArray = function() {
+    var a = [];
+    for (var o in this._hash) {
+        a.push(o);
+    }
+    return a;
+}
+
+/**
+ * Iterates through the elements of this set, order unspecified, executing the
+ * given function on each element until the function returns true
+ *
+ * @param {Function} f a function of form f(element)
+ */
+Set.prototype.visit = function(f) {
+    for (var o in this._hash) {
+        if (f(o) == true) {
+            break;
+        }
+    }
+}
+
+    return Set;
+});
+
+define('scripts/sorted-array',[],function() {
+/**
+ * A sorted array data structure
+ *
+ * @constructor
+ */
+var SortedArray = function(compare, initialArray) {
+    this._a = (initialArray instanceof Array) ? initialArray : [];
+    this._compare = compare;
+};
+
+SortedArray.prototype.add = function(elmt) {
+    var sa = this;
+    var index = this.find(function(elmt2) {
+        return sa._compare(elmt2, elmt);
+    });
+    
+    if (index < this._a.length) {
+        this._a.splice(index, 0, elmt);
+    } else {
+        this._a.push(elmt);
+    }
+};
+
+SortedArray.prototype.remove = function(elmt) {
+    var sa = this;
+    var index = this.find(function(elmt2) {
+        return sa._compare(elmt2, elmt);
+    });
+    
+    while (index < this._a.length && this._compare(this._a[index], elmt) == 0) {
+        if (this._a[index] == elmt) {
+            this._a.splice(index, 1);
+            return true;
+        } else {
+            index++;
+        }
+    }
+    return false;
+};
+
+SortedArray.prototype.removeAll = function() {
+    this._a = [];
+};
+
+SortedArray.prototype.elementAt = function(index) {
+    return this._a[index];
+};
+
+SortedArray.prototype.length = function() {
+    return this._a.length;
+};
+
+SortedArray.prototype.find = function(compare) {
+    var a = 0;
+    var b = this._a.length;
+    
+    while (a < b) {
+        var mid = Math.floor((a + b) / 2);
+        var c = compare(this._a[mid]);
+        if (mid == a) {
+            return c < 0 ? a+1 : a;
+        } else if (c < 0) {
+            a = mid;
+        } else {
+            b = mid;
+        }
+    }
+    return a;
+};
+
+SortedArray.prototype.getFirst = function() {
+    return (this._a.length > 0) ? this._a[0] : null;
+};
+
+SortedArray.prototype.getLast = function() {
+    return (this._a.length > 0) ? this._a[this._a.length - 1] : null;
+};
+
+    return SortedArray;
+});
+
+define('scripts/units',["./date-time"], function(DateTime) {
+/*==================================================
+ *  Default Unit
+ *==================================================
+ */
+
+var NativeDateUnit = new Object();
+
+NativeDateUnit.makeDefaultValue = function() {
+    return new Date();
+};
+
+NativeDateUnit.cloneValue = function(v) {
+    return new Date(v.getTime());
+};
+
+NativeDateUnit.getParser = function(format) {
+    if (typeof format == "string") {
+        format = format.toLowerCase();
+    }
+    
+    var parser = (format == "iso8601" || format == "iso 8601") ?
+                    DateTime.parseIso8601DateTime : 
+                    DateTime.parseGregorianDateTime;
+                    
+    return function(d) {
+        if (typeof d != 'undefined' && d !== null && typeof d.toUTCString == "function") {
+            return d;
+        } else {
+            return parser(d);
+        }
+    };
+};
+
+NativeDateUnit.parseFromObject = function(o) {
+    return DateTime.parseGregorianDateTime(o);
+};
+
+NativeDateUnit.toNumber = function(v) {
+    return v.getTime();
+};
+
+NativeDateUnit.fromNumber = function(n) {
+    return new Date(n);
+};
+
+NativeDateUnit.compare = function(v1, v2) {
+    var n1, n2;
+    if (typeof v1 == "object") {
+        n1 = v1.getTime();
+    } else {
+        n1 = Number(v1);
+    }
+    if (typeof v2 == "object") {
+        n2 = v2.getTime();
+    } else {
+        n2 = Number(v2);
+    }
+    
+    return n1 - n2;
+};
+
+NativeDateUnit.earlier = function(v1, v2) {
+    return NativeDateUnit.compare(v1, v2) < 0 ? v1 : v2;
+};
+
+NativeDateUnit.later = function(v1, v2) {
+    return NativeDateUnit.compare(v1, v2) > 0 ? v1 : v2;
+};
+
+NativeDateUnit.change = function(v, n) {
+    return new Date(v.getTime() + n);
+};
+
+
+    return NativeDateUnit;
+});
+
+define('scripts/event-index',["./units", "./sorted-array"], function(NativeDateUnit, SortedArray) {
+/*==================================================
+ *  Event Index
+ *==================================================
+ */
+
+var EventIndex = function(unit) {
+    var eventIndex = this;
+    
+    this._unit = (unit != null) ? unit : NativeDateUnit;
+    this._events = new SortedArray(
+        function(event1, event2) {
+            return eventIndex._unit.compare(event1.getStart(), event2.getStart());
+        }
+    );
+    this._idToEvent = {};
+    this._indexed = true;
+};
+
+EventIndex.prototype.getUnit = function() {
+    return this._unit;
+};
+
+EventIndex.prototype.getEvent = function(id) {
+    return this._idToEvent[id];
+};
+
+EventIndex.prototype.add = function(evt) {
+    this._events.add(evt);
+    this._idToEvent[evt.getID()] = evt;
+    this._indexed = false;
+};
+
+EventIndex.prototype.removeAll = function() {
+    this._events.removeAll();
+    this._idToEvent = {};
+    this._indexed = false;
+};
+
+EventIndex.prototype.getCount = function() {
+    return this._events.length();
+};
+
+EventIndex.prototype.getIterator = function(startDate, endDate) {
+    if (!this._indexed) {
+        this._index();
+    }
+    return new EventIndex._Iterator(this._events, startDate, endDate, this._unit);
+};
+
+EventIndex.prototype.getReverseIterator = function(startDate, endDate) {
+    if (!this._indexed) {
+        this._index();
+    }
+    return new EventIndex._ReverseIterator(this._events, startDate, endDate, this._unit);
+};
+
+EventIndex.prototype.getAllIterator = function() {
+    return new EventIndex._AllIterator(this._events);
+};
+
+EventIndex.prototype.getEarliestDate = function() {
+    var evt = this._events.getFirst();
+    return (evt == null) ? null : evt.getStart();
+};
+
+EventIndex.prototype.getLatestDate = function() {
+    var evt = this._events.getLast();
+    if (evt == null) {
+        return null;
+    }
+    
+    if (!this._indexed) {
+        this._index();
+    }
+    
+    var index = evt._earliestOverlapIndex;
+    var date = this._events.elementAt(index).getEnd();
+    for (var i = index + 1; i < this._events.length(); i++) {
+        date = this._unit.later(date, this._events.elementAt(i).getEnd());
+    }
+    
+    return date;
+};
+
+EventIndex.prototype._index = function() {
+    /*
+     *  For each event, we want to find the earliest preceding
+     *  event that overlaps with it, if any.
+     */
+    
+    var l = this._events.length();
+    for (var i = 0; i < l; i++) {
+        var evt = this._events.elementAt(i);
+        evt._earliestOverlapIndex = i;
+    }
+    
+    var toIndex = 1;
+    for (var i = 0; i < l; i++) {
+        var evt = this._events.elementAt(i);
+        var end = evt.getEnd();
+        
+        toIndex = Math.max(toIndex, i + 1);
+        while (toIndex < l) {
+            var evt2 = this._events.elementAt(toIndex);
+            var start2 = evt2.getStart();
+            
+            if (this._unit.compare(start2, end) < 0) {
+                evt2._earliestOverlapIndex = i;
+                toIndex++;
+            } else {
+                break;
+            }
+        }
+    }
+    this._indexed = true;
+};
+
+EventIndex._Iterator = function(events, startDate, endDate, unit) {
+    this._events = events;
+    this._startDate = startDate;
+    this._endDate = endDate;
+    this._unit = unit;
+    
+    this._currentIndex = events.find(function(evt) {
+        return unit.compare(evt.getStart(), startDate);
+    });
+    if (this._currentIndex - 1 >= 0) {
+        this._currentIndex = this._events.elementAt(this._currentIndex - 1)._earliestOverlapIndex;
+    }
+    this._currentIndex--;
+    
+    this._maxIndex = events.find(function(evt) {
+        return unit.compare(evt.getStart(), endDate);
+    });
+    
+    this._hasNext = false;
+    this._next = null;
+    this._findNext();
+};
+
+EventIndex._Iterator.prototype = {
+    hasNext: function() { return this._hasNext; },
+    next: function() {
+        if (this._hasNext) {
+            var next = this._next;
+            this._findNext();
+            
+            return next;
+        } else {
+            return null;
+        }
+    },
+    _findNext: function() {
+        var unit = this._unit;
+        while ((++this._currentIndex) < this._maxIndex) {
+            var evt = this._events.elementAt(this._currentIndex);
+            if (unit.compare(evt.getStart(), this._endDate) < 0 &&
+                unit.compare(evt.getEnd(), this._startDate) > 0) {
+                
+                this._next = evt;
+                this._hasNext = true;
+                return;
+            }
+        }
+        this._next = null;
+        this._hasNext = false;
+    }
+};
+
+EventIndex._ReverseIterator = function(events, startDate, endDate, unit) {
+    this._events = events;
+    this._startDate = startDate;
+    this._endDate = endDate;
+    this._unit = unit;
+    
+    this._minIndex = events.find(function(evt) {
+        return unit.compare(evt.getStart(), startDate);
+    });
+    if (this._minIndex - 1 >= 0) {
+        this._minIndex = this._events.elementAt(this._minIndex - 1)._earliestOverlapIndex;
+    }
+    
+    this._maxIndex = events.find(function(evt) {
+        return unit.compare(evt.getStart(), endDate);
+    });
+    
+    this._currentIndex = this._maxIndex;
+    this._hasNext = false;
+    this._next = null;
+    this._findNext();
+};
+
+EventIndex._ReverseIterator.prototype = {
+    hasNext: function() { return this._hasNext; },
+    next: function() {
+        if (this._hasNext) {
+            var next = this._next;
+            this._findNext();
+            
+            return next;
+        } else {
+            return null;
+        }
+    },
+    _findNext: function() {
+        var unit = this._unit;
+        while ((--this._currentIndex) >= this._minIndex) {
+            var evt = this._events.elementAt(this._currentIndex);
+            if (unit.compare(evt.getStart(), this._endDate) < 0 &&
+                unit.compare(evt.getEnd(), this._startDate) > 0) {
+                
+                this._next = evt;
+                this._hasNext = true;
+                return;
+            }
+        }
+        this._next = null;
+        this._hasNext = false;
+    }
+};
+
+EventIndex._AllIterator = function(events) {
+    this._events = events;
+    this._index = 0;
+};
+
+EventIndex._AllIterator.prototype = {
+    hasNext: function() {
+        return this._index < this._events.length();
+    },
+    next: function() {
+        return this._index < this._events.length() ?
+            this._events.elementAt(this._index++) : null;
+    }
+};
+
+    return EventIndex;
+});
+
+/*==================================================
+ *  General, miscellaneous SimileAjax stuff
+ *==================================================
+ */
+
+define('scripts/ajax',["./debug"], function(Debug) {
+var ListenerQueue = function(wildcardHandlerName) {
+    this._listeners = [];
+    this._wildcardHandlerName = wildcardHandlerName;
+};
+
+ListenerQueue.prototype.add = function(listener) {
+    this._listeners.push(listener);
+};
+
+ListenerQueue.prototype.remove = function(listener) {
+    var listeners = this._listeners;
+    for (var i = 0; i < listeners.length; i++) {
+        if (listeners[i] == listener) {
+            listeners.splice(i, 1);
+            break;
+        }
+    }
+};
+
+ListenerQueue.prototype.fire = function(handlerName, args) {
+    var listeners = [].concat(this._listeners);
+    for (var i = 0; i < listeners.length; i++) {
+        var listener = listeners[i];
+        if (handlerName in listener) {
+            try {
+                listener[handlerName].apply(listener, args);
+            } catch (e) {
+                Debug.exception("Error firing event of name " + handlerName, e);
+            }
+        } else if (this._wildcardHandlerName != null &&
+            this._wildcardHandlerName in listener) {
+            try {
+                listener[this._wildcardHandlerName].apply(listener, [ handlerName ]);
+            } catch (e) {
+                Debug.exception("Error firing event of name " + handlerName + " to wildcard handler", e);
+            }
+        }
+    }
+};
+
+    return ListenerQueue;
+});
+
+/*======================================================================
+ *  History
+ *
+ *  This is a singleton that keeps track of undoable user actions and 
+ *  performs undos and redos in response to the browser's Back and 
+ *  Forward buttons.
+ *
+ *  Call addAction(action) to register an undoable user action. action
+ *  must have 4 fields:
+ *
+ *      perform: an argument-less function that carries out the action
+ *      undo:    an argument-less function that undos the action
+ *      label:   a short, user-friendly string describing the action
+ *      uiLayer: the UI layer on which the action takes place
+ *
+ *  By default, the history keeps track of upto 10 actions. You can 
+ *  configure this behavior by setting 
+ *      SAHistory.maxHistoryLength
+ *  to a different number.
+ *
+ *  An iframe is inserted into the document's body element to track 
+ *  onload events.
+ *======================================================================
+ */
+define('scripts/history',[
+    "./ajax",
+    "./dom",
+    "./debug",
+    "./window-manager"
+], function(ListenerQueue, DOM, Debug, WindowManager) {
+var SAHistory = {
+    maxHistoryLength:       10,
+    historyFile:            "__history__.html",
+    enabled:               true,
+    
+    _initialized:           false,
+    _listeners:             new ListenerQueue(),
+    
+    _actions:               [],
+    _baseIndex:             0,
+    _currentIndex:          0,
+    
+    _plainDocumentTitle:    document.title
+};
+
+SAHistory.formatHistoryEntryTitle = function(actionLabel) {
+    return SAHistory._plainDocumentTitle + " {" + actionLabel + "}";
+};
+
+SAHistory.initialize = function() {
+    if (SAHistory._initialized) {
+        return;
+    }
+    
+    if (SAHistory.enabled) {
+        var iframe = document.createElement("iframe");
+        iframe.id = "simile-ajax-history";
+        iframe.style.position = "absolute";
+        iframe.style.width = "10px";
+        iframe.style.height = "10px";
+        iframe.style.top = "0px";
+        iframe.style.left = "0px";
+        iframe.style.visibility = "hidden";
+        iframe.src = SAHistory.historyFile + "?0";
+        
+        document.body.appendChild(iframe);
+        DOM.registerEvent(iframe, "load", SAHistory._handleIFrameOnLoad);
+        
+        SAHistory._iframe = iframe;
+    }
+    SAHistory._initialized = true;
+};
+
+SAHistory.addListener = function(listener) {
+    SAHistory._listeners.add(listener);
+};
+
+SAHistory.removeListener = function(listener) {
+    SAHistory._listeners.remove(listener);
+};
+
+SAHistory.addAction = function(action) {
+    SAHistory._listeners.fire("onBeforePerform", [ action ]);
+    window.setTimeout(function() {
+        try {
+            action.perform();
+            SAHistory._listeners.fire("onAfterPerform", [ action ]);
+                
+            if (SAHistory.enabled) {
+                SAHistory._actions = SAHistory._actions.slice(
+                    0, SAHistory._currentIndex - SAHistory._baseIndex);
+                    
+                SAHistory._actions.push(action);
+                SAHistory._currentIndex++;
+                
+                var diff = SAHistory._actions.length - SAHistory.maxHistoryLength;
+                if (diff > 0) {
+                    SAHistory._actions = SAHistory._actions.slice(diff);
+                    SAHistory._baseIndex += diff;
+                }
+                
+                try {
+                    SAHistory._iframe.contentWindow.location.search = 
+                        "?" + SAHistory._currentIndex;
+                } catch (e) {
+                    /*
+                     *  We can't modify location.search most probably because it's a file:// url.
+                     *  We'll just going to modify the document's title.
+                     */
+                    var title = SAHistory.formatHistoryEntryTitle(action.label);
+                    document.title = title;
+                }
+            }
+        } catch (e) {
+            Debug.exception(e, "Error adding action {" + action.label + "} to history");
+        }
+    }, 0);
+};
+
+SAHistory.addLengthyAction = function(perform, undo, label) {
+    SAHistory.addAction({
+        perform:    perform,
+        undo:       undo,
+        label:      label,
+        uiLayer:    WindowManager.getBaseLayer(),
+        lengthy:    true
+    });
+};
+
+SAHistory._handleIFrameOnLoad = function() {
+    /*
+     *  This function is invoked when the user herself
+     *  navigates backward or forward. We need to adjust
+     *  the application's state accordingly.
+     */
+    
+    try {
+        var q = SAHistory._iframe.contentWindow.location.search;
+        var c = (q.length == 0) ? 0 : Math.max(0, parseInt(q.substr(1)));
+        
+        var finishUp = function() {
+            var diff = c - SAHistory._currentIndex;
+            SAHistory._currentIndex += diff;
+            SAHistory._baseIndex += diff;
+                
+            SAHistory._iframe.contentWindow.location.search = "?" + c;
+        };
+        
+        if (c < SAHistory._currentIndex) { // need to undo
+            SAHistory._listeners.fire("onBeforeUndoSeveral", []);
+            window.setTimeout(function() {
+                while (SAHistory._currentIndex > c && 
+                       SAHistory._currentIndex > SAHistory._baseIndex) {
+                       
+                    SAHistory._currentIndex--;
+                    
+                    var action = SAHistory._actions[SAHistory._currentIndex - SAHistory._baseIndex];
+                    
+                    try {
+                        action.undo();
+                    } catch (e) {
+                        Debug.exception(e, "History: Failed to undo action {" + action.label + "}");
+                    }
+                }
+                
+                SAHistory._listeners.fire("onAfterUndoSeveral", []);
+                finishUp();
+            }, 0);
+        } else if (c > SAHistory._currentIndex) { // need to redo
+            SAHistory._listeners.fire("onBeforeRedoSeveral", []);
+            window.setTimeout(function() {
+                while (SAHistory._currentIndex < c && 
+                       SAHistory._currentIndex - SAHistory._baseIndex < SAHistory._actions.length) {
+                       
+                    var action = SAHistory._actions[SAHistory._currentIndex - SAHistory._baseIndex];
+                    
+                    try {
+                        action.perform();
+                    } catch (e) {
+                        Debug.exception(e, "History: Failed to redo action {" + action.label + "}");
+                    }
+                    
+                    SAHistory._currentIndex++;
+                }
+                
+                SAHistory._listeners.fire("onAfterRedoSeveral", []);
+                finishUp();
+            }, 0);
+        } else {
+            var index = SAHistory._currentIndex - SAHistory._baseIndex - 1;
+            var title = (index >= 0 && index < SAHistory._actions.length) ?
+                SAHistory.formatHistoryEntryTitle(SAHistory._actions[index].label) :
+                SAHistory._plainDocumentTitle;
+                
+            SAHistory._iframe.contentWindow.document.title = title;
+            document.title = title;
+        }
+    } catch (e) {
+        // silent
+    }
+};
+
+SAHistory.getNextUndoAction = function() {
+    try {
+        var index = SAHistory._currentIndex - SAHistory._baseIndex - 1;
+        return SAHistory._actions[index];
+    } catch (e) {
+        return null;
+    }
+};
+
+SAHistory.getNextRedoAction = function() {
+    try {
+        var index = SAHistory._currentIndex - SAHistory._baseIndex;
+        return SAHistory._actions[index];
+    } catch (e) {
+        return null;
+    }
+};
+
+    return SAHistory;
+});
+
+/*==================================================
+ *  Simile Ajax API
+ *==================================================
+ */
+
+/*==================================================
+ *  REMEMBER to update the Version!  Now found in scripts/base.js
+ *==================================================
+ */
+
+define('simile-ajax',[
+    "module",
+    "./scripts/simile-ajax-base",
+    "./scripts/platform",
+    "./scripts/debug",
+    "./scripts/xmlhttp",
+    "./scripts/dom",
+    "./scripts/bubble",
+    "./scripts/date-time",
+    "./scripts/string",
+    "./scripts/html",
+    "./scripts/set",
+    "./scripts/sorted-array",
+    "./scripts/event-index",
+    "./scripts/units",
+    "./scripts/ajax",
+    "./scripts/history",
+    "./scripts/window-manager"
+], function(module, SimileAjax, Platform, Debug, XmlHttp, DOM, Graphics, DateTime, StringUtils, HTML, Set, SortedArray, EventIndex, NativeDateUnit, ListenerQueue, SAHistory, WindowManager) { 
+    SimileAjax.Platform = Platform;
+    SimileAjax.Debug = Debug;
+    SimileAjax.XmlHttp = XmlHttp;
+    SimileAjax.DOM = DOM;
+    SimileAjax.Graphics = Graphics;
+    SimileAjax.DateTime = DateTime;
+    SimileAjax.StringUtils = StringUtils;
+    SimileAjax.HTML = HTML;
+    SimileAjax.Set = Set;
+    SimileAjax.SortedArray = SortedArray;
+    SimileAjax.EventIndex = EventIndex;
+    SimileAjax.NativeDateUnit = NativeDateUnit;
+    SimileAjax.ListenerQueue = ListenerQueue;
+    SimileAjax.History = SAHistory;
+    SimileAjax.WindowManager = WindowManager;
+
+    var getHead = function(doc) {
+        return doc.getElementsByTagName("head")[0];
+    };
+    
+    SimileAjax.findScript = function(doc, substring) {
+        var scripts, s, url, i;
+	    scripts = doc.documentElement.getElementsByTagName("script");
+	    for (s = 0; s < scripts.length; s++) {
+            url = scripts[s].src;
+            i = url.indexOf(substring);
+            if (i >= 0) {
+                return url;
+            }
+	    }
+        return null;
+    };
+
+    /**
+     * Parse out the query parameters from a URL
+     * @param {String} url    the url to parse, or location.href if undefined
+     * @param {Object} to     optional object to extend with the parameters
+     * @param {Object} types  optional object mapping keys to value types
+     *        (String, Number, Boolean or Array, String by default)
+     * @return a key/value Object whose keys are the query parameter names
+     * @type Object
+     */
+    SimileAjax.parseURLParameters = function(url, to, types) {
+        to = to || {};
+        types = types || {};
+        
+        if (typeof url == "undefined") {
+            url = location.href;
+        }
+        var q = url.indexOf("?");
+        if (q < 0) {
+            return to;
+        }
+        url = (url+"#").slice(q+1, url.indexOf("#")); // toss the URL fragment
+        
+        var params = url.split("&"), param, parsed = {};
+        var decode = window.decodeURIComponent || unescape;
+        for (var i = 0; param = params[i]; i++) {
+            var eq = param.indexOf("=");
+            var name = decode(param.slice(0,eq));
+            var old = parsed[name];
+            var replacement = decode(param.slice(eq+1));
+            
+            if (typeof old == "undefined") {
+                old = [];
+            } else if (!(old instanceof Array)) {
+                old = [old];
+            }
+            parsed[name] = old.concat(replacement);
+        }
+        for (var i in parsed) {
+            if (!parsed.hasOwnProperty(i)) continue;
+            var type = types[i] || String;
+            var data = parsed[i];
+            if (!(data instanceof Array)) {
+                data = [data];
+            }
+            if (type === Boolean && data[0] == "false") {
+                to[i] = false; // because Boolean("false") === true
+            } else {
+                to[i] = type.apply(this, data);
+            }
+        }
+        return to;
+    };
+
+    /**
+     * @deprecated Use RequireJS loading mechanisms instead.
+     */
+    SimileAjax.includeJavascriptFile = function(doc, url, onerror, charset, callback) {
+        SimileAjax.Debug.warn("Loading scripts is no longer a feature of SimileAjax. Use RequireJS instead.");
+        return;
+    };
+
+    /**
+     * @deprecated Use RequireJS loading mechanisms instead.
+     */
+    SimileAjax.includeJavascriptFiles = function(doc, urlPrefix, filenames) {
+        SimileAjax.Debug.warn("Loading scripts is no longer a feature of SimileAjax. Use RequireJS instead.");
+        return;
+    };
+
+    SimileAjax.includeCssFile = function(doc, url) {
+        if (doc.body == null) {
+            try {
+                doc.write("<link rel='stylesheet' href='" + url + "' type='text/css'/>");
+                return;
+            } catch (e) {
+                // fall through
+            }
+        }
+        
+        var link = doc.createElement("link");
+        link.setAttribute("rel", "stylesheet");
+        link.setAttribute("type", "text/css");
+        link.setAttribute("href", url);
+        getHead(doc).appendChild(link);
+    };
+
+    SimileAjax.includeCssFiles = function(doc, urlPrefix, filenames) {
+        for (var i = 0; i < filenames.length; i++) {
+            SimileAjax.includeCssFile(doc, urlPrefix + filenames[i]);
+        }
+    };
+    
+    /**
+     * Append into urls each string in suffixes after prefixing it with urlPrefix.
+     * @param {Array} urls
+     * @param {String} urlPrefix
+     * @param {Array} suffixes
+     */
+    SimileAjax.prefixURLs = function(urls, urlPrefix, suffixes) {
+        for (var i = 0; i < suffixes.length; i++) {
+            urls.push(urlPrefix + suffixes[i]);
+        }
+    };
+
+    /**
+     * A call to set the prefix and load CSS.
+     * @param {String} prefix
+     * @returns {Boolean}
+     */
+    SimileAjax.setPrefix = function(prefix) {
+        if (SimileAjax.urlPrefix === null && prefix !== null) {
+            if (prefix.substr(-1) !== "/") {
+                prefix += "/";
+            }
+            SimileAjax.urlPrefix = prefix;
+            SimileAjax.params.prefix = prefix;
+            SimileAjax.loadCSS(SimileAjax.params.bundle);
+            return true;
+        };
+        return false;
+    };
+
+    /**
+     * Private call to load CSS.
+     * @prefix {Boolean} bundle Whether to load bundled CSS or individual.
+     */
+    SimileAjax.loadCSS = function(bundle) {
+        var cssFiles = ["main.css"], bundledCssFile = "simile-ajax-bundle.css";
+        bundle = bundle || true;
+
+        if (bundle) {
+            SimileAjax.includeCssFile(document, SimileAjax.urlPrefix + "styles/" + bundledCssFile);
+        } else {
+            SimileAjax.includeCssFiles(document, SimileAjax.urlPrefix + "styles/", cssFiles);
+        }
+    };
+
+    /**
+     * Deal with legacy methods of passing configuration to SimileAjax.
+     */
+    SimileAjax.loadLegacy = function() {
+        var prefix, url, targets, target, i;
+ 
+        prefix = null;
+        if (typeof SimileAjax_urlPrefix == "string") {
+            prefix = SimileAjax_urlPrefix;
+            SimileAjax.setPrefix(prefix);
+        } else {
+            url = null;
+            targets = ["simile-ajax-api.js", "simile-ajax-bundle.js"];
+            for (i = 0; i < targets.length; i++) {
+                target = targets[i];
+                url = SimileAjax.findScript(document, target);
+                if (url != null) {
+                    prefix = url.substr(0, url.indexOf(target));
+                    break;
+                }
+            }
+            
+            if (url === null) {
+                SimileAjax.error = new Error("Failed to derive URL prefix for SimileAjax");
+            } else {
+                SimileAjax.setPrefix(prefix);
+                SimileAjax.params = SimileAjax.parseURLParameters(url, SimileAjax.params, SimileAjax.paramTypes);
+            } 
+        }
+ 
+        SimileAjax.Graphics = Graphics.initialize(Graphics);
+        SimileAjax.History.initialize();
+        SimileAjax.WindowManager.initialize();
+    };
+
+    /**
+     * Load based on RequireJS configuration.
+     */
+    SimileAjax.loadRequire = function() {
+        var conf;
+
+        conf = module.config();
+        SimileAjax.params = conf;
+        SimileAjax.setPrefix(SimileAjax.params.prefix);
+
+        SimileAjax.Graphics = Graphics.initialize(Graphics);
+        SimileAjax.History.initialize();
+        SimileAjax.WindowManager.initialize();
+    };
+
+    SimileAjax.load = function() {
+        if (SimileAjax.loaded) {
+            return;
+        } else {
+            SimileAjax.loaded = true;
+            if (module.config().hasOwnProperty("prefix")) {
+                SimileAjax.loadRequire();
+            } else {
+                SimileAjax.loadLegacy();
+            }
+        }
+    };
+
+    return SimileAjax;
+});
+    return require("simile-ajax");
+}));
diff --git a/assets/scripts/api.simile-widgets.org/ajax/2.2.1.fake/simile-ajax-api.js b/assets/scripts/api.simile-widgets.org/ajax/2.2.1.fake/simile-ajax-api.js
new file mode 100644
index 0000000..017a5ba
--- /dev/null
+++ b/assets/scripts/api.simile-widgets.org/ajax/2.2.1.fake/simile-ajax-api.js
@@ -0,0 +1,7 @@
+/**
+ * almond 0.2.5 Copyright (c) 2011-2012, The Dojo Foundation All Rights Reserved.
+ * Available via the MIT or new BSD license.
+ * see: http://github.com/jrburke/almond for details
+ */
+
+(function(e,t){typeof define=="function"&&define.amd?define(t):e.SimileAjax=t()})(this,function(){var e,t,r;return function(n){function d(e,t){return h.call(e,t)}function v(e,t){var n,r,i,s,o,u,a,f,c,h,p=t&&t.split("/"),d=l.map,v=d&&d["*"]||{};if(e&&e.charAt(0)===".")if(t){p=p.slice(0,p.length-1),e=p.concat(e.split("/"));for(f=0;f<e.length;f+=1){h=e[f];if(h===".")e.splice(f,1),f-=1;else if(h===".."){if(f===1&&(e[2]===".."||e[0]===".."))break;f>0&&(e.splice(f-1,2),f-=2)}}e=e.join("/")}els [...]
\ No newline at end of file
diff --git a/assets/scripts/api.simile-widgets.org/ajax/2.2.1.fake/simile-ajax-bundle.css b/assets/scripts/api.simile-widgets.org/ajax/2.2.1.fake/simile-ajax-bundle.css
new file mode 100644
index 0000000..ea92a80
--- /dev/null
+++ b/assets/scripts/api.simile-widgets.org/ajax/2.2.1.fake/simile-ajax-bundle.css
@@ -0,0 +1,181 @@
+div.simileAjax-bubble-container {
+    margin:     0px;
+    padding:    0px;
+    border:     none;
+    position:   absolute;
+    z-index:    1000;
+}
+div.simileAjax-bubble-innerContainer {
+    margin:     0px;
+    padding:    0px;
+    border:     none;
+    position:   relative;
+    width:      100%;
+    height:     100%;
+    overflow:   visible;
+}
+div.simileAjax-bubble-contentContainer {
+    margin:     0px;
+    padding:    0px;
+    border:     none;
+    position:   absolute;
+    left:       0px;
+    top:        0px;
+    width:      100%;
+    height:     100%;
+    overflow:   auto;
+    background: white;
+}
+div.simileAjax-bubble-border-left {
+    position:   absolute;
+    left:       -50px;
+    top:        0px;
+    width:      50px;
+    height:     100%;
+}
+div.simileAjax-bubble-border-left-pngTranslucent {
+    background: url(../images/bubble-left.png) top right repeat-y;
+}
+div.simileAjax-bubble-border-right {
+    position:   absolute;
+    right:      -50px;
+    top:        0px;
+    width:      50px;
+    height:     100%;
+}
+.simileAjax-bubble-border-right-pngTranslucent {
+    background: url(../images/bubble-right.png) top left repeat-y;
+}
+div.simileAjax-bubble-border-top {
+    position:   absolute;
+    top:        -50px;
+    left:       0px;
+    width:      100%;
+    height:     50px;
+}
+.simileAjax-bubble-border-top-pngTranslucent {
+    background: url(../images/bubble-top.png) bottom left repeat-x;
+}
+div.simileAjax-bubble-border-bottom {
+    position:   absolute;
+    bottom:     -50px;
+    left:       0px;
+    width:      100%;
+    height:     50px;
+}
+.simileAjax-bubble-border-bottom-pngTranslucent {
+    background: url(../images/bubble-bottom.png) top left repeat-x;
+}
+div.simileAjax-bubble-border-top-left {
+    position:   absolute;
+    top:        -50px;
+    left:       -50px;
+    width:      50px;
+    height:     50px;
+}
+.simileAjax-bubble-border-top-left-pngTranslucent {
+    background: url(../images/bubble-top-left.png) bottom right no-repeat;
+}
+div.simileAjax-bubble-border-top-right {
+    position:   absolute;
+    top:        -50px;
+    right:      -50px;
+    width:      50px;
+    height:     50px;
+}
+.simileAjax-bubble-border-top-right-pngTranslucent {
+    background: url(../images/bubble-top-right.png) bottom left no-repeat;
+}
+div.simileAjax-bubble-border-bottom-left {
+    position:   absolute;
+    bottom:     -50px;
+    left:       -50px;
+    width:      50px;
+    height:     50px;
+}
+.simileAjax-bubble-border-bottom-left-pngTranslucent {
+    background: url(../images/bubble-bottom-left.png) top right no-repeat;
+}
+div.simileAjax-bubble-border-bottom-right {
+    position:   absolute;
+    bottom:     -50px;
+    right:      -50px;
+    width:      50px;
+    height:     50px;
+}
+.simileAjax-bubble-border-bottom-right-pngTranslucent {
+    background: url(../images/bubble-bottom-right.png) top left no-repeat;
+}
+div.simileAjax-bubble-arrow-point-left {
+    position:   absolute;
+    left:       -100px;
+    width:      100px;
+    height:     49px;
+}
+.simileAjax-bubble-arrow-point-left-pngTranslucent {
+    background: url(../images/bubble-arrow-point-left.png) center right no-repeat;
+}
+div.simileAjax-bubble-arrow-point-right {
+    position:   absolute;
+    right:      -100px;
+    width:      100px;
+    height:     49px;
+}
+.simileAjax-bubble-arrow-point-right-pngTranslucent {
+    background: url(../images/bubble-arrow-point-right.png) center left no-repeat;
+}
+div.simileAjax-bubble-arrow-point-up {
+    position:   absolute;
+    top:        -100px;
+    width:      49px;
+    height:     100px;
+}
+.simileAjax-bubble-arrow-point-up-pngTranslucent {
+    background: url(../images/bubble-arrow-point-up.png) bottom center no-repeat;
+}
+div.simileAjax-bubble-arrow-point-down {
+    position:   absolute;
+    bottom:     -100px;
+    width:      49px;
+    height:     100px;
+}
+.simileAjax-bubble-arrow-point-down-pngTranslucent {
+    background: url(../images/bubble-arrow-point-down.png) bottom center no-repeat;
+}
+div.simileAjax-bubble-close {
+    position:   absolute;
+    right:      -10px;
+    top:        -12px;
+    width:      16px;
+    height:     16px;
+    cursor:     pointer;
+}
+.simileAjax-bubble-close-pngTranslucent {
+    background: url(../images/close-button.png) no-repeat;
+}
+div.simileAjax-messageBubble-top {
+    height: 33px;
+    padding-left: 44px;
+    background: url(../images/message-top-left.png) top left no-repeat;
+}
+div.simileAjax-messageBubble-top-right {
+    height: 33px;
+    background: url(../images/message-top-right.png) top right no-repeat;
+}
+div.simileAjax-messageBubble-middle {
+    padding-left: 44px;
+    background: url(../images/message-left.png) top left repeat-y;
+}
+div.simileAjax-messageBubble-middle-right {
+    padding-right: 44px;
+    background: url(../images/message-right.png) top right repeat-y;
+}
+div.simileAjax-messageBubble-bottom {
+    height: 55px;
+    padding-left: 44px;
+    background: url(../images/message-bottom-left.png) bottom left no-repeat;
+}
+div.simileAjax-messageBubble-bottom-right {
+    height: 55px;
+    background: url(../images/message-bottom-right.png) bottom right no-repeat;
+}
diff --git a/assets/scripts/api.simile-widgets.org/ajax/2.2.1.fake/simile-ajax-bundle.js b/assets/scripts/api.simile-widgets.org/ajax/2.2.1.fake/simile-ajax-bundle.js
new file mode 100644
index 0000000..f67d5db
--- /dev/null
+++ b/assets/scripts/api.simile-widgets.org/ajax/2.2.1.fake/simile-ajax-bundle.js
@@ -0,0 +1 @@
+define("scripts/simile-ajax-base",[],function(){var e={loaded:!1,loadingScriptsCount:0,error:null,params:{bundle:!0},paramTypes:{bundle:Boolean},version:"3.0.0",jQuery:null,urlPrefix:null};return e}),define("scripts/platform",[],function(){var e={};return e.os={isMac:!1,isWin:!1,isWin32:!1,isUnix:!1},e.browser={isIE:!1,isNetscape:!1,isMozilla:!1,isFirefox:!1,isOpera:!1,isSafari:!1,majorVersion:0,minorVersion:0},function(){var t=navigator.appName.toLowerCase(),n=navigator.userAgent.toLowe [...]
\ No newline at end of file
diff --git a/assets/scripts/api.simile-widgets.org/ajax/2.2.1/content/history.html b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/content/history.html
new file mode 100644
index 0000000..a30fbd8
--- /dev/null
+++ b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/content/history.html
@@ -0,0 +1,7 @@
+<html>
+<head>
+    <title>Dummy Page for Keeping Track of History</title>
+</head>
+<body>
+</body>
+</html>
\ No newline at end of file
diff --git a/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/bubble-arrow-point-down.png b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/bubble-arrow-point-down.png
new file mode 100644
index 0000000..b87e87d
Binary files /dev/null and b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/bubble-arrow-point-down.png differ
diff --git a/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/bubble-arrow-point-left.png b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/bubble-arrow-point-left.png
new file mode 100644
index 0000000..192d7f6
Binary files /dev/null and b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/bubble-arrow-point-left.png differ
diff --git a/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/bubble-arrow-point-right.png b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/bubble-arrow-point-right.png
new file mode 100644
index 0000000..d09fdc7
Binary files /dev/null and b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/bubble-arrow-point-right.png differ
diff --git a/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/bubble-arrow-point-up.png b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/bubble-arrow-point-up.png
new file mode 100644
index 0000000..7036080
Binary files /dev/null and b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/bubble-arrow-point-up.png differ
diff --git a/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/bubble-bottom-left.png b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/bubble-bottom-left.png
new file mode 100644
index 0000000..117c29c
Binary files /dev/null and b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/bubble-bottom-left.png differ
diff --git a/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/bubble-bottom-right.png b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/bubble-bottom-right.png
new file mode 100644
index 0000000..fa88d28
Binary files /dev/null and b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/bubble-bottom-right.png differ
diff --git a/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/bubble-bottom.png b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/bubble-bottom.png
new file mode 100644
index 0000000..9958c3f
Binary files /dev/null and b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/bubble-bottom.png differ
diff --git a/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/bubble-left.png b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/bubble-left.png
new file mode 100644
index 0000000..851d2d7
Binary files /dev/null and b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/bubble-left.png differ
diff --git a/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/bubble-right.png b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/bubble-right.png
new file mode 100644
index 0000000..7c74abc
Binary files /dev/null and b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/bubble-right.png differ
diff --git a/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/bubble-top-left.png b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/bubble-top-left.png
new file mode 100644
index 0000000..00c0773
Binary files /dev/null and b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/bubble-top-left.png differ
diff --git a/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/bubble-top-right.png b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/bubble-top-right.png
new file mode 100644
index 0000000..e553363
Binary files /dev/null and b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/bubble-top-right.png differ
diff --git a/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/bubble-top.png b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/bubble-top.png
new file mode 100644
index 0000000..ddb6784
Binary files /dev/null and b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/bubble-top.png differ
diff --git a/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/close-button.png b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/close-button.png
new file mode 100644
index 0000000..15f31b3
Binary files /dev/null and b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/close-button.png differ
diff --git a/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/copy.png b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/copy.png
new file mode 100644
index 0000000..cf7cee4
Binary files /dev/null and b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/copy.png differ
diff --git a/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/message-bottom-left.png b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/message-bottom-left.png
new file mode 100644
index 0000000..43a9d61
Binary files /dev/null and b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/message-bottom-left.png differ
diff --git a/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/message-bottom-right.png b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/message-bottom-right.png
new file mode 100644
index 0000000..bfa4954
Binary files /dev/null and b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/message-bottom-right.png differ
diff --git a/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/message-left.png b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/message-left.png
new file mode 100644
index 0000000..f354376
Binary files /dev/null and b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/message-left.png differ
diff --git a/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/message-right.png b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/message-right.png
new file mode 100644
index 0000000..4702c28
Binary files /dev/null and b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/message-right.png differ
diff --git a/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/message-top-left.png b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/message-top-left.png
new file mode 100644
index 0000000..b19b0ea
Binary files /dev/null and b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/message-top-left.png differ
diff --git a/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/message-top-right.png b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/message-top-right.png
new file mode 100644
index 0000000..c092555
Binary files /dev/null and b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/images/message-top-right.png differ
diff --git a/assets/scripts/api.simile-widgets.org/ajax/2.2.1/scripts/ajax.js b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/scripts/ajax.js
new file mode 100644
index 0000000..7172609
--- /dev/null
+++ b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/scripts/ajax.js
@@ -0,0 +1,45 @@
+/*==================================================
+ *  General, miscellaneous SimileAjax stuff
+ *==================================================
+ */
+
+SimileAjax.ListenerQueue = function(wildcardHandlerName) {
+    this._listeners = [];
+    this._wildcardHandlerName = wildcardHandlerName;
+};
+
+SimileAjax.ListenerQueue.prototype.add = function(listener) {
+    this._listeners.push(listener);
+};
+
+SimileAjax.ListenerQueue.prototype.remove = function(listener) {
+    var listeners = this._listeners;
+    for (var i = 0; i < listeners.length; i++) {
+        if (listeners[i] == listener) {
+            listeners.splice(i, 1);
+            break;
+        }
+    }
+};
+
+SimileAjax.ListenerQueue.prototype.fire = function(handlerName, args) {
+    var listeners = [].concat(this._listeners);
+    for (var i = 0; i < listeners.length; i++) {
+        var listener = listeners[i];
+        if (handlerName in listener) {
+            try {
+                listener[handlerName].apply(listener, args);
+            } catch (e) {
+                SimileAjax.Debug.exception("Error firing event of name " + handlerName, e);
+            }
+        } else if (this._wildcardHandlerName != null &&
+            this._wildcardHandlerName in listener) {
+            try {
+                listener[this._wildcardHandlerName].apply(listener, [ handlerName ]);
+            } catch (e) {
+                SimileAjax.Debug.exception("Error firing event of name " + handlerName + " to wildcard handler", e);
+            }
+        }
+    }
+};
+
diff --git a/assets/scripts/api.simile-widgets.org/ajax/2.2.1/scripts/data-structure.js b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/scripts/data-structure.js
new file mode 100644
index 0000000..e789cb4
--- /dev/null
+++ b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/scripts/data-structure.js
@@ -0,0 +1,447 @@
+/**
+ * A basic set (in the mathematical sense) data structure
+ *
+ * @constructor
+ * @param {Array or SimileAjax.Set} [a] an initial collection
+ */
+SimileAjax.Set = function(a) {
+    this._hash = {};
+    this._count = 0;
+    
+    if (a instanceof Array) {
+        for (var i = 0; i < a.length; i++) {
+            this.add(a[i]);
+        }
+    } else if (a instanceof SimileAjax.Set) {
+        this.addSet(a);
+    }
+}
+
+/**
+ * Adds the given object to this set, assuming there it does not already exist
+ *
+ * @param {Object} o the object to add
+ * @return {Boolean} true if the object was added, false if not
+ */
+SimileAjax.Set.prototype.add = function(o) {
+    if (!(o in this._hash)) {
+        this._hash[o] = true;
+        this._count++;
+        return true;
+    }
+    return false;
+}
+
+/**
+ * Adds each element in the given set to this set
+ *
+ * @param {SimileAjax.Set} set the set of elements to add
+ */
+SimileAjax.Set.prototype.addSet = function(set) {
+    for (var o in set._hash) {
+        this.add(o);
+    }
+}
+
+/**
+ * Removes the given element from this set
+ *
+ * @param {Object} o the object to remove
+ * @return {Boolean} true if the object was successfully removed,
+ *   false otherwise
+ */
+SimileAjax.Set.prototype.remove = function(o) {
+    if (o in this._hash) {
+        delete this._hash[o];
+        this._count--;
+        return true;
+    }
+    return false;
+}
+
+/**
+ * Removes the elements in this set that correspond to the elements in the
+ * given set
+ *
+ * @param {SimileAjax.Set} set the set of elements to remove
+ */
+SimileAjax.Set.prototype.removeSet = function(set) {
+    for (var o in set._hash) {
+        this.remove(o);
+    }
+}
+
+/**
+ * Removes all elements in this set that are not present in the given set, i.e.
+ * modifies this set to the intersection of the two sets
+ *
+ * @param {SimileAjax.Set} set the set to intersect
+ */
+SimileAjax.Set.prototype.retainSet = function(set) {
+    for (var o in this._hash) {
+        if (!set.contains(o)) {
+            delete this._hash[o];
+            this._count--;
+        }
+    }
+}
+
+/**
+ * Returns whether or not the given element exists in this set
+ *
+ * @param {SimileAjax.Set} o the object to test for
+ * @return {Boolean} true if the object is present, false otherwise
+ */
+SimileAjax.Set.prototype.contains = function(o) {
+    return (o in this._hash);
+}
+
+/**
+ * Returns the number of elements in this set
+ *
+ * @return {Number} the number of elements in this set
+ */
+SimileAjax.Set.prototype.size = function() {
+    return this._count;
+}
+
+/**
+ * Returns the elements of this set as an array
+ *
+ * @return {Array} a new array containing the elements of this set
+ */
+SimileAjax.Set.prototype.toArray = function() {
+    var a = [];
+    for (var o in this._hash) {
+        a.push(o);
+    }
+    return a;
+}
+
+/**
+ * Iterates through the elements of this set, order unspecified, executing the
+ * given function on each element until the function returns true
+ *
+ * @param {Function} f a function of form f(element)
+ */
+SimileAjax.Set.prototype.visit = function(f) {
+    for (var o in this._hash) {
+        if (f(o) == true) {
+            break;
+        }
+    }
+}
+
+/**
+ * A sorted array data structure
+ *
+ * @constructor
+ */
+SimileAjax.SortedArray = function(compare, initialArray) {
+    this._a = (initialArray instanceof Array) ? initialArray : [];
+    this._compare = compare;
+};
+
+SimileAjax.SortedArray.prototype.add = function(elmt) {
+    var sa = this;
+    var index = this.find(function(elmt2) {
+        return sa._compare(elmt2, elmt);
+    });
+    
+    if (index < this._a.length) {
+        this._a.splice(index, 0, elmt);
+    } else {
+        this._a.push(elmt);
+    }
+};
+
+SimileAjax.SortedArray.prototype.remove = function(elmt) {
+    var sa = this;
+    var index = this.find(function(elmt2) {
+        return sa._compare(elmt2, elmt);
+    });
+    
+    while (index < this._a.length && this._compare(this._a[index], elmt) == 0) {
+        if (this._a[index] == elmt) {
+            this._a.splice(index, 1);
+            return true;
+        } else {
+            index++;
+        }
+    }
+    return false;
+};
+
+SimileAjax.SortedArray.prototype.removeAll = function() {
+    this._a = [];
+};
+
+SimileAjax.SortedArray.prototype.elementAt = function(index) {
+    return this._a[index];
+};
+
+SimileAjax.SortedArray.prototype.length = function() {
+    return this._a.length;
+};
+
+SimileAjax.SortedArray.prototype.find = function(compare) {
+    var a = 0;
+    var b = this._a.length;
+    
+    while (a < b) {
+        var mid = Math.floor((a + b) / 2);
+        var c = compare(this._a[mid]);
+        if (mid == a) {
+            return c < 0 ? a+1 : a;
+        } else if (c < 0) {
+            a = mid;
+        } else {
+            b = mid;
+        }
+    }
+    return a;
+};
+
+SimileAjax.SortedArray.prototype.getFirst = function() {
+    return (this._a.length > 0) ? this._a[0] : null;
+};
+
+SimileAjax.SortedArray.prototype.getLast = function() {
+    return (this._a.length > 0) ? this._a[this._a.length - 1] : null;
+};
+
+/*==================================================
+ *  Event Index
+ *==================================================
+ */
+
+SimileAjax.EventIndex = function(unit) {
+    var eventIndex = this;
+    
+    this._unit = (unit != null) ? unit : SimileAjax.NativeDateUnit;
+    this._events = new SimileAjax.SortedArray(
+        function(event1, event2) {
+            return eventIndex._unit.compare(event1.getStart(), event2.getStart());
+        }
+    );
+    this._idToEvent = {};
+    this._indexed = true;
+};
+
+SimileAjax.EventIndex.prototype.getUnit = function() {
+    return this._unit;
+};
+
+SimileAjax.EventIndex.prototype.getEvent = function(id) {
+    return this._idToEvent[id];
+};
+
+SimileAjax.EventIndex.prototype.add = function(evt) {
+    this._events.add(evt);
+    this._idToEvent[evt.getID()] = evt;
+    this._indexed = false;
+};
+
+SimileAjax.EventIndex.prototype.removeAll = function() {
+    this._events.removeAll();
+    this._idToEvent = {};
+    this._indexed = false;
+};
+
+SimileAjax.EventIndex.prototype.getCount = function() {
+    return this._events.length();
+};
+
+SimileAjax.EventIndex.prototype.getIterator = function(startDate, endDate) {
+    if (!this._indexed) {
+        this._index();
+    }
+    return new SimileAjax.EventIndex._Iterator(this._events, startDate, endDate, this._unit);
+};
+
+SimileAjax.EventIndex.prototype.getReverseIterator = function(startDate, endDate) {
+    if (!this._indexed) {
+        this._index();
+    }
+    return new SimileAjax.EventIndex._ReverseIterator(this._events, startDate, endDate, this._unit);
+};
+
+SimileAjax.EventIndex.prototype.getAllIterator = function() {
+    return new SimileAjax.EventIndex._AllIterator(this._events);
+};
+
+SimileAjax.EventIndex.prototype.getEarliestDate = function() {
+    var evt = this._events.getFirst();
+    return (evt == null) ? null : evt.getStart();
+};
+
+SimileAjax.EventIndex.prototype.getLatestDate = function() {
+    var evt = this._events.getLast();
+    if (evt == null) {
+        return null;
+    }
+    
+    if (!this._indexed) {
+        this._index();
+    }
+    
+    var index = evt._earliestOverlapIndex;
+    var date = this._events.elementAt(index).getEnd();
+    for (var i = index + 1; i < this._events.length(); i++) {
+        date = this._unit.later(date, this._events.elementAt(i).getEnd());
+    }
+    
+    return date;
+};
+
+SimileAjax.EventIndex.prototype._index = function() {
+    /*
+     *  For each event, we want to find the earliest preceding
+     *  event that overlaps with it, if any.
+     */
+    
+    var l = this._events.length();
+    for (var i = 0; i < l; i++) {
+        var evt = this._events.elementAt(i);
+        evt._earliestOverlapIndex = i;
+    }
+    
+    var toIndex = 1;
+    for (var i = 0; i < l; i++) {
+        var evt = this._events.elementAt(i);
+        var end = evt.getEnd();
+        
+        toIndex = Math.max(toIndex, i + 1);
+        while (toIndex < l) {
+            var evt2 = this._events.elementAt(toIndex);
+            var start2 = evt2.getStart();
+            
+            if (this._unit.compare(start2, end) < 0) {
+                evt2._earliestOverlapIndex = i;
+                toIndex++;
+            } else {
+                break;
+            }
+        }
+    }
+    this._indexed = true;
+};
+
+SimileAjax.EventIndex._Iterator = function(events, startDate, endDate, unit) {
+    this._events = events;
+    this._startDate = startDate;
+    this._endDate = endDate;
+    this._unit = unit;
+    
+    this._currentIndex = events.find(function(evt) {
+        return unit.compare(evt.getStart(), startDate);
+    });
+    if (this._currentIndex - 1 >= 0) {
+        this._currentIndex = this._events.elementAt(this._currentIndex - 1)._earliestOverlapIndex;
+    }
+    this._currentIndex--;
+    
+    this._maxIndex = events.find(function(evt) {
+        return unit.compare(evt.getStart(), endDate);
+    });
+    
+    this._hasNext = false;
+    this._next = null;
+    this._findNext();
+};
+
+SimileAjax.EventIndex._Iterator.prototype = {
+    hasNext: function() { return this._hasNext; },
+    next: function() {
+        if (this._hasNext) {
+            var next = this._next;
+            this._findNext();
+            
+            return next;
+        } else {
+            return null;
+        }
+    },
+    _findNext: function() {
+        var unit = this._unit;
+        while ((++this._currentIndex) < this._maxIndex) {
+            var evt = this._events.elementAt(this._currentIndex);
+            if (unit.compare(evt.getStart(), this._endDate) < 0 &&
+                unit.compare(evt.getEnd(), this._startDate) > 0) {
+                
+                this._next = evt;
+                this._hasNext = true;
+                return;
+            }
+        }
+        this._next = null;
+        this._hasNext = false;
+    }
+};
+
+SimileAjax.EventIndex._ReverseIterator = function(events, startDate, endDate, unit) {
+    this._events = events;
+    this._startDate = startDate;
+    this._endDate = endDate;
+    this._unit = unit;
+    
+    this._minIndex = events.find(function(evt) {
+        return unit.compare(evt.getStart(), startDate);
+    });
+    if (this._minIndex - 1 >= 0) {
+        this._minIndex = this._events.elementAt(this._minIndex - 1)._earliestOverlapIndex;
+    }
+    
+    this._maxIndex = events.find(function(evt) {
+        return unit.compare(evt.getStart(), endDate);
+    });
+    
+    this._currentIndex = this._maxIndex;
+    this._hasNext = false;
+    this._next = null;
+    this._findNext();
+};
+
+SimileAjax.EventIndex._ReverseIterator.prototype = {
+    hasNext: function() { return this._hasNext; },
+    next: function() {
+        if (this._hasNext) {
+            var next = this._next;
+            this._findNext();
+            
+            return next;
+        } else {
+            return null;
+        }
+    },
+    _findNext: function() {
+        var unit = this._unit;
+        while ((--this._currentIndex) >= this._minIndex) {
+            var evt = this._events.elementAt(this._currentIndex);
+            if (unit.compare(evt.getStart(), this._endDate) < 0 &&
+                unit.compare(evt.getEnd(), this._startDate) > 0) {
+                
+                this._next = evt;
+                this._hasNext = true;
+                return;
+            }
+        }
+        this._next = null;
+        this._hasNext = false;
+    }
+};
+
+SimileAjax.EventIndex._AllIterator = function(events) {
+    this._events = events;
+    this._index = 0;
+};
+
+SimileAjax.EventIndex._AllIterator.prototype = {
+    hasNext: function() {
+        return this._index < this._events.length();
+    },
+    next: function() {
+        return this._index < this._events.length() ?
+            this._events.elementAt(this._index++) : null;
+    }
+};
\ No newline at end of file
diff --git a/assets/scripts/api.simile-widgets.org/ajax/2.2.1/scripts/date-time.js b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/scripts/date-time.js
new file mode 100644
index 0000000..d0498bf
--- /dev/null
+++ b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/scripts/date-time.js
@@ -0,0 +1,452 @@
+/**
+ * @fileOverview A collection of date/time utility functions
+ * @name SimileAjax.DateTime
+ */
+
+SimileAjax.DateTime = new Object();
+
+SimileAjax.DateTime.MILLISECOND    = 0;
+SimileAjax.DateTime.SECOND         = 1;
+SimileAjax.DateTime.MINUTE         = 2;
+SimileAjax.DateTime.HOUR           = 3;
+SimileAjax.DateTime.DAY            = 4;
+SimileAjax.DateTime.WEEK           = 5;
+SimileAjax.DateTime.MONTH          = 6;
+SimileAjax.DateTime.YEAR           = 7;
+SimileAjax.DateTime.DECADE         = 8;
+SimileAjax.DateTime.CENTURY        = 9;
+SimileAjax.DateTime.MILLENNIUM     = 10;
+
+SimileAjax.DateTime.EPOCH          = -1;
+SimileAjax.DateTime.ERA            = -2;
+
+/**
+ * An array of unit lengths, expressed in milliseconds, of various lengths of
+ * time.  The array indices are predefined and stored as properties of the
+ * SimileAjax.DateTime object, e.g. SimileAjax.DateTime.YEAR.
+ * @type Array
+ */
+SimileAjax.DateTime.gregorianUnitLengths = [];
+    (function() {
+        var d = SimileAjax.DateTime;
+        var a = d.gregorianUnitLengths;
+        
+        a[d.MILLISECOND] = 1;
+        a[d.SECOND]      = 1000;
+        a[d.MINUTE]      = a[d.SECOND] * 60;
+        a[d.HOUR]        = a[d.MINUTE] * 60;
+        a[d.DAY]         = a[d.HOUR] * 24;
+        a[d.WEEK]        = a[d.DAY] * 7;
+        a[d.MONTH]       = a[d.DAY] * 31;
+        a[d.YEAR]        = a[d.DAY] * 365;
+        a[d.DECADE]      = a[d.YEAR] * 10;
+        a[d.CENTURY]     = a[d.YEAR] * 100;
+        a[d.MILLENNIUM]  = a[d.YEAR] * 1000;
+    })();
+    
+SimileAjax.DateTime._dateRegexp = new RegExp(
+    "^(-?)([0-9]{4})(" + [
+        "(-?([0-9]{2})(-?([0-9]{2}))?)", // -month-dayOfMonth
+        "(-?([0-9]{3}))",                // -dayOfYear
+        "(-?W([0-9]{2})(-?([1-7]))?)"    // -Wweek-dayOfWeek
+    ].join("|") + ")?$"
+);
+SimileAjax.DateTime._timezoneRegexp = new RegExp(
+    "Z|(([-+])([0-9]{2})(:?([0-9]{2}))?)$"
+);
+SimileAjax.DateTime._timeRegexp = new RegExp(
+    "^([0-9]{2})(:?([0-9]{2})(:?([0-9]{2})(\.([0-9]+))?)?)?$"
+);
+
+/**
+ * Takes a date object and a string containing an ISO 8601 date and sets the
+ * the date using information parsed from the string.  Note that this method
+ * does not parse any time information.
+ *
+ * @param {Date} dateObject the date object to modify
+ * @param {String} string an ISO 8601 string to parse
+ * @return {Date} the modified date object
+ */
+SimileAjax.DateTime.setIso8601Date = function(dateObject, string) {
+    /*
+     *  This function has been adapted from dojo.date, v.0.3.0
+     *  http://dojotoolkit.org/.
+     */
+     
+    var d = string.match(SimileAjax.DateTime._dateRegexp);
+    if(!d) {
+        throw new Error("Invalid date string: " + string);
+    }
+    
+    var sign = (d[1] == "-") ? -1 : 1; // BC or AD
+    var year = sign * d[2];
+    var month = d[5];
+    var date = d[7];
+    var dayofyear = d[9];
+    var week = d[11];
+    var dayofweek = (d[13]) ? d[13] : 1;
+
+    dateObject.setUTCFullYear(year);
+    if (dayofyear) { 
+        dateObject.setUTCMonth(0);
+        dateObject.setUTCDate(Number(dayofyear));
+    } else if (week) {
+        dateObject.setUTCMonth(0);
+        dateObject.setUTCDate(1);
+        var gd = dateObject.getUTCDay();
+        var day =  (gd) ? gd : 7;
+        var offset = Number(dayofweek) + (7 * Number(week));
+        
+        if (day <= 4) { 
+            dateObject.setUTCDate(offset + 1 - day); 
+        } else { 
+            dateObject.setUTCDate(offset + 8 - day); 
+        }
+    } else {
+        if (month) { 
+            dateObject.setUTCDate(1);
+            dateObject.setUTCMonth(month - 1); 
+        }
+        if (date) { 
+            dateObject.setUTCDate(date); 
+        }
+    }
+    
+    return dateObject;
+};
+
+/**
+ * Takes a date object and a string containing an ISO 8601 time and sets the
+ * the time using information parsed from the string.  Note that this method
+ * does not parse any date information.
+ *
+ * @param {Date} dateObject the date object to modify
+ * @param {String} string an ISO 8601 string to parse
+ * @return {Date} the modified date object
+ */
+SimileAjax.DateTime.setIso8601Time = function (dateObject, string) {
+    /*
+     *  This function has been adapted from dojo.date, v.0.3.0
+     *  http://dojotoolkit.org/.
+     */
+    
+    var d = string.match(SimileAjax.DateTime._timeRegexp);
+    if(!d) {
+        SimileAjax.Debug.warn("Invalid time string: " + string);
+        return false;
+    }
+    var hours = d[1];
+    var mins = Number((d[3]) ? d[3] : 0);
+    var secs = (d[5]) ? d[5] : 0;
+    var ms = d[7] ? (Number("0." + d[7]) * 1000) : 0;
+
+    dateObject.setUTCHours(hours);
+    dateObject.setUTCMinutes(mins);
+    dateObject.setUTCSeconds(secs);
+    dateObject.setUTCMilliseconds(ms);
+    
+    return dateObject;
+};
+
+/**
+ * The timezone offset in minutes in the user's browser.
+ * @type Number
+ */
+SimileAjax.DateTime.timezoneOffset = new Date().getTimezoneOffset();
+
+/**
+ * Takes a date object and a string containing an ISO 8601 date and time and 
+ * sets the date object using information parsed from the string.
+ *
+ * @param {Date} dateObject the date object to modify
+ * @param {String} string an ISO 8601 string to parse
+ * @return {Date} the modified date object
+ */
+SimileAjax.DateTime.setIso8601 = function (dateObject, string){
+    /*
+     *  This function has been adapted from dojo.date, v.0.3.0
+     *  http://dojotoolkit.org/.
+     */
+     
+    var offset = null;
+    var comps = (string.indexOf("T") == -1) ? string.split(" ") : string.split("T");
+    
+    SimileAjax.DateTime.setIso8601Date(dateObject, comps[0]);
+    if (comps.length == 2) { 
+        // first strip timezone info from the end
+        var d = comps[1].match(SimileAjax.DateTime._timezoneRegexp);
+        if (d) {
+            if (d[0] == 'Z') {
+                offset = 0;
+            } else {
+                offset = (Number(d[3]) * 60) + Number(d[5]);
+                offset *= ((d[2] == '-') ? 1 : -1);
+            }
+            comps[1] = comps[1].substr(0, comps[1].length - d[0].length);
+        }
+
+        SimileAjax.DateTime.setIso8601Time(dateObject, comps[1]); 
+    }
+    if (offset == null) {
+        offset = dateObject.getTimezoneOffset(); // local time zone if no tz info
+    }
+    dateObject.setTime(dateObject.getTime() + offset * 60000);
+    
+    return dateObject;
+};
+
+/**
+ * Takes a string containing an ISO 8601 date and returns a newly instantiated
+ * date object with the parsed date and time information from the string.
+ *
+ * @param {String} string an ISO 8601 string to parse
+ * @return {Date} a new date object created from the string
+ */
+SimileAjax.DateTime.parseIso8601DateTime = function (string) {
+    try {
+        return SimileAjax.DateTime.setIso8601(new Date(0), string);
+    } catch (e) {
+        return null;
+    }
+};
+
+/**
+ * Takes a string containing a Gregorian date and time and returns a newly
+ * instantiated date object with the parsed date and time information from the
+ * string.  If the param is actually an instance of Date instead of a string, 
+ * simply returns the given date instead.
+ *
+ * @param {Object} o an object, to either return or parse as a string
+ * @return {Date} the date object
+ */
+SimileAjax.DateTime.parseGregorianDateTime = function(o) {
+    if (o == null) {
+        return null;
+    } else if (o instanceof Date) {
+        return o;
+    }
+    
+    var s = o.toString();
+    if (s.length > 0 && s.length < 8) {
+        var space = s.indexOf(" ");
+        if (space > 0) {
+            var year = parseInt(s.substr(0, space));
+            var suffix = s.substr(space + 1);
+            if (suffix.toLowerCase() == "bc") {
+                year = 1 - year;
+            }
+        } else {
+            var year = parseInt(s);
+        }
+            
+        var d = new Date(0);
+        d.setUTCFullYear(year);
+        
+        return d;
+    }
+    
+    try {
+        return new Date(Date.parse(s));
+    } catch (e) {
+        return null;
+    }
+};
+
+/**
+ * Rounds date objects down to the nearest interval or multiple of an interval.
+ * This method modifies the given date object, converting it to the given
+ * timezone if specified.
+ * 
+ * @param {Date} date the date object to round
+ * @param {Number} intervalUnit a constant, integer index specifying an 
+ *   interval, e.g. SimileAjax.DateTime.HOUR
+ * @param {Number} timeZone a timezone shift, given in hours
+ * @param {Number} multiple a multiple of the interval to round by
+ * @param {Number} firstDayOfWeek an integer specifying the first day of the
+ *   week, 0 corresponds to Sunday, 1 to Monday, etc.
+ */
+SimileAjax.DateTime.roundDownToInterval = function(date, intervalUnit, timeZone, multiple, firstDayOfWeek) {
+    var timeShift = timeZone * 
+        SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.HOUR];
+        
+    var date2 = new Date(date.getTime() + timeShift);
+    var clearInDay = function(d) {
+        d.setUTCMilliseconds(0);
+        d.setUTCSeconds(0);
+        d.setUTCMinutes(0);
+        d.setUTCHours(0);
+    };
+    var clearInYear = function(d) {
+        clearInDay(d);
+        d.setUTCDate(1);
+        d.setUTCMonth(0);
+    };
+    
+    switch(intervalUnit) {
+    case SimileAjax.DateTime.MILLISECOND:
+        var x = date2.getUTCMilliseconds();
+        date2.setUTCMilliseconds(x - (x % multiple));
+        break;
+    case SimileAjax.DateTime.SECOND:
+        date2.setUTCMilliseconds(0);
+        
+        var x = date2.getUTCSeconds();
+        date2.setUTCSeconds(x - (x % multiple));
+        break;
+    case SimileAjax.DateTime.MINUTE:
+        date2.setUTCMilliseconds(0);
+        date2.setUTCSeconds(0);
+        
+        var x = date2.getUTCMinutes();
+        date2.setTime(date2.getTime() - 
+            (x % multiple) * SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.MINUTE]);
+        break;
+    case SimileAjax.DateTime.HOUR:
+        date2.setUTCMilliseconds(0);
+        date2.setUTCSeconds(0);
+        date2.setUTCMinutes(0);
+        
+        var x = date2.getUTCHours();
+        date2.setUTCHours(x - (x % multiple));
+        break;
+    case SimileAjax.DateTime.DAY:
+        clearInDay(date2);
+        break;
+    case SimileAjax.DateTime.WEEK:
+        clearInDay(date2);
+        var d = (date2.getUTCDay() + 7 - firstDayOfWeek) % 7;
+        date2.setTime(date2.getTime() - 
+            d * SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.DAY]);
+        break;
+    case SimileAjax.DateTime.MONTH:
+        clearInDay(date2);
+        date2.setUTCDate(1);
+        
+        var x = date2.getUTCMonth();
+        date2.setUTCMonth(x - (x % multiple));
+        break;
+    case SimileAjax.DateTime.YEAR:
+        clearInYear(date2);
+        
+        var x = date2.getUTCFullYear();
+        date2.setUTCFullYear(x - (x % multiple));
+        break;
+    case SimileAjax.DateTime.DECADE:
+        clearInYear(date2);
+        date2.setUTCFullYear(Math.floor(date2.getUTCFullYear() / 10) * 10);
+        break;
+    case SimileAjax.DateTime.CENTURY:
+        clearInYear(date2);
+        date2.setUTCFullYear(Math.floor(date2.getUTCFullYear() / 100) * 100);
+        break;
+    case SimileAjax.DateTime.MILLENNIUM:
+        clearInYear(date2);
+        date2.setUTCFullYear(Math.floor(date2.getUTCFullYear() / 1000) * 1000);
+        break;
+    }
+    
+    date.setTime(date2.getTime() - timeShift);
+};
+
+/**
+ * Rounds date objects up to the nearest interval or multiple of an interval.
+ * This method modifies the given date object, converting it to the given
+ * timezone if specified.
+ * 
+ * @param {Date} date the date object to round
+ * @param {Number} intervalUnit a constant, integer index specifying an 
+ *   interval, e.g. SimileAjax.DateTime.HOUR
+ * @param {Number} timeZone a timezone shift, given in hours
+ * @param {Number} multiple a multiple of the interval to round by
+ * @param {Number} firstDayOfWeek an integer specifying the first day of the
+ *   week, 0 corresponds to Sunday, 1 to Monday, etc.
+ * @see SimileAjax.DateTime.roundDownToInterval
+ */
+SimileAjax.DateTime.roundUpToInterval = function(date, intervalUnit, timeZone, multiple, firstDayOfWeek) {
+    var originalTime = date.getTime();
+    SimileAjax.DateTime.roundDownToInterval(date, intervalUnit, timeZone, multiple, firstDayOfWeek);
+    if (date.getTime() < originalTime) {
+        date.setTime(date.getTime() + 
+            SimileAjax.DateTime.gregorianUnitLengths[intervalUnit] * multiple);
+    }
+};
+
+/**
+ * Increments a date object by a specified interval, taking into
+ * consideration the timezone.
+ *
+ * @param {Date} date the date object to increment
+ * @param {Number} intervalUnit a constant, integer index specifying an 
+ *   interval, e.g. SimileAjax.DateTime.HOUR
+ * @param {Number} timeZone the timezone offset in hours
+ */
+SimileAjax.DateTime.incrementByInterval = function(date, intervalUnit, timeZone) {
+    timeZone = (typeof timeZone == 'undefined') ? 0 : timeZone;
+
+    var timeShift = timeZone * 
+        SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.HOUR];
+        
+    var date2 = new Date(date.getTime() + timeShift);
+
+    switch(intervalUnit) {
+    case SimileAjax.DateTime.MILLISECOND:
+        date2.setTime(date2.getTime() + 1)
+        break;
+    case SimileAjax.DateTime.SECOND:
+        date2.setTime(date2.getTime() + 1000);
+        break;
+    case SimileAjax.DateTime.MINUTE:
+        date2.setTime(date2.getTime() + 
+            SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.MINUTE]);
+        break;
+    case SimileAjax.DateTime.HOUR:
+        date2.setTime(date2.getTime() + 
+            SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.HOUR]);
+        break;
+    case SimileAjax.DateTime.DAY:
+        date2.setUTCDate(date2.getUTCDate() + 1);
+        break;
+    case SimileAjax.DateTime.WEEK:
+        date2.setUTCDate(date2.getUTCDate() + 7);
+        break;
+    case SimileAjax.DateTime.MONTH:
+        date2.setUTCMonth(date2.getUTCMonth() + 1);
+        break;
+    case SimileAjax.DateTime.YEAR:
+        date2.setUTCFullYear(date2.getUTCFullYear() + 1);
+        break;
+    case SimileAjax.DateTime.DECADE:
+        date2.setUTCFullYear(date2.getUTCFullYear() + 10);
+        break;
+    case SimileAjax.DateTime.CENTURY:
+        date2.setUTCFullYear(date2.getUTCFullYear() + 100);
+        break;
+    case SimileAjax.DateTime.MILLENNIUM:
+        date2.setUTCFullYear(date2.getUTCFullYear() + 1000);
+        break;
+    }
+
+    date.setTime(date2.getTime() - timeShift);
+};
+
+/**
+ * Returns a new date object with the given time offset removed.
+ *
+ * @param {Date} date the starting date
+ * @param {Number} timeZone a timezone specified in an hour offset to remove
+ * @return {Date} a new date object with the offset removed
+ */
+SimileAjax.DateTime.removeTimeZoneOffset = function(date, timeZone) {
+    return new Date(date.getTime() + 
+        timeZone * SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.HOUR]);
+};
+
+/**
+ * Returns the timezone of the user's browser.
+ *
+ * @return {Number} the timezone in the user's locale in hours
+ */
+SimileAjax.DateTime.getTimezone = function() {
+    var d = new Date().getTimezoneOffset();
+    return d / -60;
+};
diff --git a/assets/scripts/api.simile-widgets.org/ajax/2.2.1/scripts/debug.js b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/scripts/debug.js
new file mode 100644
index 0000000..3fca6a5
--- /dev/null
+++ b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/scripts/debug.js
@@ -0,0 +1,94 @@
+/*==================================================
+ *  Debug Utility Functions
+ *==================================================
+ */
+
+SimileAjax.Debug = {
+    silent: false
+};
+
+SimileAjax.Debug.log = function(msg) {
+    var f;
+    if ("console" in window && "log" in window.console) { // FireBug installed
+        f = function(msg2) {
+            console.log(msg2);
+        }
+    } else {
+        f = function(msg2) {
+            if (!SimileAjax.Debug.silent) {
+                alert(msg2);
+            }
+        }
+    }
+    SimileAjax.Debug.log = f;
+    f(msg);
+};
+
+SimileAjax.Debug.warn = function(msg) {
+    var f;
+    if ("console" in window && "warn" in window.console) { // FireBug installed
+        f = function(msg2) {
+            console.warn(msg2);
+        }
+    } else {
+        f = function(msg2) {
+            if (!SimileAjax.Debug.silent) {
+                alert(msg2);
+            }
+        }
+    }
+    SimileAjax.Debug.warn = f;
+    f(msg);
+};
+
+SimileAjax.Debug.exception = function(e, msg) {
+    var f, params = SimileAjax.parseURLParameters();
+    if (params.errors == "throw" || SimileAjax.params.errors == "throw") {
+        f = function(e2, msg2) {
+            throw(e2); // do not hide from browser's native debugging features
+        };
+    } else if ("console" in window && "error" in window.console) { // FireBug installed
+        f = function(e2, msg2) {
+            if (msg2 != null) {
+                console.error(msg2 + " %o", e2);
+            } else {
+                console.error(e2);
+            }
+            throw(e2); // do not hide from browser's native debugging features
+        };
+    } else {
+        f = function(e2, msg2) {
+            if (!SimileAjax.Debug.silent) {
+                alert("Caught exception: " + msg2 + "\n\nDetails: " + ("description" in e2 ? e2.description : e2));
+            }
+            throw(e2); // do not hide from browser's native debugging features
+        };
+    }
+    SimileAjax.Debug.exception = f;
+    f(e, msg);
+};
+
+SimileAjax.Debug.objectToString = function(o) {
+    return SimileAjax.Debug._objectToString(o, "");
+};
+
+SimileAjax.Debug._objectToString = function(o, indent) {
+    var indent2 = indent + " ";
+    if (typeof o == "object") {
+        var s = "{";
+        for (n in o) {
+            s += indent2 + n + ": " + SimileAjax.Debug._objectToString(o[n], indent2) + "\n";
+        }
+        s += indent + "}";
+        return s;
+    } else if (typeof o == "array") {
+        var s = "[";
+        for (var n = 0; n < o.length; n++) {
+            s += SimileAjax.Debug._objectToString(o[n], indent2) + "\n";
+        }
+        s += indent + "]";
+        return s;
+    } else {
+        return o;
+    }
+};
diff --git a/assets/scripts/api.simile-widgets.org/ajax/2.2.1/scripts/dom.js b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/scripts/dom.js
new file mode 100644
index 0000000..c153a32
--- /dev/null
+++ b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/scripts/dom.js
@@ -0,0 +1,344 @@
+/*==================================================
+ *  DOM Utility Functions
+ *==================================================
+ */
+
+SimileAjax.DOM = new Object();
+
+SimileAjax.DOM.registerEventWithObject = function(elmt, eventName, obj, handlerName) {
+    SimileAjax.DOM.registerEvent(elmt, eventName, function(elmt2, evt, target) {
+        return obj[handlerName].call(obj, elmt2, evt, target);
+    });
+};
+
+SimileAjax.DOM.registerEvent = function(elmt, eventName, handler) {
+    var handler2 = function(evt) {
+        evt = (evt) ? evt : ((event) ? event : null);
+        if (evt) {
+            var target = (evt.target) ? 
+                evt.target : ((evt.srcElement) ? evt.srcElement : null);
+            if (target) {
+                target = (target.nodeType == 1 || target.nodeType == 9) ? 
+                    target : target.parentNode;
+            }
+            
+            return handler(elmt, evt, target);
+        }
+        return true;
+    }
+    
+    if (SimileAjax.Platform.browser.isIE) {
+        elmt.attachEvent("on" + eventName, handler2);
+    } else {
+        elmt.addEventListener(eventName, handler2, false);
+    }
+};
+
+SimileAjax.DOM.getPageCoordinates = function(elmt) {
+    var left = 0;
+    var top = 0;
+    
+    if (elmt.nodeType != 1) {
+        elmt = elmt.parentNode;
+    }
+    
+    var elmt2 = elmt;
+    while (elmt2 != null) {
+        left += elmt2.offsetLeft;
+        top += elmt2.offsetTop;
+        elmt2 = elmt2.offsetParent;
+    }
+    
+    var body = document.body;
+    while (elmt != null && elmt != body) {
+        if ("scrollLeft" in elmt) {
+            left -= elmt.scrollLeft;
+            top -= elmt.scrollTop;
+        }
+        elmt = elmt.parentNode;
+    }
+    
+    return { left: left, top: top };
+};
+
+SimileAjax.DOM.getSize = function(elmt) {
+	var w = this.getStyle(elmt,"width");
+	var h = this.getStyle(elmt,"height");
+	if (w.indexOf("px") > -1) w = w.replace("px","");
+	if (h.indexOf("px") > -1) h = h.replace("px","");
+	return {
+		w: w,
+		h: h
+	}
+}
+
+SimileAjax.DOM.getStyle = function(elmt, styleProp) {
+    if (elmt.currentStyle) { // IE
+        var style = elmt.currentStyle[styleProp];
+    } else if (window.getComputedStyle) { // standard DOM
+        var style = document.defaultView.getComputedStyle(elmt, null).getPropertyValue(styleProp);
+    } else {
+    	var style = "";
+    }
+    return style;
+}
+
+SimileAjax.DOM.getEventRelativeCoordinates = function(evt, elmt) {
+    if (SimileAjax.Platform.browser.isIE) {
+      if (evt.type == "mousewheel") {
+        var coords = SimileAjax.DOM.getPageCoordinates(elmt);
+        return {
+          x: evt.clientX - coords.left, 
+          y: evt.clientY - coords.top
+        };        
+      } else {
+        return {
+          x: evt.offsetX,
+          y: evt.offsetY
+        };
+      }
+    } else {
+        var coords = SimileAjax.DOM.getPageCoordinates(elmt);
+
+        if ((evt.type == "DOMMouseScroll") &&
+          SimileAjax.Platform.browser.isFirefox &&
+          (SimileAjax.Platform.browser.majorVersion == 2)) {
+          // Due to: https://bugzilla.mozilla.org/show_bug.cgi?id=352179                  
+
+          return {
+            x: evt.screenX - coords.left,
+            y: evt.screenY - coords.top 
+          };
+        } else {
+          return {
+              x: evt.pageX - coords.left,
+              y: evt.pageY - coords.top
+          };
+        }
+    }
+};
+
+SimileAjax.DOM.getEventPageCoordinates = function(evt) {
+    if (SimileAjax.Platform.browser.isIE) {
+        return {
+            x: evt.clientX + document.body.scrollLeft,
+            y: evt.clientY + document.body.scrollTop
+        };
+    } else {
+        return {
+            x: evt.pageX,
+            y: evt.pageY
+        };
+    }
+};
+
+SimileAjax.DOM.hittest = function(x, y, except) {
+    return SimileAjax.DOM._hittest(document.body, x, y, except);
+};
+
+SimileAjax.DOM._hittest = function(elmt, x, y, except) {
+    var childNodes = elmt.childNodes;
+    outer: for (var i = 0; i < childNodes.length; i++) {
+        var childNode = childNodes[i];
+        for (var j = 0; j < except.length; j++) {
+            if (childNode == except[j]) {
+                continue outer;
+            }
+        }
+        
+        if (childNode.offsetWidth == 0 && childNode.offsetHeight == 0) {
+            /*
+             *  Sometimes SPAN elements have zero width and height but
+             *  they have children like DIVs that cover non-zero areas.
+             */
+            var hitNode = SimileAjax.DOM._hittest(childNode, x, y, except);
+            if (hitNode != childNode) {
+                return hitNode;
+            }
+        } else {
+            var top = 0;
+            var left = 0;
+            
+            var node = childNode;
+            while (node) {
+                top += node.offsetTop;
+                left += node.offsetLeft;
+                node = node.offsetParent;
+            }
+            
+            if (left <= x && top <= y && (x - left) < childNode.offsetWidth && (y - top) < childNode.offsetHeight) {
+                return SimileAjax.DOM._hittest(childNode, x, y, except);
+            } else if (childNode.nodeType == 1 && childNode.tagName == "TR") {
+                /*
+                 *  Table row might have cells that span several rows.
+                 */
+                var childNode2 = SimileAjax.DOM._hittest(childNode, x, y, except);
+                if (childNode2 != childNode) {
+                    return childNode2;
+                }
+            }
+        }
+    }
+    return elmt;
+};
+
+SimileAjax.DOM.cancelEvent = function(evt) {
+    evt.returnValue = false;
+    evt.cancelBubble = true;
+    if ("preventDefault" in evt) {
+        evt.preventDefault();
+    }
+};
+
+SimileAjax.DOM.appendClassName = function(elmt, className) {
+    var classes = elmt.className.split(" ");
+    for (var i = 0; i < classes.length; i++) {
+        if (classes[i] == className) {
+            return;
+        }
+    }
+    classes.push(className);
+    elmt.className = classes.join(" ");
+};
+
+SimileAjax.DOM.createInputElement = function(type) {
+    var div = document.createElement("div");
+    div.innerHTML = "<input type='" + type + "' />";
+    
+    return div.firstChild;
+};
+
+SimileAjax.DOM.createDOMFromTemplate = function(template) {
+    var result = {};
+    result.elmt = SimileAjax.DOM._createDOMFromTemplate(template, result, null);
+    
+    return result;
+};
+
+SimileAjax.DOM._createDOMFromTemplate = function(templateNode, result, parentElmt) {
+    if (templateNode == null) {
+        /*
+        var node = doc.createTextNode("--null--");
+        if (parentElmt != null) {
+            parentElmt.appendChild(node);
+        }
+        return node;
+        */
+        return null;
+    } else if (typeof templateNode != "object") {
+        var node = document.createTextNode(templateNode);
+        if (parentElmt != null) {
+            parentElmt.appendChild(node);
+        }
+        return node;
+    } else {
+        var elmt = null;
+        if ("tag" in templateNode) {
+            var tag = templateNode.tag;
+            if (parentElmt != null) {
+                if (tag == "tr") {
+                    elmt = parentElmt.insertRow(parentElmt.rows.length);
+                } else if (tag == "td") {
+                    elmt = parentElmt.insertCell(parentElmt.cells.length);
+                }
+            }
+            if (elmt == null) {
+                elmt = tag == "input" ?
+                    SimileAjax.DOM.createInputElement(templateNode.type) :
+                    document.createElement(tag);
+                    
+                if (parentElmt != null) {
+                    parentElmt.appendChild(elmt);
+                }
+            }
+        } else {
+            elmt = templateNode.elmt;
+            if (parentElmt != null) {
+                parentElmt.appendChild(elmt);
+            }
+        }
+        
+        for (var attribute in templateNode) {
+            var value = templateNode[attribute];
+            
+            if (attribute == "field") {
+                result[value] = elmt;
+                
+            } else if (attribute == "className") {
+                elmt.className = value;
+            } else if (attribute == "id") {
+                elmt.id = value;
+            } else if (attribute == "title") {
+                elmt.title = value;
+            } else if (attribute == "type" && elmt.tagName == "input") {
+                // do nothing
+            } else if (attribute == "style") {
+                for (n in value) {
+                    var v = value[n];
+                    if (n == "float") {
+                        n = SimileAjax.Platform.browser.isIE ? "styleFloat" : "cssFloat";
+                    }
+                    elmt.style[n] = v;
+                }
+            } else if (attribute == "children") {
+                for (var i = 0; i < value.length; i++) {
+                    SimileAjax.DOM._createDOMFromTemplate(value[i], result, elmt);
+                }
+            } else if (attribute != "tag" && attribute != "elmt") {
+                elmt.setAttribute(attribute, value);
+            }
+        }
+        return elmt;
+    }
+}
+
+SimileAjax.DOM._cachedParent = null;
+SimileAjax.DOM.createElementFromString = function(s) {
+    if (SimileAjax.DOM._cachedParent == null) {
+        SimileAjax.DOM._cachedParent = document.createElement("div");
+    }
+    SimileAjax.DOM._cachedParent.innerHTML = s;
+    return SimileAjax.DOM._cachedParent.firstChild;
+};
+
+SimileAjax.DOM.createDOMFromString = function(root, s, fieldElmts) {
+    var elmt = typeof root == "string" ? document.createElement(root) : root;
+    elmt.innerHTML = s;
+    
+    var dom = { elmt: elmt };
+    SimileAjax.DOM._processDOMChildrenConstructedFromString(dom, elmt, fieldElmts != null ? fieldElmts : {} );
+    
+    return dom;
+};
+
+SimileAjax.DOM._processDOMConstructedFromString = function(dom, elmt, fieldElmts) {
+    var id = elmt.id;
+    if (id != null && id.length > 0) {
+        elmt.removeAttribute("id");
+        if (id in fieldElmts) {
+            var parentElmt = elmt.parentNode;
+            parentElmt.insertBefore(fieldElmts[id], elmt);
+            parentElmt.removeChild(elmt);
+            
+            dom[id] = fieldElmts[id];
+            return;
+        } else {
+            dom[id] = elmt;
+        }
+    }
+    
+    if (elmt.hasChildNodes()) {
+        SimileAjax.DOM._processDOMChildrenConstructedFromString(dom, elmt, fieldElmts);
+    }
+};
+
+SimileAjax.DOM._processDOMChildrenConstructedFromString = function(dom, elmt, fieldElmts) {
+    var node = elmt.firstChild;
+    while (node != null) {
+        var node2 = node.nextSibling;
+        if (node.nodeType == 1) {
+            SimileAjax.DOM._processDOMConstructedFromString(dom, node, fieldElmts);
+        }
+        node = node2;
+    }
+};
diff --git a/assets/scripts/api.simile-widgets.org/ajax/2.2.1/scripts/graphics.js b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/scripts/graphics.js
new file mode 100644
index 0000000..5a8499a
--- /dev/null
+++ b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/scripts/graphics.js
@@ -0,0 +1,653 @@
+/**
+ * @fileOverview Graphics utility functions and constants
+ * @name SimileAjax.Graphics
+ */
+
+SimileAjax.Graphics = new Object();
+
+/**
+ * A boolean value indicating whether PNG translucency is supported on the
+ * user's browser or not.
+ *
+ * @type Boolean
+ */
+SimileAjax.Graphics.pngIsTranslucent = (!SimileAjax.Platform.browser.isIE) || (SimileAjax.Platform.browser.majorVersion > 6);
+if (!SimileAjax.Graphics.pngIsTranslucent) {
+    SimileAjax.includeCssFile(document, SimileAjax.urlPrefix + "styles/graphics-ie6.css");
+}
+
+/*==================================================
+ *  Opacity, translucency
+ *==================================================
+ */
+SimileAjax.Graphics._createTranslucentImage1 = function(url, verticalAlign) {
+    var elmt = document.createElement("img");
+    elmt.setAttribute("src", url);
+    if (verticalAlign != null) {
+        elmt.style.verticalAlign = verticalAlign;
+    }
+    return elmt;
+};
+SimileAjax.Graphics._createTranslucentImage2 = function(url, verticalAlign) {
+    var elmt = document.createElement("img");
+    elmt.style.width = "1px";  // just so that IE will calculate the size property
+    elmt.style.height = "1px";
+    elmt.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + url +"', sizingMethod='image')";
+    elmt.style.verticalAlign = (verticalAlign != null) ? verticalAlign : "middle";
+    return elmt;
+};
+
+/**
+ * Creates a DOM element for an <code>img</code> tag using the URL given. This
+ * is a convenience method that automatically includes the necessary CSS to
+ * allow for translucency, even on IE.
+ * 
+ * @function
+ * @param {String} url the URL to the image
+ * @param {String} verticalAlign the CSS value for the image's vertical-align
+ * @return {Element} a DOM element containing the <code>img</code> tag
+ */
+SimileAjax.Graphics.createTranslucentImage = SimileAjax.Graphics.pngIsTranslucent ?
+    SimileAjax.Graphics._createTranslucentImage1 :
+    SimileAjax.Graphics._createTranslucentImage2;
+
+SimileAjax.Graphics._createTranslucentImageHTML1 = function(url, verticalAlign) {
+    return "<img src=\"" + url + "\"" +
+        (verticalAlign != null ? " style=\"vertical-align: " + verticalAlign + ";\"" : "") +
+        " />";
+};
+SimileAjax.Graphics._createTranslucentImageHTML2 = function(url, verticalAlign) {
+    var style = 
+        "width: 1px; height: 1px; " +
+        "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + url +"', sizingMethod='image');" +
+        (verticalAlign != null ? " vertical-align: " + verticalAlign + ";" : "");
+        
+    return "<img src='" + url + "' style=\"" + style + "\" />";
+};
+
+/**
+ * Creates an HTML string for an <code>img</code> tag using the URL given.
+ * This is a convenience method that automatically includes the necessary CSS
+ * to allow for translucency, even on IE.
+ * 
+ * @function
+ * @param {String} url the URL to the image
+ * @param {String} verticalAlign the CSS value for the image's vertical-align
+ * @return {String} a string containing the <code>img</code> tag
+ */
+SimileAjax.Graphics.createTranslucentImageHTML = SimileAjax.Graphics.pngIsTranslucent ?
+    SimileAjax.Graphics._createTranslucentImageHTML1 :
+    SimileAjax.Graphics._createTranslucentImageHTML2;
+
+/**
+ * Sets the opacity on the given DOM element.
+ *
+ * @param {Element} elmt the DOM element to set the opacity on
+ * @param {Number} opacity an integer from 0 to 100 specifying the opacity
+ */
+SimileAjax.Graphics.setOpacity = function(elmt, opacity) {
+    if (SimileAjax.Platform.browser.isIE) {
+        elmt.style.filter = "progid:DXImageTransform.Microsoft.Alpha(Style=0,Opacity=" + opacity + ")";
+    } else {
+        var o = (opacity / 100).toString();
+        elmt.style.opacity = o;
+        elmt.style.MozOpacity = o;
+    }
+};
+
+/*==================================================
+ *  Bubble
+ *==================================================
+ */
+
+SimileAjax.Graphics.bubbleConfig = {
+    containerCSSClass:              "simileAjax-bubble-container",
+    innerContainerCSSClass:         "simileAjax-bubble-innerContainer",
+    contentContainerCSSClass:       "simileAjax-bubble-contentContainer",
+    
+    borderGraphicSize:              50,
+    borderGraphicCSSClassPrefix:    "simileAjax-bubble-border-",
+    
+    arrowGraphicTargetOffset:       33,  // from tip of arrow to the side of the graphic that touches the content of the bubble
+    arrowGraphicLength:             100, // dimension of arrow graphic along the direction that the arrow points
+    arrowGraphicWidth:              49,  // dimension of arrow graphic perpendicular to the direction that the arrow points
+    arrowGraphicCSSClassPrefix:     "simileAjax-bubble-arrow-",
+    
+    closeGraphicCSSClass:           "simileAjax-bubble-close",
+    
+    extraPadding:                   20
+};
+
+/**
+ * Creates a nice, rounded bubble popup with the given content in a div,
+ * page coordinates and a suggested width. The bubble will point to the 
+ * location on the page as described by pageX and pageY.  All measurements 
+ * should be given in pixels.
+ *
+ * @param {Element} the content div
+ * @param {Number} pageX the x coordinate of the point to point to
+ * @param {Number} pageY the y coordinate of the point to point to
+ * @param {Number} contentWidth a suggested width of the content
+ * @param {String} orientation a string ("top", "bottom", "left", or "right")
+ *   that describes the orientation of the arrow on the bubble
+ * @param {Number} maxHeight. Add a scrollbar div if bubble would be too tall.
+ *   Default of 0 or null means no maximum
+ */
+SimileAjax.Graphics.createBubbleForContentAndPoint = function(
+       div, pageX, pageY, contentWidth, orientation, maxHeight) {
+    if (typeof contentWidth != "number") {
+        contentWidth = 300;
+    }
+    if (typeof maxHeight != "number") {
+        maxHeight = 0;
+    }
+
+    div.style.position = "absolute";
+    div.style.left = "-5000px";
+    div.style.top = "0px";
+    div.style.width = contentWidth + "px";
+    document.body.appendChild(div);
+    
+    window.setTimeout(function() {
+        var width = div.scrollWidth + 10;
+        var height = div.scrollHeight + 10;
+        var scrollDivW = 0; // width of the possible inner container when we want vertical scrolling
+        if (maxHeight > 0 && height > maxHeight) {
+          height = maxHeight;
+          scrollDivW = width - 25;
+        }  
+       
+        var bubble = SimileAjax.Graphics.createBubbleForPoint(pageX, pageY, width, height, orientation);
+        
+        document.body.removeChild(div);
+        div.style.position = "static";
+        div.style.left = "";
+        div.style.top = "";
+        
+        // create a scroll div if needed
+        if (scrollDivW > 0) {
+          var scrollDiv = document.createElement("div");
+          div.style.width = "";
+          scrollDiv.style.width = scrollDivW + "px";
+          scrollDiv.appendChild(div);
+          bubble.content.appendChild(scrollDiv);
+        } else {
+          div.style.width = width + "px";
+          bubble.content.appendChild(div);
+        }
+    }, 200);
+};
+
+/**
+ * Creates a nice, rounded bubble popup with the given page coordinates and
+ * content dimensions.  The bubble will point to the location on the page
+ * as described by pageX and pageY.  All measurements should be given in
+ * pixels.
+ *
+ * @param {Number} pageX the x coordinate of the point to point to
+ * @param {Number} pageY the y coordinate of the point to point to
+ * @param {Number} contentWidth the width of the content box in the bubble
+ * @param {Number} contentHeight the height of the content box in the bubble
+ * @param {String} orientation a string ("top", "bottom", "left", or "right")
+ *   that describes the orientation of the arrow on the bubble
+ * @return {Element} a DOM element for the newly created bubble
+ */
+SimileAjax.Graphics.createBubbleForPoint = function(pageX, pageY, contentWidth, contentHeight, orientation) {
+    contentWidth = parseInt(contentWidth, 10); // harden against bad input bugs
+    contentHeight = parseInt(contentHeight, 10); // getting numbers-as-strings
+    
+    var bubbleConfig = SimileAjax.Graphics.bubbleConfig;
+    var pngTransparencyClassSuffix = 
+        SimileAjax.Graphics.pngIsTranslucent ? "pngTranslucent" : "pngNotTranslucent";
+    
+    var bubbleWidth = contentWidth + 2 * bubbleConfig.borderGraphicSize;
+    var bubbleHeight = contentHeight + 2 * bubbleConfig.borderGraphicSize;
+    
+    var generatePngSensitiveClass = function(className) {
+        return className + " " + className + "-" + pngTransparencyClassSuffix;
+    };
+    
+    /*
+     *  Render container divs
+     */
+    var div = document.createElement("div");
+    div.className = generatePngSensitiveClass(bubbleConfig.containerCSSClass);
+    div.style.width = contentWidth + "px";
+    div.style.height = contentHeight + "px";
+    
+    var divInnerContainer = document.createElement("div");
+    divInnerContainer.className = generatePngSensitiveClass(bubbleConfig.innerContainerCSSClass);
+    div.appendChild(divInnerContainer);
+    
+    /*
+     *  Create layer for bubble
+     */
+    var close = function() { 
+        if (!bubble._closed) {
+            document.body.removeChild(bubble._div);
+            bubble._doc = null;
+            bubble._div = null;
+            bubble._content = null;
+            bubble._closed = true;
+        }
+    }
+    var bubble = { _closed: false };
+    var layer = SimileAjax.WindowManager.pushLayer(close, true, div);
+    bubble._div = div;
+    bubble.close = function() { SimileAjax.WindowManager.popLayer(layer); }
+    
+    /*
+     *  Render border graphics
+     */
+    var createBorder = function(classNameSuffix) {
+        var divBorderGraphic = document.createElement("div");
+        divBorderGraphic.className = generatePngSensitiveClass(bubbleConfig.borderGraphicCSSClassPrefix + classNameSuffix);
+        divInnerContainer.appendChild(divBorderGraphic);
+    };
+    createBorder("top-left");
+    createBorder("top-right");
+    createBorder("bottom-left");
+    createBorder("bottom-right");
+    createBorder("left");
+    createBorder("right");
+    createBorder("top");
+    createBorder("bottom");
+    
+    /*
+     *  Render content
+     */
+    var divContentContainer = document.createElement("div");
+    divContentContainer.className = generatePngSensitiveClass(bubbleConfig.contentContainerCSSClass);
+    divInnerContainer.appendChild(divContentContainer);
+    bubble.content = divContentContainer;
+    
+    /*
+     *  Render close button
+     */
+    var divClose = document.createElement("div");
+    divClose.className = generatePngSensitiveClass(bubbleConfig.closeGraphicCSSClass);
+    divInnerContainer.appendChild(divClose);
+    SimileAjax.WindowManager.registerEventWithObject(divClose, "click", bubble, "close");
+    
+    (function() {
+        var dims = SimileAjax.Graphics.getWindowDimensions();
+        var docWidth = dims.w;
+        var docHeight = dims.h;
+        
+        var halfArrowGraphicWidth = Math.ceil(bubbleConfig.arrowGraphicWidth / 2);
+        
+        var createArrow = function(classNameSuffix) {
+            var divArrowGraphic = document.createElement("div");
+            divArrowGraphic.className = generatePngSensitiveClass(bubbleConfig.arrowGraphicCSSClassPrefix + "point-" + classNameSuffix);
+            divInnerContainer.appendChild(divArrowGraphic);
+            return divArrowGraphic;
+        };
+        
+        if (pageX - halfArrowGraphicWidth - bubbleConfig.borderGraphicSize - bubbleConfig.extraPadding > 0 &&
+            pageX + halfArrowGraphicWidth + bubbleConfig.borderGraphicSize + bubbleConfig.extraPadding < docWidth) {
+            
+            /*
+             *  Bubble can be positioned above or below the target point.
+             */
+            
+            var left = pageX - Math.round(contentWidth / 2);
+            left = pageX < (docWidth / 2) ?
+                Math.max(left, bubbleConfig.extraPadding + bubbleConfig.borderGraphicSize) : 
+                Math.min(left, docWidth - bubbleConfig.extraPadding - bubbleConfig.borderGraphicSize - contentWidth);
+                
+            if ((orientation && orientation == "top") || 
+                (!orientation && 
+                    (pageY 
+                        - bubbleConfig.arrowGraphicTargetOffset 
+                        - contentHeight 
+                        - bubbleConfig.borderGraphicSize 
+                        - bubbleConfig.extraPadding > 0))) {
+                
+                /*
+                 *  Position bubble above the target point.
+                 */
+                
+                var divArrow = createArrow("down");
+                divArrow.style.left = (pageX - halfArrowGraphicWidth - left) + "px";
+                
+                div.style.left = left + "px";
+                div.style.top = (pageY - bubbleConfig.arrowGraphicTargetOffset - contentHeight) + "px";
+                
+                return;
+            } else if ((orientation && orientation == "bottom") || 
+                (!orientation && 
+                    (pageY 
+                        + bubbleConfig.arrowGraphicTargetOffset 
+                        + contentHeight 
+                        + bubbleConfig.borderGraphicSize 
+                        + bubbleConfig.extraPadding < docHeight))) {
+                        
+                /*
+                 *  Position bubble below the target point.
+                 */
+                
+                var divArrow = createArrow("up");
+                divArrow.style.left = (pageX - halfArrowGraphicWidth - left) + "px";
+                
+                div.style.left = left + "px";
+                div.style.top = (pageY + bubbleConfig.arrowGraphicTargetOffset) + "px";
+                
+                return;
+            }
+        }
+        
+        var top = pageY - Math.round(contentHeight / 2);
+        top = pageY < (docHeight / 2) ?
+            Math.max(top, bubbleConfig.extraPadding + bubbleConfig.borderGraphicSize) : 
+            Math.min(top, docHeight - bubbleConfig.extraPadding - bubbleConfig.borderGraphicSize - contentHeight);
+            
+        if ((orientation && orientation == "left") || 
+            (!orientation && 
+                (pageX 
+                    - bubbleConfig.arrowGraphicTargetOffset 
+                    - contentWidth
+                    - bubbleConfig.borderGraphicSize 
+                    - bubbleConfig.extraPadding > 0))) {
+            
+            /*
+             *  Position bubble left of the target point.
+             */
+            
+            var divArrow = createArrow("right");
+            divArrow.style.top = (pageY - halfArrowGraphicWidth - top) + "px";
+            
+            div.style.top = top + "px";
+            div.style.left = (pageX - bubbleConfig.arrowGraphicTargetOffset - contentWidth) + "px";
+        } else {
+            
+            /*
+             *  Position bubble right of the target point, as the last resort.
+             */
+            
+            var divArrow = createArrow("left");
+            divArrow.style.top = (pageY - halfArrowGraphicWidth - top) + "px";
+            
+            div.style.top = top + "px";
+            div.style.left = (pageX + bubbleConfig.arrowGraphicTargetOffset) + "px";
+        }
+    })();
+    
+    document.body.appendChild(div);
+    
+    return bubble;
+};
+
+SimileAjax.Graphics.getWindowDimensions = function() {
+    if (typeof window.innerHeight == 'number') {
+        return { w:window.innerWidth, h:window.innerHeight }; // Non-IE
+    } else if (document.documentElement && document.documentElement.clientHeight) {
+        return { // IE6+, in "standards compliant mode"
+            w:document.documentElement.clientWidth,
+            h:document.documentElement.clientHeight
+        };
+    } else if (document.body && document.body.clientHeight) {
+        return { // IE 4 compatible
+            w:document.body.clientWidth,
+            h:document.body.clientHeight
+        };
+    }
+};
+
+
+/**
+ * Creates a floating, rounded message bubble in the center of the window for
+ * displaying modal information, e.g. "Loading..."
+ *
+ * @param {Document} doc the root document for the page to render on
+ * @param {Object} an object with two properties, contentDiv and containerDiv,
+ *   consisting of the newly created DOM elements
+ */
+SimileAjax.Graphics.createMessageBubble = function(doc) {
+    var containerDiv = doc.createElement("div");
+    if (SimileAjax.Graphics.pngIsTranslucent) {
+        var topDiv = doc.createElement("div");
+        topDiv.style.height = "33px";
+        topDiv.style.background = "url(" + SimileAjax.urlPrefix + "images/message-top-left.png) top left no-repeat";
+        topDiv.style.paddingLeft = "44px";
+        containerDiv.appendChild(topDiv);
+        
+        var topRightDiv = doc.createElement("div");
+        topRightDiv.style.height = "33px";
+        topRightDiv.style.background = "url(" + SimileAjax.urlPrefix + "images/message-top-right.png) top right no-repeat";
+        topDiv.appendChild(topRightDiv);
+        
+        var middleDiv = doc.createElement("div");
+        middleDiv.style.background = "url(" + SimileAjax.urlPrefix + "images/message-left.png) top left repeat-y";
+        middleDiv.style.paddingLeft = "44px";
+        containerDiv.appendChild(middleDiv);
+        
+        var middleRightDiv = doc.createElement("div");
+        middleRightDiv.style.background = "url(" + SimileAjax.urlPrefix + "images/message-right.png) top right repeat-y";
+        middleRightDiv.style.paddingRight = "44px";
+        middleDiv.appendChild(middleRightDiv);
+        
+        var contentDiv = doc.createElement("div");
+        middleRightDiv.appendChild(contentDiv);
+        
+        var bottomDiv = doc.createElement("div");
+        bottomDiv.style.height = "55px";
+        bottomDiv.style.background = "url(" + SimileAjax.urlPrefix + "images/message-bottom-left.png) bottom left no-repeat";
+        bottomDiv.style.paddingLeft = "44px";
+        containerDiv.appendChild(bottomDiv);
+        
+        var bottomRightDiv = doc.createElement("div");
+        bottomRightDiv.style.height = "55px";
+        bottomRightDiv.style.background = "url(" + SimileAjax.urlPrefix + "images/message-bottom-right.png) bottom right no-repeat";
+        bottomDiv.appendChild(bottomRightDiv);
+    } else {
+        containerDiv.style.border = "2px solid #7777AA";
+        containerDiv.style.padding = "20px";
+        containerDiv.style.background = "white";
+        SimileAjax.Graphics.setOpacity(containerDiv, 90);
+        
+        var contentDiv = doc.createElement("div");
+        containerDiv.appendChild(contentDiv);
+    }
+    
+    return {
+        containerDiv:   containerDiv,
+        contentDiv:     contentDiv
+    };
+};
+
+/*==================================================
+ *  Animation
+ *==================================================
+ */
+
+/**
+ * Creates an animation for a function, and an interval of values.  The word
+ * "animation" here is used in the sense of repeatedly calling a function with
+ * a current value from within an interval, and a delta value.
+ *
+ * @param {Function} f a function to be called every 50 milliseconds throughout
+ *   the animation duration, of the form f(current, delta), where current is
+ *   the current value within the range and delta is the current change.
+ * @param {Number} from a starting value
+ * @param {Number} to an ending value
+ * @param {Number} duration the duration of the animation in milliseconds
+ * @param {Function} [cont] an optional function that is called at the end of
+ *   the animation, i.e. a continuation.
+ * @return {SimileAjax.Graphics._Animation} a new animation object
+ */
+SimileAjax.Graphics.createAnimation = function(f, from, to, duration, cont) {
+    return new SimileAjax.Graphics._Animation(f, from, to, duration, cont);
+};
+
+SimileAjax.Graphics._Animation = function(f, from, to, duration, cont) {
+    this.f = f;
+    this.cont = (typeof cont == "function") ? cont : function() {};
+    
+    this.from = from;
+    this.to = to;
+    this.current = from;
+    
+    this.duration = duration;
+    this.start = new Date().getTime();
+    this.timePassed = 0;
+};
+
+/**
+ * Runs this animation.
+ */
+SimileAjax.Graphics._Animation.prototype.run = function() {
+    var a = this;
+    window.setTimeout(function() { a.step(); }, 50);
+};
+
+/**
+ * Increments this animation by one step, and then continues the animation with
+ * <code>run()</code>.
+ */
+SimileAjax.Graphics._Animation.prototype.step = function() {
+    this.timePassed += 50;
+    
+    var timePassedFraction = this.timePassed / this.duration;
+    var parameterFraction = -Math.cos(timePassedFraction * Math.PI) / 2 + 0.5;
+    var current = parameterFraction * (this.to - this.from) + this.from;
+    
+    try {
+        this.f(current, current - this.current);
+    } catch (e) {
+    }
+    this.current = current;
+    
+    if (this.timePassed < this.duration) {
+        this.run();
+    } else {
+        this.f(this.to, 0);
+        this["cont"]();
+    }
+};
+
+/*==================================================
+ *  CopyPasteButton
+ *
+ *  Adapted from http://spaces.live.com/editorial/rayozzie/demo/liveclip/liveclipsample/techPreview.html.
+ *==================================================
+ */
+
+/**
+ * Creates a button and textarea for displaying structured data and copying it
+ * to the clipboard.  The data is dynamically generated by the given 
+ * createDataFunction parameter.
+ *
+ * @param {String} image an image URL to use as the background for the 
+ *   generated box
+ * @param {Number} width the width in pixels of the generated box
+ * @param {Number} height the height in pixels of the generated box
+ * @param {Function} createDataFunction a function that is called with no
+ *   arguments to generate the structured data
+ * @return a new DOM element
+ */
+SimileAjax.Graphics.createStructuredDataCopyButton = function(image, width, height, createDataFunction) {
+    var div = document.createElement("div");
+    div.style.position = "relative";
+    div.style.display = "inline";
+    div.style.width = width + "px";
+    div.style.height = height + "px";
+    div.style.overflow = "hidden";
+    div.style.margin = "2px";
+    
+    if (SimileAjax.Graphics.pngIsTranslucent) {
+        div.style.background = "url(" + image + ") no-repeat";
+    } else {
+        div.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + image +"', sizingMethod='image')";
+    }
+    
+    var style;
+    if (SimileAjax.Platform.browser.isIE) {
+        style = "filter:alpha(opacity=0)";
+    } else {
+        style = "opacity: 0";
+    }
+    div.innerHTML = "<textarea rows='1' autocomplete='off' value='none' style='" + style + "' />";
+    
+    var textarea = div.firstChild;
+    textarea.style.width = width + "px";
+    textarea.style.height = height + "px";
+    textarea.onmousedown = function(evt) {
+        evt = (evt) ? evt : ((event) ? event : null);
+        if (evt.button == 2) {
+            textarea.value = createDataFunction();
+            textarea.select();
+        }
+    };
+    
+    return div;
+};
+
+/*==================================================
+ *  getWidthHeight
+ *==================================================
+ */
+SimileAjax.Graphics.getWidthHeight = function(el) {
+    // RETURNS hash {width:  w, height: h} in pixels
+    
+    var w, h;
+    // offsetWidth rounds on FF, so doesn't work for us.
+    // See https://bugzilla.mozilla.org/show_bug.cgi?id=458617
+    if (el.getBoundingClientRect == null) {
+    	// use offsetWidth
+      w = el.offsetWidth;
+      h = el.offsetHeight;
+    } else {
+    	// use getBoundingClientRect
+      var rect = el.getBoundingClientRect();
+      w = Math.ceil(rect.right - rect.left);
+    	h = Math.ceil(rect.bottom - rect.top);
+    }
+    return {
+        width:  w,
+        height: h
+    };
+};
+ 
+
+/*==================================================
+ *  FontRenderingContext
+ *==================================================
+ */
+SimileAjax.Graphics.getFontRenderingContext = function(elmt, width) {
+    return new SimileAjax.Graphics._FontRenderingContext(elmt, width);
+};
+
+SimileAjax.Graphics._FontRenderingContext = function(elmt, width) {
+    this._elmt = elmt;
+    this._elmt.style.visibility = "hidden";
+    if (typeof width == "string") {
+        this._elmt.style.width = width;
+    } else if (typeof width == "number") {
+        this._elmt.style.width = width + "px";
+    }
+};
+
+SimileAjax.Graphics._FontRenderingContext.prototype.dispose = function() {
+    this._elmt = null;
+};
+
+SimileAjax.Graphics._FontRenderingContext.prototype.update = function() {
+    this._elmt.innerHTML = "A";
+    this._lineHeight = this._elmt.offsetHeight;
+};
+
+SimileAjax.Graphics._FontRenderingContext.prototype.computeSize = function(text, className) {
+    // className arg is optional
+    var el = this._elmt;
+    el.innerHTML = text;
+    el.className = className === undefined ? '' : className;
+    var wh = SimileAjax.Graphics.getWidthHeight(el);
+    el.className = ''; // reset for the next guy
+    
+    return wh;
+};
+
+SimileAjax.Graphics._FontRenderingContext.prototype.getLineHeight = function() {
+    return this._lineHeight;
+};
+
diff --git a/assets/scripts/api.simile-widgets.org/ajax/2.2.1/scripts/history.js b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/scripts/history.js
new file mode 100644
index 0000000..678b1ec
--- /dev/null
+++ b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/scripts/history.js
@@ -0,0 +1,220 @@
+/*======================================================================
+ *  History
+ *
+ *  This is a singleton that keeps track of undoable user actions and 
+ *  performs undos and redos in response to the browser's Back and 
+ *  Forward buttons.
+ *
+ *  Call addAction(action) to register an undoable user action. action
+ *  must have 4 fields:
+ *
+ *      perform: an argument-less function that carries out the action
+ *      undo:    an argument-less function that undos the action
+ *      label:   a short, user-friendly string describing the action
+ *      uiLayer: the UI layer on which the action takes place
+ *
+ *  By default, the history keeps track of upto 10 actions. You can 
+ *  configure this behavior by setting 
+ *      SimileAjax.History.maxHistoryLength
+ *  to a different number.
+ *
+ *  An iframe is inserted into the document's body element to track 
+ *  onload events.
+ *======================================================================
+ */
+ 
+SimileAjax.History = {
+    maxHistoryLength:       10,
+    historyFile:            "__history__.html",
+    enabled:               true,
+    
+    _initialized:           false,
+    _listeners:             new SimileAjax.ListenerQueue(),
+    
+    _actions:               [],
+    _baseIndex:             0,
+    _currentIndex:          0,
+    
+    _plainDocumentTitle:    document.title
+};
+
+SimileAjax.History.formatHistoryEntryTitle = function(actionLabel) {
+    return SimileAjax.History._plainDocumentTitle + " {" + actionLabel + "}";
+};
+
+SimileAjax.History.initialize = function() {
+    if (SimileAjax.History._initialized) {
+        return;
+    }
+    
+    if (SimileAjax.History.enabled) {
+        var iframe = document.createElement("iframe");
+        iframe.id = "simile-ajax-history";
+        iframe.style.position = "absolute";
+        iframe.style.width = "10px";
+        iframe.style.height = "10px";
+        iframe.style.top = "0px";
+        iframe.style.left = "0px";
+        iframe.style.visibility = "hidden";
+        iframe.src = SimileAjax.History.historyFile + "?0";
+        
+        document.body.appendChild(iframe);
+        SimileAjax.DOM.registerEvent(iframe, "load", SimileAjax.History._handleIFrameOnLoad);
+        
+        SimileAjax.History._iframe = iframe;
+    }
+    SimileAjax.History._initialized = true;
+};
+
+SimileAjax.History.addListener = function(listener) {
+    SimileAjax.History.initialize();
+    
+    SimileAjax.History._listeners.add(listener);
+};
+
+SimileAjax.History.removeListener = function(listener) {
+    SimileAjax.History.initialize();
+    
+    SimileAjax.History._listeners.remove(listener);
+};
+
+SimileAjax.History.addAction = function(action) {
+    SimileAjax.History.initialize();
+    
+    SimileAjax.History._listeners.fire("onBeforePerform", [ action ]);
+    window.setTimeout(function() {
+        try {
+            action.perform();
+            SimileAjax.History._listeners.fire("onAfterPerform", [ action ]);
+                
+            if (SimileAjax.History.enabled) {
+                SimileAjax.History._actions = SimileAjax.History._actions.slice(
+                    0, SimileAjax.History._currentIndex - SimileAjax.History._baseIndex);
+                    
+                SimileAjax.History._actions.push(action);
+                SimileAjax.History._currentIndex++;
+                
+                var diff = SimileAjax.History._actions.length - SimileAjax.History.maxHistoryLength;
+                if (diff > 0) {
+                    SimileAjax.History._actions = SimileAjax.History._actions.slice(diff);
+                    SimileAjax.History._baseIndex += diff;
+                }
+                
+                try {
+                    SimileAjax.History._iframe.contentWindow.location.search = 
+                        "?" + SimileAjax.History._currentIndex;
+                } catch (e) {
+                    /*
+                     *  We can't modify location.search most probably because it's a file:// url.
+                     *  We'll just going to modify the document's title.
+                     */
+                    var title = SimileAjax.History.formatHistoryEntryTitle(action.label);
+                    document.title = title;
+                }
+            }
+        } catch (e) {
+            SimileAjax.Debug.exception(e, "Error adding action {" + action.label + "} to history");
+        }
+    }, 0);
+};
+
+SimileAjax.History.addLengthyAction = function(perform, undo, label) {
+    SimileAjax.History.addAction({
+        perform:    perform,
+        undo:       undo,
+        label:      label,
+        uiLayer:    SimileAjax.WindowManager.getBaseLayer(),
+        lengthy:    true
+    });
+};
+
+SimileAjax.History._handleIFrameOnLoad = function() {
+    /*
+     *  This function is invoked when the user herself
+     *  navigates backward or forward. We need to adjust
+     *  the application's state accordingly.
+     */
+    
+    try {
+        var q = SimileAjax.History._iframe.contentWindow.location.search;
+        var c = (q.length == 0) ? 0 : Math.max(0, parseInt(q.substr(1)));
+        
+        var finishUp = function() {
+            var diff = c - SimileAjax.History._currentIndex;
+            SimileAjax.History._currentIndex += diff;
+            SimileAjax.History._baseIndex += diff;
+                
+            SimileAjax.History._iframe.contentWindow.location.search = "?" + c;
+        };
+        
+        if (c < SimileAjax.History._currentIndex) { // need to undo
+            SimileAjax.History._listeners.fire("onBeforeUndoSeveral", []);
+            window.setTimeout(function() {
+                while (SimileAjax.History._currentIndex > c && 
+                       SimileAjax.History._currentIndex > SimileAjax.History._baseIndex) {
+                       
+                    SimileAjax.History._currentIndex--;
+                    
+                    var action = SimileAjax.History._actions[SimileAjax.History._currentIndex - SimileAjax.History._baseIndex];
+                    
+                    try {
+                        action.undo();
+                    } catch (e) {
+                        SimileAjax.Debug.exception(e, "History: Failed to undo action {" + action.label + "}");
+                    }
+                }
+                
+                SimileAjax.History._listeners.fire("onAfterUndoSeveral", []);
+                finishUp();
+            }, 0);
+        } else if (c > SimileAjax.History._currentIndex) { // need to redo
+            SimileAjax.History._listeners.fire("onBeforeRedoSeveral", []);
+            window.setTimeout(function() {
+                while (SimileAjax.History._currentIndex < c && 
+                       SimileAjax.History._currentIndex - SimileAjax.History._baseIndex < SimileAjax.History._actions.length) {
+                       
+                    var action = SimileAjax.History._actions[SimileAjax.History._currentIndex - SimileAjax.History._baseIndex];
+                    
+                    try {
+                        action.perform();
+                    } catch (e) {
+                        SimileAjax.Debug.exception(e, "History: Failed to redo action {" + action.label + "}");
+                    }
+                    
+                    SimileAjax.History._currentIndex++;
+                }
+                
+                SimileAjax.History._listeners.fire("onAfterRedoSeveral", []);
+                finishUp();
+            }, 0);
+        } else {
+            var index = SimileAjax.History._currentIndex - SimileAjax.History._baseIndex - 1;
+            var title = (index >= 0 && index < SimileAjax.History._actions.length) ?
+                SimileAjax.History.formatHistoryEntryTitle(SimileAjax.History._actions[index].label) :
+                SimileAjax.History._plainDocumentTitle;
+                
+            SimileAjax.History._iframe.contentWindow.document.title = title;
+            document.title = title;
+        }
+    } catch (e) {
+        // silent
+    }
+};
+
+SimileAjax.History.getNextUndoAction = function() {
+    try {
+        var index = SimileAjax.History._currentIndex - SimileAjax.History._baseIndex - 1;
+        return SimileAjax.History._actions[index];
+    } catch (e) {
+        return null;
+    }
+};
+
+SimileAjax.History.getNextRedoAction = function() {
+    try {
+        var index = SimileAjax.History._currentIndex - SimileAjax.History._baseIndex;
+        return SimileAjax.History._actions[index];
+    } catch (e) {
+        return null;
+    }
+};
diff --git a/assets/scripts/api.simile-widgets.org/ajax/2.2.1/scripts/html.js b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/scripts/html.js
new file mode 100644
index 0000000..cedeb4a
--- /dev/null
+++ b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/scripts/html.js
@@ -0,0 +1,274 @@
+/*==================================================
+ *  HTML Utility Functions
+ *==================================================
+ */
+
+SimileAjax.HTML = new Object();
+
+SimileAjax.HTML._e2uHash = {};
+(function() {
+    var e2uHash = SimileAjax.HTML._e2uHash;
+    e2uHash['nbsp']= '\u00A0[space]';
+    e2uHash['iexcl']= '\u00A1';
+    e2uHash['cent']= '\u00A2';
+    e2uHash['pound']= '\u00A3';
+    e2uHash['curren']= '\u00A4';
+    e2uHash['yen']= '\u00A5';
+    e2uHash['brvbar']= '\u00A6';
+    e2uHash['sect']= '\u00A7';
+    e2uHash['uml']= '\u00A8';
+    e2uHash['copy']= '\u00A9';
+    e2uHash['ordf']= '\u00AA';
+    e2uHash['laquo']= '\u00AB';
+    e2uHash['not']= '\u00AC';
+    e2uHash['shy']= '\u00AD';
+    e2uHash['reg']= '\u00AE';
+    e2uHash['macr']= '\u00AF';
+    e2uHash['deg']= '\u00B0';
+    e2uHash['plusmn']= '\u00B1';
+    e2uHash['sup2']= '\u00B2';
+    e2uHash['sup3']= '\u00B3';
+    e2uHash['acute']= '\u00B4';
+    e2uHash['micro']= '\u00B5';
+    e2uHash['para']= '\u00B6';
+    e2uHash['middot']= '\u00B7';
+    e2uHash['cedil']= '\u00B8';
+    e2uHash['sup1']= '\u00B9';
+    e2uHash['ordm']= '\u00BA';
+    e2uHash['raquo']= '\u00BB';
+    e2uHash['frac14']= '\u00BC';
+    e2uHash['frac12']= '\u00BD';
+    e2uHash['frac34']= '\u00BE';
+    e2uHash['iquest']= '\u00BF';
+    e2uHash['Agrave']= '\u00C0';
+    e2uHash['Aacute']= '\u00C1';
+    e2uHash['Acirc']= '\u00C2';
+    e2uHash['Atilde']= '\u00C3';
+    e2uHash['Auml']= '\u00C4';
+    e2uHash['Aring']= '\u00C5';
+    e2uHash['AElig']= '\u00C6';
+    e2uHash['Ccedil']= '\u00C7';
+    e2uHash['Egrave']= '\u00C8';
+    e2uHash['Eacute']= '\u00C9';
+    e2uHash['Ecirc']= '\u00CA';
+    e2uHash['Euml']= '\u00CB';
+    e2uHash['Igrave']= '\u00CC';
+    e2uHash['Iacute']= '\u00CD';
+    e2uHash['Icirc']= '\u00CE';
+    e2uHash['Iuml']= '\u00CF';
+    e2uHash['ETH']= '\u00D0';
+    e2uHash['Ntilde']= '\u00D1';
+    e2uHash['Ograve']= '\u00D2';
+    e2uHash['Oacute']= '\u00D3';
+    e2uHash['Ocirc']= '\u00D4';
+    e2uHash['Otilde']= '\u00D5';
+    e2uHash['Ouml']= '\u00D6';
+    e2uHash['times']= '\u00D7';
+    e2uHash['Oslash']= '\u00D8';
+    e2uHash['Ugrave']= '\u00D9';
+    e2uHash['Uacute']= '\u00DA';
+    e2uHash['Ucirc']= '\u00DB';
+    e2uHash['Uuml']= '\u00DC';
+    e2uHash['Yacute']= '\u00DD';
+    e2uHash['THORN']= '\u00DE';
+    e2uHash['szlig']= '\u00DF';
+    e2uHash['agrave']= '\u00E0';
+    e2uHash['aacute']= '\u00E1';
+    e2uHash['acirc']= '\u00E2';
+    e2uHash['atilde']= '\u00E3';
+    e2uHash['auml']= '\u00E4';
+    e2uHash['aring']= '\u00E5';
+    e2uHash['aelig']= '\u00E6';
+    e2uHash['ccedil']= '\u00E7';
+    e2uHash['egrave']= '\u00E8';
+    e2uHash['eacute']= '\u00E9';
+    e2uHash['ecirc']= '\u00EA';
+    e2uHash['euml']= '\u00EB';
+    e2uHash['igrave']= '\u00EC';
+    e2uHash['iacute']= '\u00ED';
+    e2uHash['icirc']= '\u00EE';
+    e2uHash['iuml']= '\u00EF';
+    e2uHash['eth']= '\u00F0';
+    e2uHash['ntilde']= '\u00F1';
+    e2uHash['ograve']= '\u00F2';
+    e2uHash['oacute']= '\u00F3';
+    e2uHash['ocirc']= '\u00F4';
+    e2uHash['otilde']= '\u00F5';
+    e2uHash['ouml']= '\u00F6';
+    e2uHash['divide']= '\u00F7';
+    e2uHash['oslash']= '\u00F8';
+    e2uHash['ugrave']= '\u00F9';
+    e2uHash['uacute']= '\u00FA';
+    e2uHash['ucirc']= '\u00FB';
+    e2uHash['uuml']= '\u00FC';
+    e2uHash['yacute']= '\u00FD';
+    e2uHash['thorn']= '\u00FE';
+    e2uHash['yuml']= '\u00FF';
+    e2uHash['quot']= '\u0022';
+    e2uHash['amp']= '\u0026';
+    e2uHash['lt']= '\u003C';
+    e2uHash['gt']= '\u003E';
+    e2uHash['OElig']= '';
+    e2uHash['oelig']= '\u0153';
+    e2uHash['Scaron']= '\u0160';
+    e2uHash['scaron']= '\u0161';
+    e2uHash['Yuml']= '\u0178';
+    e2uHash['circ']= '\u02C6';
+    e2uHash['tilde']= '\u02DC';
+    e2uHash['ensp']= '\u2002';
+    e2uHash['emsp']= '\u2003';
+    e2uHash['thinsp']= '\u2009';
+    e2uHash['zwnj']= '\u200C';
+    e2uHash['zwj']= '\u200D';
+    e2uHash['lrm']= '\u200E';
+    e2uHash['rlm']= '\u200F';
+    e2uHash['ndash']= '\u2013';
+    e2uHash['mdash']= '\u2014';
+    e2uHash['lsquo']= '\u2018';
+    e2uHash['rsquo']= '\u2019';
+    e2uHash['sbquo']= '\u201A';
+    e2uHash['ldquo']= '\u201C';
+    e2uHash['rdquo']= '\u201D';
+    e2uHash['bdquo']= '\u201E';
+    e2uHash['dagger']= '\u2020';
+    e2uHash['Dagger']= '\u2021';
+    e2uHash['permil']= '\u2030';
+    e2uHash['lsaquo']= '\u2039';
+    e2uHash['rsaquo']= '\u203A';
+    e2uHash['euro']= '\u20AC';
+    e2uHash['fnof']= '\u0192';
+    e2uHash['Alpha']= '\u0391';
+    e2uHash['Beta']= '\u0392';
+    e2uHash['Gamma']= '\u0393';
+    e2uHash['Delta']= '\u0394';
+    e2uHash['Epsilon']= '\u0395';
+    e2uHash['Zeta']= '\u0396';
+    e2uHash['Eta']= '\u0397';
+    e2uHash['Theta']= '\u0398';
+    e2uHash['Iota']= '\u0399';
+    e2uHash['Kappa']= '\u039A';
+    e2uHash['Lambda']= '\u039B';
+    e2uHash['Mu']= '\u039C';
+    e2uHash['Nu']= '\u039D';
+    e2uHash['Xi']= '\u039E';
+    e2uHash['Omicron']= '\u039F';
+    e2uHash['Pi']= '\u03A0';
+    e2uHash['Rho']= '\u03A1';
+    e2uHash['Sigma']= '\u03A3';
+    e2uHash['Tau']= '\u03A4';
+    e2uHash['Upsilon']= '\u03A5';
+    e2uHash['Phi']= '\u03A6';
+    e2uHash['Chi']= '\u03A7';
+    e2uHash['Psi']= '\u03A8';
+    e2uHash['Omega']= '\u03A9';
+    e2uHash['alpha']= '\u03B1';
+    e2uHash['beta']= '\u03B2';
+    e2uHash['gamma']= '\u03B3';
+    e2uHash['delta']= '\u03B4';
+    e2uHash['epsilon']= '\u03B5';
+    e2uHash['zeta']= '\u03B6';
+    e2uHash['eta']= '\u03B7';
+    e2uHash['theta']= '\u03B8';
+    e2uHash['iota']= '\u03B9';
+    e2uHash['kappa']= '\u03BA';
+    e2uHash['lambda']= '\u03BB';
+    e2uHash['mu']= '\u03BC';
+    e2uHash['nu']= '\u03BD';
+    e2uHash['xi']= '\u03BE';
+    e2uHash['omicron']= '\u03BF';
+    e2uHash['pi']= '\u03C0';
+    e2uHash['rho']= '\u03C1';
+    e2uHash['sigmaf']= '\u03C2';
+    e2uHash['sigma']= '\u03C3';
+    e2uHash['tau']= '\u03C4';
+    e2uHash['upsilon']= '\u03C5';
+    e2uHash['phi']= '\u03C6';
+    e2uHash['chi']= '\u03C7';
+    e2uHash['psi']= '\u03C8';
+    e2uHash['omega']= '\u03C9';
+    e2uHash['thetasym']= '\u03D1';
+    e2uHash['upsih']= '\u03D2';
+    e2uHash['piv']= '\u03D6';
+    e2uHash['bull']= '\u2022';
+    e2uHash['hellip']= '\u2026';
+    e2uHash['prime']= '\u2032';
+    e2uHash['Prime']= '\u2033';
+    e2uHash['oline']= '\u203E';
+    e2uHash['frasl']= '\u2044';
+    e2uHash['weierp']= '\u2118';
+    e2uHash['image']= '\u2111';
+    e2uHash['real']= '\u211C';
+    e2uHash['trade']= '\u2122';
+    e2uHash['alefsym']= '\u2135';
+    e2uHash['larr']= '\u2190';
+    e2uHash['uarr']= '\u2191';
+    e2uHash['rarr']= '\u2192';
+    e2uHash['darr']= '\u2193';
+    e2uHash['harr']= '\u2194';
+    e2uHash['crarr']= '\u21B5';
+    e2uHash['lArr']= '\u21D0';
+    e2uHash['uArr']= '\u21D1';
+    e2uHash['rArr']= '\u21D2';
+    e2uHash['dArr']= '\u21D3';
+    e2uHash['hArr']= '\u21D4';
+    e2uHash['forall']= '\u2200';
+    e2uHash['part']= '\u2202';
+    e2uHash['exist']= '\u2203';
+    e2uHash['empty']= '\u2205';
+    e2uHash['nabla']= '\u2207';
+    e2uHash['isin']= '\u2208';
+    e2uHash['notin']= '\u2209';
+    e2uHash['ni']= '\u220B';
+    e2uHash['prod']= '\u220F';
+    e2uHash['sum']= '\u2211';
+    e2uHash['minus']= '\u2212';
+    e2uHash['lowast']= '\u2217';
+    e2uHash['radic']= '\u221A';
+    e2uHash['prop']= '\u221D';
+    e2uHash['infin']= '\u221E';
+    e2uHash['ang']= '\u2220';
+    e2uHash['and']= '\u2227';
+    e2uHash['or']= '\u2228';
+    e2uHash['cap']= '\u2229';
+    e2uHash['cup']= '\u222A';
+    e2uHash['int']= '\u222B';
+    e2uHash['there4']= '\u2234';
+    e2uHash['sim']= '\u223C';
+    e2uHash['cong']= '\u2245';
+    e2uHash['asymp']= '\u2248';
+    e2uHash['ne']= '\u2260';
+    e2uHash['equiv']= '\u2261';
+    e2uHash['le']= '\u2264';
+    e2uHash['ge']= '\u2265';
+    e2uHash['sub']= '\u2282';
+    e2uHash['sup']= '\u2283';
+    e2uHash['nsub']= '\u2284';
+    e2uHash['sube']= '\u2286';
+    e2uHash['supe']= '\u2287';
+    e2uHash['oplus']= '\u2295';
+    e2uHash['otimes']= '\u2297';
+    e2uHash['perp']= '\u22A5';
+    e2uHash['sdot']= '\u22C5';
+    e2uHash['lceil']= '\u2308';
+    e2uHash['rceil']= '\u2309';
+    e2uHash['lfloor']= '\u230A';
+    e2uHash['rfloor']= '\u230B';
+    e2uHash['lang']= '\u2329';
+    e2uHash['rang']= '\u232A';
+    e2uHash['loz']= '\u25CA';
+    e2uHash['spades']= '\u2660';
+    e2uHash['clubs']= '\u2663';
+    e2uHash['hearts']= '\u2665';
+    e2uHash['diams']= '\u2666'; 
+})();
+
+SimileAjax.HTML.deEntify = function(s) {
+    var e2uHash = SimileAjax.HTML._e2uHash;
+    
+    var re = /&(\w+?);/;
+    while (re.test(s)) {
+        var m = s.match(re);
+        s = s.replace(re, e2uHash[m[1]]);
+    }
+    return s;
+};
\ No newline at end of file
diff --git a/assets/scripts/api.simile-widgets.org/ajax/2.2.1/scripts/jquery-1.3.2.min.js b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/scripts/jquery-1.3.2.min.js
new file mode 100644
index 0000000..e4c1728
--- /dev/null
+++ b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/scripts/jquery-1.3.2.min.js
@@ -0,0 +1,23 @@
+if (!("jQuery" in window) && !("$" in window)) {
+
+/*
+ * jQuery JavaScript Library v1.3.2
+ * http://jquery.com/
+ *
+ * Copyright (c) 2009 John Resig
+ * Dual licensed under the MIT and GPL licenses.
+ * http://docs.jquery.com/License
+ *
+ * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009)
+ * Revision: 6246
+ */
+(function(){var l=this,g,y=l.jQuery,p=l.$,o=l.jQuery=l.$=function(E,F){return new o.fn.init(E,F)},D=/^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,f=/^.[^:#\[\.,]*$/;o.fn=o.prototype={init:function(E,H){E=E||document;if(E.nodeType){this[0]=E;this.length=1;this.context=E;return this}if(typeof E==="string"){var G=D.exec(E);if(G&&(G[1]||!H)){if(G[1]){E=o.clean([G[1]],H)}else{var I=document.getElementById(G[3]);if(I&&I.id!=G[3]){return o().find(E)}var F=o(I||[]);F.context=document;F.selector=E;return [...]
+/*
+ * Sizzle CSS Selector Engine - v0.9.3
+ *  Copyright 2009, The Dojo Foundation
+ *  Released under the MIT, BSD, and GPL Licenses.
+ *  More information: http://sizzlejs.com/
+ */
+(function(){var R=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,L=0,H=Object.prototype.toString;var F=function(Y,U,ab,ac){ab=ab||[];U=U||document;if(U.nodeType!==1&&U.nodeType!==9){return[]}if(!Y||typeof Y!=="string"){return ab}var Z=[],W,af,ai,T,ad,V,X=true;R.lastIndex=0;while((W=R.exec(Y))!==null){Z.push(W[1]);if(W[2]){V=RegExp.rightContext;break}}if(Z.length>1&&M.exec(Y)){if(Z.length===2&&I.relative[Z[0]]){af=J(Z[ [...]
+
+}
\ No newline at end of file
diff --git a/assets/scripts/api.simile-widgets.org/ajax/2.2.1/scripts/json.js b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/scripts/json.js
new file mode 100644
index 0000000..ef6dc30
--- /dev/null
+++ b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/scripts/json.js
@@ -0,0 +1,129 @@
+/*
+ *  Copied directly from http://www.json.org/json.js.
+ */
+
+/*
+    json.js
+    2006-04-28
+
+    This file adds these methods to JavaScript:
+
+        object.toJSONString()
+
+            This method produces a JSON text from an object. The
+            object must not contain any cyclical references.
+
+        array.toJSONString()
+
+            This method produces a JSON text from an array. The
+            array must not contain any cyclical references.
+
+        string.parseJSON()
+
+            This method parses a JSON text to produce an object or
+            array. It will return false if there is an error.
+*/
+
+SimileAjax.JSON = new Object();
+
+(function () {
+    var m = {
+        '\b': '\\b',
+        '\t': '\\t',
+        '\n': '\\n',
+        '\f': '\\f',
+        '\r': '\\r',
+        '"' : '\\"',
+        '\\': '\\\\'
+    };
+    var s = {
+        array: function (x) {
+            var a = ['['], b, f, i, l = x.length, v;
+            for (i = 0; i < l; i += 1) {
+                v = x[i];
+                f = s[typeof v];
+                if (f) {
+                    v = f(v);
+                    if (typeof v == 'string') {
+                        if (b) {
+                            a[a.length] = ',';
+                        }
+                        a[a.length] = v;
+                        b = true;
+                    }
+                }
+            }
+            a[a.length] = ']';
+            return a.join('');
+        },
+        'boolean': function (x) {
+            return String(x);
+        },
+        'null': function (x) {
+            return "null";
+        },
+        number: function (x) {
+            return isFinite(x) ? String(x) : 'null';
+        },
+        object: function (x) {
+            if (x) {
+                if (x instanceof Array) {
+                    return s.array(x);
+                }
+                var a = ['{'], b, f, i, v;
+                for (i in x) {
+                    v = x[i];
+                    f = s[typeof v];
+                    if (f) {
+                        v = f(v);
+                        if (typeof v == 'string') {
+                            if (b) {
+                                a[a.length] = ',';
+                            }
+                            a.push(s.string(i), ':', v);
+                            b = true;
+                        }
+                    }
+                }
+                a[a.length] = '}';
+                return a.join('');
+            }
+            return 'null';
+        },
+        string: function (x) {
+            if (/["\\\x00-\x1f]/.test(x)) {
+                x = x.replace(/([\x00-\x1f\\"])/g, function(a, b) {
+                    var c = m[b];
+                    if (c) {
+                        return c;
+                    }
+                    c = b.charCodeAt();
+                    return '\\u00' +
+                        Math.floor(c / 16).toString(16) +
+                        (c % 16).toString(16);
+                });
+            }
+            return '"' + x + '"';
+        }
+    };
+
+    SimileAjax.JSON.toJSONString = function(o) {
+        if (o instanceof Object) {
+            return s.object(o);
+        } else if (o instanceof Array) {
+            return s.array(o);
+        } else {
+            return o.toString();
+        }
+    };
+    
+    SimileAjax.JSON.parseJSON = function () {
+        try {
+            return !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
+                    this.replace(/"(\\.|[^"\\])*"/g, ''))) &&
+                eval('(' + this + ')');
+        } catch (e) {
+            return false;
+        }
+    };
+})();
diff --git a/assets/scripts/api.simile-widgets.org/ajax/2.2.1/scripts/platform.js b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/scripts/platform.js
new file mode 100644
index 0000000..ace89d4
--- /dev/null
+++ b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/scripts/platform.js
@@ -0,0 +1,114 @@
+/*==================================================
+ *  Platform Utility Functions and Constants
+ *==================================================
+ */
+
+/*  This must be called after our jQuery has been loaded 
+    but before control returns to user-code.
+*/
+
+
+/*==================================================
+ *  REMEMBER to update the Version!
+ *==================================================
+ */
+SimileAjax.version = '2.2.1';
+
+SimileAjax.jQuery = jQuery.noConflict(true);
+if (typeof window["$"] == "undefined") {
+    window.$ = SimileAjax.jQuery;
+}
+
+SimileAjax.Platform.os = {
+    isMac:   false,
+    isWin:   false,
+    isWin32: false,
+    isUnix:  false
+};
+SimileAjax.Platform.browser = {
+    isIE:           false,
+    isNetscape:     false,
+    isMozilla:      false,
+    isFirefox:      false,
+    isOpera:        false,
+    isSafari:       false,
+
+    majorVersion:   0,
+    minorVersion:   0
+};
+
+(function() {
+    var an = navigator.appName.toLowerCase();
+	var ua = navigator.userAgent.toLowerCase(); 
+    
+    /*
+     *  Operating system
+     */
+	SimileAjax.Platform.os.isMac = (ua.indexOf('mac') != -1);
+	SimileAjax.Platform.os.isWin = (ua.indexOf('win') != -1);
+	SimileAjax.Platform.os.isWin32 = SimileAjax.Platform.isWin && (   
+        ua.indexOf('95') != -1 || 
+        ua.indexOf('98') != -1 || 
+        ua.indexOf('nt') != -1 || 
+        ua.indexOf('win32') != -1 || 
+        ua.indexOf('32bit') != -1
+    );
+	SimileAjax.Platform.os.isUnix = (ua.indexOf('x11') != -1);
+    
+    /*
+     *  Browser
+     */
+    SimileAjax.Platform.browser.isIE = (an.indexOf("microsoft") != -1);
+    SimileAjax.Platform.browser.isNetscape = (an.indexOf("netscape") != -1);
+    SimileAjax.Platform.browser.isMozilla = (ua.indexOf("mozilla") != -1);
+    SimileAjax.Platform.browser.isFirefox = (ua.indexOf("firefox") != -1);
+    SimileAjax.Platform.browser.isOpera = (an.indexOf("opera") != -1);
+    SimileAjax.Platform.browser.isSafari = (an.indexOf("safari") != -1);
+    
+    var parseVersionString = function(s) {
+        var a = s.split(".");
+        SimileAjax.Platform.browser.majorVersion = parseInt(a[0]);
+        SimileAjax.Platform.browser.minorVersion = parseInt(a[1]);
+    };
+    var indexOf = function(s, sub, start) {
+        var i = s.indexOf(sub, start);
+        return i >= 0 ? i : s.length;
+    };
+    
+    if (SimileAjax.Platform.browser.isMozilla) {
+        var offset = ua.indexOf("mozilla/");
+        if (offset >= 0) {
+            parseVersionString(ua.substring(offset + 8, indexOf(ua, " ", offset)));
+        }
+    }
+    if (SimileAjax.Platform.browser.isIE) {
+        var offset = ua.indexOf("msie ");
+        if (offset >= 0) {
+            parseVersionString(ua.substring(offset + 5, indexOf(ua, ";", offset)));
+        }
+    }
+    if (SimileAjax.Platform.browser.isNetscape) {
+        var offset = ua.indexOf("rv:");
+        if (offset >= 0) {
+            parseVersionString(ua.substring(offset + 3, indexOf(ua, ")", offset)));
+        }
+    }
+    if (SimileAjax.Platform.browser.isFirefox) {
+        var offset = ua.indexOf("firefox/");
+        if (offset >= 0) {
+            parseVersionString(ua.substring(offset + 8, indexOf(ua, " ", offset)));
+        }
+    }
+    
+    if (!("localeCompare" in String.prototype)) {
+        String.prototype.localeCompare = function (s) {
+            if (this < s) return -1;
+            else if (this > s) return 1;
+            else return 0;
+        };
+    }
+})();
+
+SimileAjax.Platform.getDefaultLocale = function() {
+    return SimileAjax.Platform.clientLocale;
+};
\ No newline at end of file
diff --git a/assets/scripts/api.simile-widgets.org/ajax/2.2.1/scripts/signal.js b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/scripts/signal.js
new file mode 100644
index 0000000..a638fd2
--- /dev/null
+++ b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/scripts/signal.js
@@ -0,0 +1,43 @@
+/*==================================================
+ *  This file is used to detect that all outstanding
+ *  javascript files have been loaded. You can put
+ *  a function reference into SimileAjax_onLoad
+ *  to have it executed once all javascript files
+ *  have loaded.
+ *==================================================
+ */
+(function() {
+    var substring = SimileAjax.urlPrefix + "scripts/signal.js";
+    var heads = document.documentElement.getElementsByTagName("head");
+    for (var h = 0; h < heads.length; h++) {
+        var node = heads[h].firstChild;
+        while (node != null) {
+            if (node.nodeType == 1 && node.tagName.toLowerCase() == "script") {
+                var url = node.src;
+                var i = url.indexOf(substring);
+                if (i >= 0) {
+                    heads[h].removeChild(node); // remove it so we won't hit it again
+
+                    var count = parseInt(url.substr(url.indexOf(substring) + substring.length + 1));
+                    SimileAjax.loadingScriptsCount -= count;
+                    if (SimileAjax.loadingScriptsCount == 0) {
+                        var f = null;
+                        if (typeof SimileAjax_onLoad == "string") {
+                            f = eval(SimileAjax_onLoad);
+                            SimileAjax_onLoad = null;
+                        } else if (typeof SimileAjax_onLoad == "function") {
+                            f = SimileAjax_onLoad;
+                            SimileAjax_onLoad = null;
+                        }
+                        
+                        if (f != null) {
+                            f();
+                        }
+                    }
+                    return;
+                }
+            }
+            node = node.nextSibling;
+        }
+    }
+})();
diff --git a/assets/scripts/api.simile-widgets.org/ajax/2.2.1/scripts/string.js b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/scripts/string.js
new file mode 100644
index 0000000..01466d8
--- /dev/null
+++ b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/scripts/string.js
@@ -0,0 +1,43 @@
+/*==================================================
+ *  String Utility Functions and Constants
+ *==================================================
+ */
+
+String.prototype.trim = function() {
+    return this.replace(/^\s+|\s+$/g, '');
+};
+
+String.prototype.startsWith = function(prefix) {
+    return this.length >= prefix.length && this.substr(0, prefix.length) == prefix;
+};
+
+String.prototype.endsWith = function(suffix) {
+    return this.length >= suffix.length && this.substr(this.length - suffix.length) == suffix;
+};
+
+String.substitute = function(s, objects) {
+    var result = "";
+    var start = 0;
+    while (start < s.length - 1) {
+        var percent = s.indexOf("%", start);
+        if (percent < 0 || percent == s.length - 1) {
+            break;
+        } else if (percent > start && s.charAt(percent - 1) == "\\") {
+            result += s.substring(start, percent - 1) + "%";
+            start = percent + 1;
+        } else {
+            var n = parseInt(s.charAt(percent + 1));
+            if (isNaN(n) || n >= objects.length) {
+                result += s.substring(start, percent + 2);
+            } else {
+                result += s.substring(start, percent) + objects[n].toString();
+            }
+            start = percent + 2;
+        }
+    }
+    
+    if (start < s.length) {
+        result += s.substring(start);
+    }
+    return result;
+};
diff --git a/assets/scripts/api.simile-widgets.org/ajax/2.2.1/scripts/units.js b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/scripts/units.js
new file mode 100644
index 0000000..54f2a5b
--- /dev/null
+++ b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/scripts/units.js
@@ -0,0 +1,64 @@
+/*==================================================
+ *  Default Unit
+ *==================================================
+ */
+
+SimileAjax.NativeDateUnit = new Object();
+
+SimileAjax.NativeDateUnit.makeDefaultValue = function() {
+    return new Date();
+};
+
+SimileAjax.NativeDateUnit.cloneValue = function(v) {
+    return new Date(v.getTime());
+};
+
+SimileAjax.NativeDateUnit.getParser = function(format) {
+    if (typeof format == "string") {
+        format = format.toLowerCase();
+    }
+    return (format == "iso8601" || format == "iso 8601") ?
+        SimileAjax.DateTime.parseIso8601DateTime : 
+        SimileAjax.DateTime.parseGregorianDateTime;
+};
+
+SimileAjax.NativeDateUnit.parseFromObject = function(o) {
+    return SimileAjax.DateTime.parseGregorianDateTime(o);
+};
+
+SimileAjax.NativeDateUnit.toNumber = function(v) {
+    return v.getTime();
+};
+
+SimileAjax.NativeDateUnit.fromNumber = function(n) {
+    return new Date(n);
+};
+
+SimileAjax.NativeDateUnit.compare = function(v1, v2) {
+    var n1, n2;
+    if (typeof v1 == "object") {
+        n1 = v1.getTime();
+    } else {
+        n1 = Number(v1);
+    }
+    if (typeof v2 == "object") {
+        n2 = v2.getTime();
+    } else {
+        n2 = Number(v2);
+    }
+    
+    return n1 - n2;
+};
+
+SimileAjax.NativeDateUnit.earlier = function(v1, v2) {
+    return SimileAjax.NativeDateUnit.compare(v1, v2) < 0 ? v1 : v2;
+};
+
+SimileAjax.NativeDateUnit.later = function(v1, v2) {
+    return SimileAjax.NativeDateUnit.compare(v1, v2) > 0 ? v1 : v2;
+};
+
+SimileAjax.NativeDateUnit.change = function(v, n) {
+    return new Date(v.getTime() + n);
+};
+
diff --git a/assets/scripts/api.simile-widgets.org/ajax/2.2.1/scripts/window-manager.js b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/scripts/window-manager.js
new file mode 100644
index 0000000..0da5970
--- /dev/null
+++ b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/scripts/window-manager.js
@@ -0,0 +1,414 @@
+/**
+ * @fileOverview UI layers and window-wide dragging
+ * @name SimileAjax.WindowManager
+ */
+
+/**
+ *  This is a singleton that keeps track of UI layers (modal and 
+ *  modeless) and enables/disables UI elements based on which layers
+ *  they belong to. It also provides window-wide dragging 
+ *  implementation.
+ */ 
+SimileAjax.WindowManager = {
+    _initialized:       false,
+    _listeners:         [],
+    
+    _draggedElement:                null,
+    _draggedElementCallback:        null,
+    _dropTargetHighlightElement:    null,
+    _lastCoords:                    null,
+    _ghostCoords:                   null,
+    _draggingMode:                  "",
+    _dragging:                      false,
+    
+    _layers:            []
+};
+
+SimileAjax.WindowManager.initialize = function() {
+    if (SimileAjax.WindowManager._initialized) {
+        return;
+    }
+    
+    SimileAjax.DOM.registerEvent(document.body, "mousedown", SimileAjax.WindowManager._onBodyMouseDown);
+    SimileAjax.DOM.registerEvent(document.body, "mousemove", SimileAjax.WindowManager._onBodyMouseMove);
+    SimileAjax.DOM.registerEvent(document.body, "mouseup",   SimileAjax.WindowManager._onBodyMouseUp);
+    SimileAjax.DOM.registerEvent(document, "keydown",       SimileAjax.WindowManager._onBodyKeyDown);
+    SimileAjax.DOM.registerEvent(document, "keyup",         SimileAjax.WindowManager._onBodyKeyUp);
+    
+    SimileAjax.WindowManager._layers.push({index: 0});
+    
+    SimileAjax.WindowManager._historyListener = {
+        onBeforeUndoSeveral:    function() {},
+        onAfterUndoSeveral:     function() {},
+        onBeforeUndo:           function() {},
+        onAfterUndo:            function() {},
+        
+        onBeforeRedoSeveral:    function() {},
+        onAfterRedoSeveral:     function() {},
+        onBeforeRedo:           function() {},
+        onAfterRedo:            function() {}
+    };
+    SimileAjax.History.addListener(SimileAjax.WindowManager._historyListener);
+    
+    SimileAjax.WindowManager._initialized = true;
+};
+
+SimileAjax.WindowManager.getBaseLayer = function() {
+    SimileAjax.WindowManager.initialize();
+    return SimileAjax.WindowManager._layers[0];
+};
+
+SimileAjax.WindowManager.getHighestLayer = function() {
+    SimileAjax.WindowManager.initialize();
+    return SimileAjax.WindowManager._layers[SimileAjax.WindowManager._layers.length - 1];
+};
+
+SimileAjax.WindowManager.registerEventWithObject = function(elmt, eventName, obj, handlerName, layer) {
+    SimileAjax.WindowManager.registerEvent(
+        elmt, 
+        eventName, 
+        function(elmt2, evt, target) {
+            return obj[handlerName].call(obj, elmt2, evt, target);
+        },
+        layer
+    );
+};
+
+SimileAjax.WindowManager.registerEvent = function(elmt, eventName, handler, layer) {
+    if (layer == null) {
+        layer = SimileAjax.WindowManager.getHighestLayer();
+    }
+    
+    var handler2 = function(elmt, evt, target) {
+        if (SimileAjax.WindowManager._canProcessEventAtLayer(layer)) {
+            SimileAjax.WindowManager._popToLayer(layer.index);
+            try {
+                handler(elmt, evt, target);
+            } catch (e) {
+                SimileAjax.Debug.exception(e);
+            }
+        }
+        SimileAjax.DOM.cancelEvent(evt);
+        return false;
+    }
+    
+    SimileAjax.DOM.registerEvent(elmt, eventName, handler2);
+};
+
+SimileAjax.WindowManager.pushLayer = function(f, ephemeral, elmt) {
+    var layer = { onPop: f, index: SimileAjax.WindowManager._layers.length, ephemeral: (ephemeral), elmt: elmt };
+    SimileAjax.WindowManager._layers.push(layer);
+    
+    return layer;
+};
+
+SimileAjax.WindowManager.popLayer = function(layer) {
+    for (var i = 1; i < SimileAjax.WindowManager._layers.length; i++) {
+        if (SimileAjax.WindowManager._layers[i] == layer) {
+            SimileAjax.WindowManager._popToLayer(i - 1);
+            break;
+        }
+    }
+};
+
+SimileAjax.WindowManager.popAllLayers = function() {
+    SimileAjax.WindowManager._popToLayer(0);
+};
+
+SimileAjax.WindowManager.registerForDragging = function(elmt, callback, layer) {
+    SimileAjax.WindowManager.registerEvent(
+        elmt, 
+        "mousedown", 
+        function(elmt, evt, target) {
+            SimileAjax.WindowManager._handleMouseDown(elmt, evt, callback);
+        }, 
+        layer
+    );
+};
+
+SimileAjax.WindowManager._popToLayer = function(level) {
+    while (level+1 < SimileAjax.WindowManager._layers.length) {
+        try {
+            var layer = SimileAjax.WindowManager._layers.pop();
+            if (layer.onPop != null) {
+                layer.onPop();
+            }
+        } catch (e) {
+        }
+    }
+};
+
+SimileAjax.WindowManager._canProcessEventAtLayer = function(layer) {
+    if (layer.index == (SimileAjax.WindowManager._layers.length - 1)) {
+        return true;
+    }
+    for (var i = layer.index + 1; i < SimileAjax.WindowManager._layers.length; i++) {
+        if (!SimileAjax.WindowManager._layers[i].ephemeral) {
+            return false;
+        }
+    }
+    return true;
+};
+
+SimileAjax.WindowManager.cancelPopups = function(evt) {
+    var evtCoords = (evt) ? SimileAjax.DOM.getEventPageCoordinates(evt) : { x: -1, y: -1 };
+    
+    var i = SimileAjax.WindowManager._layers.length - 1;
+    while (i > 0 && SimileAjax.WindowManager._layers[i].ephemeral) {
+        var layer = SimileAjax.WindowManager._layers[i];
+        if (layer.elmt != null) { // if event falls within main element of layer then don't cancel
+            var elmt = layer.elmt;
+            var elmtCoords = SimileAjax.DOM.getPageCoordinates(elmt);
+            if (evtCoords.x >= elmtCoords.left && evtCoords.x < (elmtCoords.left + elmt.offsetWidth) &&
+                evtCoords.y >= elmtCoords.top && evtCoords.y < (elmtCoords.top + elmt.offsetHeight)) {
+                break;
+            }
+        }
+        i--;
+    }
+    SimileAjax.WindowManager._popToLayer(i);
+};
+
+SimileAjax.WindowManager._onBodyMouseDown = function(elmt, evt, target) {
+    if (!("eventPhase" in evt) || evt.eventPhase == evt.BUBBLING_PHASE) {
+        SimileAjax.WindowManager.cancelPopups(evt);
+    }
+};
+
+SimileAjax.WindowManager._handleMouseDown = function(elmt, evt, callback) {
+    SimileAjax.WindowManager._draggedElement = elmt;
+    SimileAjax.WindowManager._draggedElementCallback = callback;
+    SimileAjax.WindowManager._lastCoords = { x: evt.clientX, y: evt.clientY };
+        
+    SimileAjax.DOM.cancelEvent(evt);
+    return false;
+};
+
+SimileAjax.WindowManager._onBodyKeyDown = function(elmt, evt, target) {
+    if (SimileAjax.WindowManager._dragging) {
+        if (evt.keyCode == 27) { // esc
+            SimileAjax.WindowManager._cancelDragging();
+        } else if ((evt.keyCode == 17 || evt.keyCode == 16) && SimileAjax.WindowManager._draggingMode != "copy") {
+            SimileAjax.WindowManager._draggingMode = "copy";
+            
+            var img = SimileAjax.Graphics.createTranslucentImage(SimileAjax.urlPrefix + "images/copy.png");
+            img.style.position = "absolute";
+            img.style.left = (SimileAjax.WindowManager._ghostCoords.left - 16) + "px";
+            img.style.top = (SimileAjax.WindowManager._ghostCoords.top) + "px";
+            document.body.appendChild(img);
+            
+            SimileAjax.WindowManager._draggingModeIndicatorElmt = img;
+        }
+    }
+};
+
+SimileAjax.WindowManager._onBodyKeyUp = function(elmt, evt, target) {
+    if (SimileAjax.WindowManager._dragging) {
+        if (evt.keyCode == 17 || evt.keyCode == 16) {
+            SimileAjax.WindowManager._draggingMode = "";
+            if (SimileAjax.WindowManager._draggingModeIndicatorElmt != null) {
+                document.body.removeChild(SimileAjax.WindowManager._draggingModeIndicatorElmt);
+                SimileAjax.WindowManager._draggingModeIndicatorElmt = null;
+            }
+        }
+    }
+};
+
+SimileAjax.WindowManager._onBodyMouseMove = function(elmt, evt, target) {
+    if (SimileAjax.WindowManager._draggedElement != null) {
+        var callback = SimileAjax.WindowManager._draggedElementCallback;
+        
+        var lastCoords = SimileAjax.WindowManager._lastCoords;
+        var diffX = evt.clientX - lastCoords.x;
+        var diffY = evt.clientY - lastCoords.y;
+        
+        if (!SimileAjax.WindowManager._dragging) {
+            if (Math.abs(diffX) > 5 || Math.abs(diffY) > 5) {
+                try {
+                    if ("onDragStart" in callback) {
+                        callback.onDragStart();
+                    }
+                    
+                    if ("ghost" in callback && callback.ghost) {
+                        var draggedElmt = SimileAjax.WindowManager._draggedElement;
+                        
+                        SimileAjax.WindowManager._ghostCoords = SimileAjax.DOM.getPageCoordinates(draggedElmt);
+                        SimileAjax.WindowManager._ghostCoords.left += diffX;
+                        SimileAjax.WindowManager._ghostCoords.top += diffY;
+                        
+                        var ghostElmt = draggedElmt.cloneNode(true);
+                        ghostElmt.style.position = "absolute";
+                        ghostElmt.style.left = SimileAjax.WindowManager._ghostCoords.left + "px";
+                        ghostElmt.style.top = SimileAjax.WindowManager._ghostCoords.top + "px";
+                        ghostElmt.style.zIndex = 1000;
+                        SimileAjax.Graphics.setOpacity(ghostElmt, 50);
+                        
+                        document.body.appendChild(ghostElmt);
+                        callback._ghostElmt = ghostElmt;
+                    }
+                    
+                    SimileAjax.WindowManager._dragging = true;
+                    SimileAjax.WindowManager._lastCoords = { x: evt.clientX, y: evt.clientY };
+                    
+                    document.body.focus();
+                } catch (e) {
+                    SimileAjax.Debug.exception("WindowManager: Error handling mouse down", e);
+                    SimileAjax.WindowManager._cancelDragging();
+                }
+            }
+        } else {
+            try {
+                SimileAjax.WindowManager._lastCoords = { x: evt.clientX, y: evt.clientY };
+                
+                if ("onDragBy" in callback) {
+                    callback.onDragBy(diffX, diffY);
+                }
+                
+                if ("_ghostElmt" in callback) {
+                    var ghostElmt = callback._ghostElmt;
+                    
+                    SimileAjax.WindowManager._ghostCoords.left += diffX;
+                    SimileAjax.WindowManager._ghostCoords.top += diffY;
+                    
+                    ghostElmt.style.left = SimileAjax.WindowManager._ghostCoords.left + "px";
+                    ghostElmt.style.top = SimileAjax.WindowManager._ghostCoords.top + "px";
+                    if (SimileAjax.WindowManager._draggingModeIndicatorElmt != null) {
+                        var indicatorElmt = SimileAjax.WindowManager._draggingModeIndicatorElmt;
+                        
+                        indicatorElmt.style.left = (SimileAjax.WindowManager._ghostCoords.left - 16) + "px";
+                        indicatorElmt.style.top = SimileAjax.WindowManager._ghostCoords.top + "px";
+                    }
+                    
+                    if ("droppable" in callback && callback.droppable) {
+                        var coords = SimileAjax.DOM.getEventPageCoordinates(evt);
+                        var target = SimileAjax.DOM.hittest(
+                            coords.x, coords.y, 
+                            [   SimileAjax.WindowManager._ghostElmt, 
+                                SimileAjax.WindowManager._dropTargetHighlightElement 
+                            ]
+                        );
+                        target = SimileAjax.WindowManager._findDropTarget(target);
+                        
+                        if (target != SimileAjax.WindowManager._potentialDropTarget) {
+                            if (SimileAjax.WindowManager._dropTargetHighlightElement != null) {
+                                document.body.removeChild(SimileAjax.WindowManager._dropTargetHighlightElement);
+                                
+                                SimileAjax.WindowManager._dropTargetHighlightElement = null;
+                                SimileAjax.WindowManager._potentialDropTarget = null;
+                            }
+
+                            var droppable = false;
+                            if (target != null) {
+                                if ((!("canDropOn" in callback) || callback.canDropOn(target)) &&
+                                    (!("canDrop" in target) || target.canDrop(SimileAjax.WindowManager._draggedElement))) {
+                                    
+                                    droppable = true;
+                                }
+                            }
+                            
+                            if (droppable) {
+                                var border = 4;
+                                var targetCoords = SimileAjax.DOM.getPageCoordinates(target);
+                                var highlight = document.createElement("div");
+                                highlight.style.border = border + "px solid yellow";
+                                highlight.style.backgroundColor = "yellow";
+                                highlight.style.position = "absolute";
+                                highlight.style.left = targetCoords.left + "px";
+                                highlight.style.top = targetCoords.top + "px";
+                                highlight.style.width = (target.offsetWidth - border * 2) + "px";
+                                highlight.style.height = (target.offsetHeight - border * 2) + "px";
+                                SimileAjax.Graphics.setOpacity(highlight, 30);
+                                document.body.appendChild(highlight);
+                                
+                                SimileAjax.WindowManager._potentialDropTarget = target;
+                                SimileAjax.WindowManager._dropTargetHighlightElement = highlight;
+                            }
+                        }
+                    }
+                }
+            } catch (e) {
+                SimileAjax.Debug.exception("WindowManager: Error handling mouse move", e);
+                SimileAjax.WindowManager._cancelDragging();
+            }
+        }
+        
+        SimileAjax.DOM.cancelEvent(evt);
+        return false;
+    }
+};
+
+SimileAjax.WindowManager._onBodyMouseUp = function(elmt, evt, target) {
+    if (SimileAjax.WindowManager._draggedElement != null) {
+        try {
+            if (SimileAjax.WindowManager._dragging) {
+                var callback = SimileAjax.WindowManager._draggedElementCallback;
+                if ("onDragEnd" in callback) {
+                    callback.onDragEnd();
+                }
+                if ("droppable" in callback && callback.droppable) {
+                    var dropped = false;
+                    
+                    var target = SimileAjax.WindowManager._potentialDropTarget;
+                    if (target != null) {
+                        if ((!("canDropOn" in callback) || callback.canDropOn(target)) &&
+                            (!("canDrop" in target) || target.canDrop(SimileAjax.WindowManager._draggedElement))) {
+                            
+                            if ("onDropOn" in callback) {
+                                callback.onDropOn(target);
+                            }
+                            target.ondrop(SimileAjax.WindowManager._draggedElement, SimileAjax.WindowManager._draggingMode);
+                            
+                            dropped = true;
+                        }
+                    }
+                    
+                    if (!dropped) {
+                        // TODO: do holywood explosion here
+                    }
+                }
+            }
+        } finally {
+            SimileAjax.WindowManager._cancelDragging();
+        }
+        
+        SimileAjax.DOM.cancelEvent(evt);
+        return false;
+    }
+};
+
+SimileAjax.WindowManager._cancelDragging = function() {
+    var callback = SimileAjax.WindowManager._draggedElementCallback;
+    if ("_ghostElmt" in callback) {
+        var ghostElmt = callback._ghostElmt;
+        document.body.removeChild(ghostElmt);
+        
+        delete callback._ghostElmt;
+    }
+    if (SimileAjax.WindowManager._dropTargetHighlightElement != null) {
+        document.body.removeChild(SimileAjax.WindowManager._dropTargetHighlightElement);
+        SimileAjax.WindowManager._dropTargetHighlightElement = null;
+    }
+    if (SimileAjax.WindowManager._draggingModeIndicatorElmt != null) {
+        document.body.removeChild(SimileAjax.WindowManager._draggingModeIndicatorElmt);
+        SimileAjax.WindowManager._draggingModeIndicatorElmt = null;
+    }
+    
+    SimileAjax.WindowManager._draggedElement = null;
+    SimileAjax.WindowManager._draggedElementCallback = null;
+    SimileAjax.WindowManager._potentialDropTarget = null;
+    SimileAjax.WindowManager._dropTargetHighlightElement = null;
+    SimileAjax.WindowManager._lastCoords = null;
+    SimileAjax.WindowManager._ghostCoords = null;
+    SimileAjax.WindowManager._draggingMode = "";
+    SimileAjax.WindowManager._dragging = false;
+};
+
+SimileAjax.WindowManager._findDropTarget = function(elmt) {
+    while (elmt != null) {
+        if ("ondrop" in elmt && (typeof elmt.ondrop) == "function") {
+            break;
+        }
+        elmt = elmt.parentNode;
+    }
+    return elmt;
+};
diff --git a/assets/scripts/api.simile-widgets.org/ajax/2.2.1/scripts/xmlhttp.js b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/scripts/xmlhttp.js
new file mode 100644
index 0000000..b6d2c0a
--- /dev/null
+++ b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/scripts/xmlhttp.js
@@ -0,0 +1,137 @@
+/**
+ * @fileOverview XmlHttp utility functions
+ * @name SimileAjax.XmlHttp
+ */
+
+SimileAjax.XmlHttp = new Object();
+
+/**
+ *  Callback for XMLHttp onRequestStateChange.
+ */
+SimileAjax.XmlHttp._onReadyStateChange = function(xmlhttp, fError, fDone) {
+    switch (xmlhttp.readyState) {
+    // 1: Request not yet made
+    // 2: Contact established with server but nothing downloaded yet
+    // 3: Called multiple while downloading in progress
+    
+    // Download complete
+    case 4:
+        try {
+            if (xmlhttp.status == 0     // file:// urls, works on Firefox
+             || xmlhttp.status == 200   // http:// urls
+            ) {
+                if (fDone) {
+                    fDone(xmlhttp);
+                }
+            } else {
+                if (fError) {
+                    fError(
+                        xmlhttp.statusText,
+                        xmlhttp.status,
+                        xmlhttp
+                    );
+                }
+            }
+        } catch (e) {
+            SimileAjax.Debug.exception("XmlHttp: Error handling onReadyStateChange", e);
+        }
+        break;
+    }
+};
+
+/**
+ *  Creates an XMLHttpRequest object. On the first run, this
+ *  function creates a platform-specific function for
+ *  instantiating an XMLHttpRequest object and then replaces
+ *  itself with that function.
+ */
+SimileAjax.XmlHttp._createRequest = function() {
+    if (SimileAjax.Platform.browser.isIE) {
+        var programIDs = [
+        "Msxml2.XMLHTTP",
+        "Microsoft.XMLHTTP",
+        "Msxml2.XMLHTTP.4.0"
+        ];
+        for (var i = 0; i < programIDs.length; i++) {
+            try {
+                var programID = programIDs[i];
+                var f = function() {
+                    return new ActiveXObject(programID);
+                };
+                var o = f();
+                
+                // We are replacing the SimileAjax._createXmlHttpRequest
+                // function with this inner function as we've
+                // found out that it works. This is so that we
+                // don't have to do all the testing over again
+                // on subsequent calls.
+                SimileAjax.XmlHttp._createRequest = f;
+                
+                return o;
+            } catch (e) {
+                // silent
+            }
+        }
+        // fall through to try new XMLHttpRequest();
+    }
+
+    try {
+        var f = function() {
+            return new XMLHttpRequest();
+        };
+        var o = f();
+        
+        // We are replacing the SimileAjax._createXmlHttpRequest
+        // function with this inner function as we've
+        // found out that it works. This is so that we
+        // don't have to do all the testing over again
+        // on subsequent calls.
+        SimileAjax.XmlHttp._createRequest = f;
+        
+        return o;
+    } catch (e) {
+        throw new Error("Failed to create an XMLHttpRequest object");
+    }
+};
+
+/**
+ * Performs an asynchronous HTTP GET.
+ *  
+ * @param {Function} fError a function of the form 
+     function(statusText, statusCode, xmlhttp)
+ * @param {Function} fDone a function of the form function(xmlhttp)
+ */
+SimileAjax.XmlHttp.get = function(url, fError, fDone) {
+    var xmlhttp = SimileAjax.XmlHttp._createRequest();
+    
+    xmlhttp.open("GET", url, true);
+    xmlhttp.onreadystatechange = function() {
+        SimileAjax.XmlHttp._onReadyStateChange(xmlhttp, fError, fDone);
+    };
+    xmlhttp.send(null);
+};
+
+/**
+ * Performs an asynchronous HTTP POST.
+ *  
+ * @param {Function} fError a function of the form 
+     function(statusText, statusCode, xmlhttp)
+ * @param {Function} fDone a function of the form function(xmlhttp)
+ */
+SimileAjax.XmlHttp.post = function(url, body, fError, fDone) {
+    var xmlhttp = SimileAjax.XmlHttp._createRequest();
+    
+    xmlhttp.open("POST", url, true);
+    xmlhttp.onreadystatechange = function() {
+        SimileAjax.XmlHttp._onReadyStateChange(xmlhttp, fError, fDone);
+    };
+    xmlhttp.send(body);
+};
+
+SimileAjax.XmlHttp._forceXML = function(xmlhttp) {
+    try {
+        xmlhttp.overrideMimeType("text/xml");
+    } catch (e) {
+        xmlhttp.setrequestheader("Content-Type", "text/xml");
+    }
+};
\ No newline at end of file
diff --git a/assets/scripts/api.simile-widgets.org/ajax/2.2.1/simile-ajax-api.js b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/simile-ajax-api.js
new file mode 100644
index 0000000..d1347d8
--- /dev/null
+++ b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/simile-ajax-api.js
@@ -0,0 +1,214 @@
+/*==================================================
+ *  Simile Ajax API
+ *
+ *  Include this file in your HTML file as follows:
+ *
+ *    <script src="http://simile.mit.edu/ajax/api/simile-ajax-api.js" type="text/javascript"></script>
+ *
+ *==================================================
+ */
+
+if (typeof SimileAjax == "undefined") {
+    var SimileAjax = {
+        loaded:                 false,
+        loadingScriptsCount:    0,
+        error:                  null,
+        params:                 { bundle:"true" }
+    };
+    
+    SimileAjax.Platform = new Object();
+        /*
+            HACK: We need these 2 things here because we cannot simply append
+            a <script> element containing code that accesses SimileAjax.Platform
+            to initialize it because IE executes that <script> code first
+            before it loads ajax.js and platform.js.
+        */
+        
+    var getHead = function(doc) {
+        return doc.getElementsByTagName("head")[0];
+    };
+    
+    SimileAjax.findScript = function(doc, substring) {
+        var heads = doc.documentElement.getElementsByTagName("head");
+        for (var h = 0; h < heads.length; h++) {
+            var node = heads[h].firstChild;
+            while (node != null) {
+                if (node.nodeType == 1 && node.tagName.toLowerCase() == "script") {
+                    var url = node.src;
+                    var i = url.indexOf(substring);
+                    if (i >= 0) {
+                        return url;
+                    }
+                }
+                node = node.nextSibling;
+            }
+        }
+        return null;
+    };
+    SimileAjax.includeJavascriptFile = function(doc, url, onerror, charset) {
+        onerror = onerror || "";
+        if (doc.body == null) {
+            try {
+                var q = "'" + onerror.replace( /'/g, '&apos' ) + "'"; // "
+                doc.write("<script src='" + url + "' onerror="+ q +
+                          (charset ? " charset='"+ charset +"'" : "") +
+                          " type='text/javascript'>"+ onerror + "</script>");
+                return;
+            } catch (e) {
+                // fall through
+            }
+        }
+
+        var script = doc.createElement("script");
+        if (onerror) {
+            try { script.innerHTML = onerror; } catch(e) {}
+            script.setAttribute("onerror", onerror);
+        }
+        if (charset) {
+            script.setAttribute("charset", charset);
+        }
+        script.type = "text/javascript";
+        script.language = "JavaScript";
+        script.src = url;
+        return getHead(doc).appendChild(script);
+    };
+    SimileAjax.includeJavascriptFiles = function(doc, urlPrefix, filenames) {
+        for (var i = 0; i < filenames.length; i++) {
+            SimileAjax.includeJavascriptFile(doc, urlPrefix + filenames[i]);
+        }
+        SimileAjax.loadingScriptsCount += filenames.length;
+        SimileAjax.includeJavascriptFile(doc, SimileAjax.urlPrefix + "scripts/signal.js?" + filenames.length);
+    };
+    SimileAjax.includeCssFile = function(doc, url) {
+        if (doc.body == null) {
+            try {
+                doc.write("<link rel='stylesheet' href='" + url + "' type='text/css'/>");
+                return;
+            } catch (e) {
+                // fall through
+            }
+        }
+        
+        var link = doc.createElement("link");
+        link.setAttribute("rel", "stylesheet");
+        link.setAttribute("type", "text/css");
+        link.setAttribute("href", url);
+        getHead(doc).appendChild(link);
+    };
+    SimileAjax.includeCssFiles = function(doc, urlPrefix, filenames) {
+        for (var i = 0; i < filenames.length; i++) {
+            SimileAjax.includeCssFile(doc, urlPrefix + filenames[i]);
+        }
+    };
+    
+    /**
+     * Append into urls each string in suffixes after prefixing it with urlPrefix.
+     * @param {Array} urls
+     * @param {String} urlPrefix
+     * @param {Array} suffixes
+     */
+    SimileAjax.prefixURLs = function(urls, urlPrefix, suffixes) {
+        for (var i = 0; i < suffixes.length; i++) {
+            urls.push(urlPrefix + suffixes[i]);
+        }
+    };
+
+    /**
+     * Parse out the query parameters from a URL
+     * @param {String} url    the url to parse, or location.href if undefined
+     * @param {Object} to     optional object to extend with the parameters
+     * @param {Object} types  optional object mapping keys to value types
+     *        (String, Number, Boolean or Array, String by default)
+     * @return a key/value Object whose keys are the query parameter names
+     * @type Object
+     */
+    SimileAjax.parseURLParameters = function(url, to, types) {
+        to = to || {};
+        types = types || {};
+        
+        if (typeof url == "undefined") {
+            url = location.href;
+        }
+        var q = url.indexOf("?");
+        if (q < 0) {
+            return to;
+        }
+        url = (url+"#").slice(q+1, url.indexOf("#")); // toss the URL fragment
+        
+        var params = url.split("&"), param, parsed = {};
+        var decode = window.decodeURIComponent || unescape;
+        for (var i = 0; param = params[i]; i++) {
+            var eq = param.indexOf("=");
+            var name = decode(param.slice(0,eq));
+            var old = parsed[name];
+            if (typeof old == "undefined") {
+                old = [];
+            } else if (!(old instanceof Array)) {
+                old = [old];
+            }
+            parsed[name] = old.concat(decode(param.slice(eq+1)));
+        }
+        for (var i in parsed) {
+            if (!parsed.hasOwnProperty(i)) continue;
+            var type = types[i] || String;
+            var data = parsed[i];
+            if (!(data instanceof Array)) {
+                data = [data];
+            }
+            if (type === Boolean && data[0] == "false") {
+                to[i] = false; // because Boolean("false") === true
+            } else {
+                to[i] = type.apply(this, data);
+            }
+        }
+        return to;
+    };
+
+    (function() {
+        var javascriptFiles = [
+            "platform.js",
+            "debug.js",
+            "xmlhttp.js",
+            "json.js",
+            "dom.js",
+            "graphics.js",
+            "date-time.js",
+            "string.js",
+            "html.js",
+            "data-structure.js",
+            "units.js",
+            
+            "ajax.js",
+            "history.js",
+            "window-manager.js"
+        ];
+        var cssFiles = [
+            "graphics.css"
+        ];
+        if (!("jQuery" in window) && !("$" in window)) {
+        	javascriptFiles.unshift("jquery-1.3.2.min.js");
+        }
+        
+        if (typeof SimileAjax_urlPrefix == "string") {
+            SimileAjax.urlPrefix = SimileAjax_urlPrefix;
+        } else {
+            var url = SimileAjax.findScript(document, "simile-ajax-api.js");
+            if (url == null) {
+                SimileAjax.error = new Error("Failed to derive URL prefix for Simile Ajax API code files");
+                return;
+            }
+
+            SimileAjax.urlPrefix = url.substr(0, url.indexOf("simile-ajax-api.js"));
+        }
+
+        SimileAjax.parseURLParameters(url, SimileAjax.params, {bundle:Boolean});
+        if (SimileAjax.params.bundle) {
+            SimileAjax.includeJavascriptFiles(document, SimileAjax.urlPrefix, [ "simile-ajax-bundle.js" ]);
+        } else {
+            SimileAjax.includeJavascriptFiles(document, SimileAjax.urlPrefix + "scripts/", javascriptFiles);
+        }
+        SimileAjax.includeCssFiles(document, SimileAjax.urlPrefix + "styles/", cssFiles);
+        
+        SimileAjax.loaded = true;
+    })();
+}
diff --git a/assets/scripts/api.simile-widgets.org/ajax/2.2.1/simile-ajax-bundle.js b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/simile-ajax-bundle.js
new file mode 100644
index 0000000..d42e209
--- /dev/null
+++ b/assets/scripts/api.simile-widgets.org/ajax/2.2.1/simile-ajax-bundle.js
@@ -0,0 +1,2908 @@
+
+
+/* jquery-1.3.2.min.js */
+if(!("jQuery" in window)&&!("$" in window)){(function(){var W=this,AB,F=W.jQuery,S=W.$,T=W.jQuery=W.$=function(B,A){return new T.fn.init(B,A);
+},M=/^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,AC=/^.[^:#\[\.,]*$/;
+T.fn=T.prototype={init:function(a,B){a=a||document;
+if(a.nodeType){this[0]=a;
+this.length=1;
+this.context=a;
+return this;
+}if(typeof a==="string"){var C=M.exec(a);
+if(C&&(C[1]||!B)){if(C[1]){a=T.clean([C[1]],B);
+}else{var A=document.getElementById(C[3]);
+if(A&&A.id!=C[3]){return T().find(a);
+}var D=T(A||[]);
+D.context=document;
+D.selector=a;
+return D;
+}}else{return T(B).find(a);
+}}else{if(T.isFunction(a)){return T(document).ready(a);
+}}if(a.selector&&a.context){this.selector=a.selector;
+this.context=a.context;
+}return this.setArray(T.isArray(a)?a:T.makeArray(a));
+},selector:"",jquery:"1.3.2",size:function(){return this.length;
+},get:function(A){return A===AB?Array.prototype.slice.call(this):this[A];
+},pushStack:function(C,A,D){var B=T(C);
+B.prevObject=this;
+B.context=this.context;
+if(A==="find"){B.selector=this.selector+(this.selector?" ":"")+D;
+}else{if(A){B.selector=this.selector+"."+A+"("+D+")";
+}}return B;
+},setArray:function(A){this.length=0;
+Array.prototype.push.apply(this,A);
+return this;
+},each:function(A,B){return T.each(this,A,B);
+},index:function(A){return T.inArray(A&&A.jquery?A[0]:A,this);
+},attr:function(C,A,B){var D=C;
+if(typeof C==="string"){if(A===AB){return this[0]&&T[B||"attr"](this[0],C);
+}else{D={};
+D[C]=A;
+}}return this.each(function(a){for(C in D){T.attr(B?this.style:this,C,T.prop(this,D[C],B,a,C));
+}});
+},css:function(B,A){if((B=="width"||B=="height")&&parseFloat(A)<0){A=AB;
+}return this.attr(B,A,"curCSS");
+},text:function(A){if(typeof A!=="object"&&A!=null){return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(A));
+}var B="";
+T.each(A||this,function(){T.each(this.childNodes,function(){if(this.nodeType!=8){B+=this.nodeType!=1?this.nodeValue:T.fn.text([this]);
+}});
+});
+return B;
+},wrapAll:function(B){if(this[0]){var A=T(B,this[0].ownerDocument).clone();
+if(this[0].parentNode){A.insertBefore(this[0]);
+}A.map(function(){var C=this;
+while(C.firstChild){C=C.firstChild;
+}return C;
+}).append(this);
+}return this;
+},wrapInner:function(A){return this.each(function(){T(this).contents().wrapAll(A);
+});
+},wrap:function(A){return this.each(function(){T(this).wrapAll(A);
+});
+},append:function(){return this.domManip(arguments,true,function(A){if(this.nodeType==1){this.appendChild(A);
+}});
+},prepend:function(){return this.domManip(arguments,true,function(A){if(this.nodeType==1){this.insertBefore(A,this.firstChild);
+}});
+},before:function(){return this.domManip(arguments,false,function(A){this.parentNode.insertBefore(A,this);
+});
+},after:function(){return this.domManip(arguments,false,function(A){this.parentNode.insertBefore(A,this.nextSibling);
+});
+},end:function(){return this.prevObject||T([]);
+},push:[].push,sort:[].sort,splice:[].splice,find:function(B){if(this.length===1){var A=this.pushStack([],"find",B);
+A.length=0;
+T.find(B,this[0],A);
+return A;
+}else{return this.pushStack(T.unique(T.map(this,function(C){return T.find(B,C);
+})),"find",B);
+}},clone:function(B){var D=this.map(function(){if(!T.support.noCloneEvent&&!T.isXMLDoc(this)){var b=this.outerHTML;
+if(!b){var a=this.ownerDocument.createElement("div");
+a.appendChild(this.cloneNode(true));
+b=a.innerHTML;
+}return T.clean([b.replace(/ jQuery\d+="(?:\d+|null)"/g,"").replace(/^\s*/,"")])[0];
+}else{return this.cloneNode(true);
+}});
+if(B===true){var A=this.find("*").andSelf(),C=0;
+D.find("*").andSelf().each(function(){if(this.nodeName!==A[C].nodeName){return ;
+}var c=T.data(A[C],"events");
+for(var a in c){for(var b in c[a]){T.event.add(this,a,c[a][b],c[a][b].data);
+}}C++;
+});
+}return D;
+},filter:function(A){return this.pushStack(T.isFunction(A)&&T.grep(this,function(B,C){return A.call(B,C);
+})||T.multiFilter(A,T.grep(this,function(B){return B.nodeType===1;
+})),"filter",A);
+},closest:function(C){var A=T.expr.match.POS.test(C)?T(C):null,B=0;
+return this.map(function(){var D=this;
+while(D&&D.ownerDocument){if(A?A.index(D)>-1:T(D).is(C)){T.data(D,"closest",B);
+return D;
+}D=D.parentNode;
+B++;
+}});
+},not:function(B){if(typeof B==="string"){if(AC.test(B)){return this.pushStack(T.multiFilter(B,this,true),"not",B);
+}else{B=T.multiFilter(B,this);
+}}var A=B.length&&B[B.length-1]!==AB&&!B.nodeType;
+return this.filter(function(){return A?T.inArray(this,B)<0:this!=B;
+});
+},add:function(A){return this.pushStack(T.unique(T.merge(this.get(),typeof A==="string"?T(A):T.makeArray(A))));
+},is:function(A){return !!A&&T.multiFilter(A,this).length>0;
+},hasClass:function(A){return !!A&&this.is("."+A);
+},val:function(C){if(C===AB){var e=this[0];
+if(e){if(T.nodeName(e,"option")){return(e.attributes.value||{}).specified?e.value:e.text;
+}if(T.nodeName(e,"select")){var a=e.selectedIndex,B=[],A=e.options,b=e.type=="select-one";
+if(a<0){return null;
+}for(var d=b?a:0,D=b?a+1:A.length;
+d<D;
+d++){var c=A[d];
+if(c.selected){C=T(c).val();
+if(b){return C;
+}B.push(C);
+}}return B;
+}return(e.value||"").replace(/\r/g,"");
+}return AB;
+}if(typeof C==="number"){C+="";
+}return this.each(function(){if(this.nodeType!=1){return ;
+}if(T.isArray(C)&&/radio|checkbox/.test(this.type)){this.checked=(T.inArray(this.value,C)>=0||T.inArray(this.name,C)>=0);
+}else{if(T.nodeName(this,"select")){var f=T.makeArray(C);
+T("option",this).each(function(){this.selected=(T.inArray(this.value,f)>=0||T.inArray(this.text,f)>=0);
+});
+if(!f.length){this.selectedIndex=-1;
+}}else{this.value=C;
+}}});
+},html:function(A){return A===AB?(this[0]?this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g,""):null):this.empty().append(A);
+},replaceWith:function(A){return this.after(A).remove();
+},eq:function(A){return this.slice(A,+A+1);
+},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments),"slice",Array.prototype.slice.call(arguments).join(","));
+},map:function(A){return this.pushStack(T.map(this,function(B,C){return A.call(B,C,B);
+}));
+},andSelf:function(){return this.add(this.prevObject);
+},domManip:function(D,A,B){if(this[0]){var a=(this[0].ownerDocument||this[0]).createDocumentFragment(),d=T.clean(D,(this[0].ownerDocument||this[0]),a),b=a.firstChild;
+if(b){for(var c=0,e=this.length;
+c<e;
+c++){B.call(C(this[c],b),this.length>1||c>0?a.cloneNode(true):a);
+}}if(d){T.each(d,E);
+}}return this;
+function C(g,f){return A&&T.nodeName(g,"table")&&T.nodeName(f,"tr")?(g.getElementsByTagName("tbody")[0]||g.appendChild(g.ownerDocument.createElement("tbody"))):g;
+}}};
+T.fn.init.prototype=T.fn;
+function E(B,A){if(A.src){T.ajax({url:A.src,async:false,dataType:"script"});
+}else{T.globalEval(A.text||A.textContent||A.innerHTML||"");
+}if(A.parentNode){A.parentNode.removeChild(A);
+}}function AD(){return +new Date;
+}T.extend=T.fn.extend=function(){var C=arguments[0]||{},a=1,D=arguments.length,d=false,b;
+if(typeof C==="boolean"){d=C;
+C=arguments[1]||{};
+a=2;
+}if(typeof C!=="object"&&!T.isFunction(C)){C={};
+}if(D==a){C=this;
+--a;
+}for(;
+a<D;
+a++){if((b=arguments[a])!=null){for(var c in b){var B=C[c],A=b[c];
+if(C===A){continue;
+}if(d&&A&&typeof A==="object"&&!A.nodeType){C[c]=T.extend(d,B||(A.length!=null?[]:{}),A);
+}else{if(A!==AB){C[c]=A;
+}}}}}return C;
+};
+var AG=/z-?index|font-?weight|opacity|zoom|line-?height/i,Q=document.defaultView||{},L=Object.prototype.toString;
+T.extend({noConflict:function(A){W.$=S;
+if(A){W.jQuery=F;
+}return T;
+},isFunction:function(A){return L.call(A)==="[object Function]";
+},isArray:function(A){return L.call(A)==="[object Array]";
+},isXMLDoc:function(A){return A.nodeType===9&&A.documentElement.nodeName!=="HTML"||!!A.ownerDocument&&T.isXMLDoc(A.ownerDocument);
+},globalEval:function(A){if(A&&/\S/.test(A)){var B=document.getElementsByTagName("head")[0]||document.documentElement,C=document.createElement("script");
+C.type="text/javascript";
+if(T.support.scriptEval){C.appendChild(document.createTextNode(A));
+}else{C.text=A;
+}B.insertBefore(C,B.firstChild);
+B.removeChild(C);
+}},nodeName:function(A,B){return A.nodeName&&A.nodeName.toUpperCase()==B.toUpperCase();
+},each:function(a,A,b){var c,D=0,C=a.length;
+if(b){if(C===AB){for(c in a){if(A.apply(a[c],b)===false){break;
+}}}else{for(;
+D<C;
+){if(A.apply(a[D++],b)===false){break;
+}}}}else{if(C===AB){for(c in a){if(A.call(a[c],c,a[c])===false){break;
+}}}else{for(var B=a[0];
+D<C&&A.call(B,D,B)!==false;
+B=a[++D]){}}}return a;
+},prop:function(B,A,C,D,a){if(T.isFunction(A)){A=A.call(B,D);
+}return typeof A==="number"&&C=="curCSS"&&!AG.test(a)?A+"px":A;
+},className:{add:function(B,A){T.each((A||"").split(/\s+/),function(D,C){if(B.nodeType==1&&!T.className.has(B.className,C)){B.className+=(B.className?" ":"")+C;
+}});
+},remove:function(B,A){if(B.nodeType==1){B.className=A!==AB?T.grep(B.className.split(/\s+/),function(C){return !T.className.has(A,C);
+}).join(" "):"";
+}},has:function(A,B){return A&&T.inArray(B,(A.className||A).toString().split(/\s+/))>-1;
+}},swap:function(B,C,A){var a={};
+for(var D in C){a[D]=B.style[D];
+B.style[D]=C[D];
+}A.call(B);
+for(var D in C){B.style[D]=a[D];
+}},css:function(a,c,C,d){if(c=="width"||c=="height"){var A,b={position:"absolute",visibility:"hidden",display:"block"},B=c=="width"?["Left","Right"]:["Top","Bottom"];
+function D(){A=c=="width"?a.offsetWidth:a.offsetHeight;
+if(d==="border"){return ;
+}T.each(B,function(){if(!d){A-=parseFloat(T.curCSS(a,"padding"+this,true))||0;
+}if(d==="margin"){A+=parseFloat(T.curCSS(a,"margin"+this,true))||0;
+}else{A-=parseFloat(T.curCSS(a,"border"+this+"Width",true))||0;
+}});
+}if(a.offsetWidth!==0){D();
+}else{T.swap(a,b,D);
+}return Math.max(0,Math.round(A));
+}return T.curCSS(a,c,C);
+},curCSS:function(a,d,c){var B,e=a.style;
+if(d=="opacity"&&!T.support.opacity){B=T.attr(e,"opacity");
+return B==""?"1":B;
+}if(d.match(/float/i)){d=H;
+}if(!c&&e&&e[d]){B=e[d];
+}else{if(Q.getComputedStyle){if(d.match(/float/i)){d="float";
+}d=d.replace(/([A-Z])/g,"-$1").toLowerCase();
+var A=Q.getComputedStyle(a,null);
+if(A){B=A.getPropertyValue(d);
+}if(d=="opacity"&&B==""){B="1";
+}}else{if(a.currentStyle){var D=d.replace(/\-(\w)/g,function(g,f){return f.toUpperCase();
+});
+B=a.currentStyle[d]||a.currentStyle[D];
+if(!/^\d+(px)?$/i.test(B)&&/^\d/.test(B)){var b=e.left,C=a.runtimeStyle.left;
+a.runtimeStyle.left=a.currentStyle.left;
+e.left=B||0;
+B=e.pixelLeft+"px";
+e.left=b;
+a.runtimeStyle.left=C;
+}}}}return B;
+},clean:function(c,B,D){B=B||document;
+if(typeof B.createElement==="undefined"){B=B.ownerDocument||B[0]&&B[0].ownerDocument||document;
+}if(!D&&c.length===1&&typeof c[0]==="string"){var a=/^<(\w+)\s*\/?>$/.exec(c[0]);
+if(a){return[B.createElement(a[1])];
+}}var b=[],d=[],A=B.createElement("div");
+T.each(c,function(h,e){if(typeof e==="number"){e+="";
+}if(!e){return ;
+}if(typeof e==="string"){e=e.replace(/(<(\w+)[^>]*?)\/>/g,function(m,l,n){return n.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?m:l+"></"+n+">";
+});
+var i=e.replace(/^\s+/,"").substring(0,10).toLowerCase();
+var g=!i.indexOf("<opt")&&[1,"<select multiple='multiple'>","</select>"]||!i.indexOf("<leg")&&[1,"<fieldset>","</fieldset>"]||i.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"<table>","</table>"]||!i.indexOf("<tr")&&[2,"<table><tbody>","</tbody></table>"]||(!i.indexOf("<td")||!i.indexOf("<th"))&&[3,"<table><tbody><tr>","</tr></tbody></table>"]||!i.indexOf("<col")&&[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"]||!T.support.htmlSerialize&&[1,"div<div>","</div>"]||[0,"",""];
+A.innerHTML=g[1]+e+g[2];
+while(g[0]--){A=A.lastChild;
+}if(!T.support.tbody){var f=/<tbody/i.test(e),j=!i.indexOf("<table")&&!f?A.firstChild&&A.firstChild.childNodes:g[1]=="<table>"&&!f?A.childNodes:[];
+for(var k=j.length-1;
+k>=0;
+--k){if(T.nodeName(j[k],"tbody")&&!j[k].childNodes.length){j[k].parentNode.removeChild(j[k]);
+}}}if(!T.support.leadingWhitespace&&/^\s/.test(e)){A.insertBefore(B.createTextNode(e.match(/^\s*/)[0]),A.firstChild);
+}e=T.makeArray(A.childNodes);
+}if(e.nodeType){b.push(e);
+}else{b=T.merge(b,e);
+}});
+if(D){for(var C=0;
+b[C];
+C++){if(T.nodeName(b[C],"script")&&(!b[C].type||b[C].type.toLowerCase()==="text/javascript")){d.push(b[C].parentNode?b[C].parentNode.removeChild(b[C]):b[C]);
+}else{if(b[C].nodeType===1){b.splice.apply(b,[C+1,0].concat(T.makeArray(b[C].getElementsByTagName("script"))));
+}D.appendChild(b[C]);
+}}return d;
+}return b;
+},attr:function(C,b,B){if(!C||C.nodeType==3||C.nodeType==8){return AB;
+}var a=!T.isXMLDoc(C),A=B!==AB;
+b=a&&T.props[b]||b;
+if(C.tagName){var c=/href|src|style/.test(b);
+if(b=="selected"&&C.parentNode){C.parentNode.selectedIndex;
+}if(b in C&&a&&!c){if(A){if(b=="type"&&T.nodeName(C,"input")&&C.parentNode){throw"type property can't be changed";
+}C[b]=B;
+}if(T.nodeName(C,"form")&&C.getAttributeNode(b)){return C.getAttributeNode(b).nodeValue;
+}if(b=="tabIndex"){var D=C.getAttributeNode("tabIndex");
+return D&&D.specified?D.value:C.nodeName.match(/(button|input|object|select|textarea)/i)?0:C.nodeName.match(/^(a|area)$/i)&&C.href?0:AB;
+}return C[b];
+}if(!T.support.style&&a&&b=="style"){return T.attr(C.style,"cssText",B);
+}if(A){C.setAttribute(b,""+B);
+}var d=!T.support.hrefNormalized&&a&&c?C.getAttribute(b,2):C.getAttribute(b);
+return d===null?AB:d;
+}if(!T.support.opacity&&b=="opacity"){if(A){C.zoom=1;
+C.filter=(C.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(B)+""=="NaN"?"":"alpha(opacity="+B*100+")");
+}return C.filter&&C.filter.indexOf("opacity=")>=0?(parseFloat(C.filter.match(/opacity=([^)]*)/)[1])/100)+"":"";
+}b=b.replace(/-([a-z])/ig,function(f,e){return e.toUpperCase();
+});
+if(A){C[b]=B;
+}return C[b];
+},trim:function(A){return(A||"").replace(/^\s+|\s+$/g,"");
+},makeArray:function(A){var C=[];
+if(A!=null){var B=A.length;
+if(B==null||typeof A==="string"||T.isFunction(A)||A.setInterval){C[0]=A;
+}else{while(B){C[--B]=A[B];
+}}}return C;
+},inArray:function(B,A){for(var D=0,C=A.length;
+D<C;
+D++){if(A[D]===B){return D;
+}}return -1;
+},merge:function(B,a){var D=0,C,A=B.length;
+if(!T.support.getAll){while((C=a[D++])!=null){if(C.nodeType!=8){B[A++]=C;
+}}}else{while((C=a[D++])!=null){B[A++]=C;
+}}return B;
+},unique:function(A){var b=[],c={};
+try{for(var a=0,D=A.length;
+a<D;
+a++){var B=T.data(A[a]);
+if(!c[B]){c[B]=true;
+b.push(A[a]);
+}}}catch(C){b=A;
+}return b;
+},grep:function(a,A,b){var D=[];
+for(var C=0,B=a.length;
+C<B;
+C++){if(!b!=!A(a[C],C)){D.push(a[C]);
+}}return D;
+},map:function(b,A){var a=[];
+for(var D=0,C=b.length;
+D<C;
+D++){var B=A(b[D],D);
+if(B!=null){a[a.length]=B;
+}}return a.concat.apply([],a);
+}});
+var O=navigator.userAgent.toLowerCase();
+T.browser={version:(O.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/)||[0,"0"])[1],safari:/webkit/.test(O),opera:/opera/.test(O),msie:/msie/.test(O)&&!/opera/.test(O),mozilla:/mozilla/.test(O)&&!/(compatible|webkit)/.test(O)};
+T.each({parent:function(A){return A.parentNode;
+},parents:function(A){return T.dir(A,"parentNode");
+},next:function(A){return T.nth(A,2,"nextSibling");
+},prev:function(A){return T.nth(A,2,"previousSibling");
+},nextAll:function(A){return T.dir(A,"nextSibling");
+},prevAll:function(A){return T.dir(A,"previousSibling");
+},siblings:function(A){return T.sibling(A.parentNode.firstChild,A);
+},children:function(A){return T.sibling(A.firstChild);
+},contents:function(A){return T.nodeName(A,"iframe")?A.contentDocument||A.contentWindow.document:T.makeArray(A.childNodes);
+}},function(B,A){T.fn[B]=function(D){var C=T.map(this,A);
+if(D&&typeof D=="string"){C=T.multiFilter(D,C);
+}return this.pushStack(T.unique(C),B,D);
+};
+});
+T.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(B,A){T.fn[B]=function(d){var a=[],C=T(d);
+for(var D=0,c=C.length;
+D<c;
+D++){var b=(D>0?this.clone(true):this).get();
+T.fn[A].apply(T(C[D]),b);
+a=a.concat(b);
+}return this.pushStack(a,B,d);
+};
+});
+T.each({removeAttr:function(A){T.attr(this,A,"");
+if(this.nodeType==1){this.removeAttribute(A);
+}},addClass:function(A){T.className.add(this,A);
+},removeClass:function(A){T.className.remove(this,A);
+},toggleClass:function(A,B){if(typeof B!=="boolean"){B=!T.className.has(this,A);
+}T.className[B?"add":"remove"](this,A);
+},remove:function(A){if(!A||T.filter(A,[this]).length){T("*",this).add([this]).each(function(){T.event.remove(this);
+T.removeData(this);
+});
+if(this.parentNode){this.parentNode.removeChild(this);
+}}},empty:function(){T(this).children().remove();
+while(this.firstChild){this.removeChild(this.firstChild);
+}}},function(B,A){T.fn[B]=function(){return this.each(A,arguments);
+};
+});
+function Y(B,A){return B[0]&&parseInt(T.curCSS(B[0],A,true),10)||0;
+}var AA="jQuery"+AD(),I=0,R={};
+T.extend({cache:{},data:function(C,D,B){C=C==W?R:C;
+var A=C[AA];
+if(!A){A=C[AA]=++I;
+}if(D&&!T.cache[A]){T.cache[A]={};
+}if(B!==AB){T.cache[A][D]=B;
+}return D?T.cache[A][D]:A;
+},removeData:function(C,D){C=C==W?R:C;
+var A=C[AA];
+if(D){if(T.cache[A]){delete T.cache[A][D];
+D="";
+for(D in T.cache[A]){break;
+}if(!D){T.removeData(C);
+}}}else{try{delete C[AA];
+}catch(B){if(C.removeAttribute){C.removeAttribute(AA);
+}}delete T.cache[A];
+}},queue:function(C,D,A){if(C){D=(D||"fx")+"queue";
+var B=T.data(C,D);
+if(!B||T.isArray(A)){B=T.data(C,D,T.makeArray(A));
+}else{if(A){B.push(A);
+}}}return B;
+},dequeue:function(A,B){var D=T.queue(A,B),C=D.shift();
+if(!B||B==="fx"){C=D[0];
+}if(C!==AB){C.call(A);
+}}});
+T.fn.extend({data:function(D,B){var A=D.split(".");
+A[1]=A[1]?"."+A[1]:"";
+if(B===AB){var C=this.triggerHandler("getData"+A[1]+"!",[A[0]]);
+if(C===AB&&this.length){C=T.data(this[0],D);
+}return C===AB&&A[1]?this.data(A[0]):C;
+}else{return this.trigger("setData"+A[1]+"!",[A[0],B]).each(function(){T.data(this,D,B);
+});
+}},removeData:function(A){return this.each(function(){T.removeData(this,A);
+});
+},queue:function(B,A){if(typeof B!=="string"){A=B;
+B="fx";
+}if(A===AB){return T.queue(this[0],B);
+}return this.each(function(){var C=T.queue(this,B,A);
+if(B=="fx"&&C.length==1){C[0].call(this);
+}});
+},dequeue:function(A){return this.each(function(){T.dequeue(this,A);
+});
+}});
+(function(){var B=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,d=0,h=Object.prototype.toString;
+var j=function(n,r,AI,z){AI=AI||[];
+r=r||document;
+if(r.nodeType!==1&&r.nodeType!==9){return[];
+}if(!n||typeof n!=="string"){return AI;
+}var m=[],p,w,t,s,y,q,o=true;
+B.lastIndex=0;
+while((p=B.exec(n))!==null){m.push(p[1]);
+if(p[2]){q=RegExp.rightContext;
+break;
+}}if(m.length>1&&c.exec(n)){if(m.length===2&&g.relative[m[0]]){w=f(m[0]+m[1],r);
+}else{w=g.relative[m[0]]?[r]:j(m.shift(),r);
+while(m.length){n=m.shift();
+if(g.relative[n]){n+=m.shift();
+}w=f(n,w);
+}}}else{var x=z?{expr:m.pop(),set:k(z)}:j.find(m.pop(),m.length===1&&r.parentNode?r.parentNode:r,C(r));
+w=j.filter(x.expr,x.set);
+if(m.length>0){t=k(w);
+}else{o=false;
+}while(m.length){var u=m.pop(),v=u;
+if(!g.relative[u]){u="";
+}else{v=m.pop();
+}if(v==null){v=r;
+}g.relative[u](t,v,C(r));
+}}if(!t){t=w;
+}if(!t){throw"Syntax error, unrecognized expression: "+(u||n);
+}if(h.call(t)==="[object Array]"){if(!o){AI.push.apply(AI,t);
+}else{if(r.nodeType===1){for(var l=0;
+t[l]!=null;
+l++){if(t[l]&&(t[l]===true||t[l].nodeType===1&&e(r,t[l]))){AI.push(w[l]);
+}}}else{for(var l=0;
+t[l]!=null;
+l++){if(t[l]&&t[l].nodeType===1){AI.push(w[l]);
+}}}}}else{k(t,AI);
+}if(q){j(q,r,AI,z);
+if(i){hasDuplicate=false;
+AI.sort(i);
+if(hasDuplicate){for(var l=1;
+l<AI.length;
+l++){if(AI[l]===AI[l-1]){AI.splice(l--,1);
+}}}}}return AI;
+};
+j.matches=function(m,l){return j(m,null,null,l);
+};
+j.find=function(l,s,t){var m,o;
+if(!l){return[];
+}for(var p=0,q=g.order.length;
+p<q;
+p++){var n=g.order[p],o;
+if((o=g.match[n].exec(l))){var r=RegExp.leftContext;
+if(r.substr(r.length-1)!=="\\"){o[1]=(o[1]||"").replace(/\\/g,"");
+m=g.find[n](o,s,t);
+if(m!=null){l=l.replace(g.match[n],"");
+break;
+}}}}if(!m){m=s.getElementsByTagName("*");
+}return{set:m,expr:l};
+};
+j.filter=function(y,z,v,p){var q=y,t=[],l=z,n,s,m=z&&z[0]&&C(z[0]);
+while(y&&z.length){for(var AI in g.filter){if((n=g.match[AI].exec(y))!=null){var r=g.filter[AI],u,w;
+s=false;
+if(l==t){t=[];
+}if(g.preFilter[AI]){n=g.preFilter[AI](n,l,v,t,p,m);
+if(!n){s=u=true;
+}else{if(n===true){continue;
+}}}if(n){for(var o=0;
+(w=l[o])!=null;
+o++){if(w){u=r(w,n,o,l);
+var x=p^!!u;
+if(v&&u!=null){if(x){s=true;
+}else{l[o]=false;
+}}else{if(x){t.push(w);
+s=true;
+}}}}}if(u!==AB){if(!v){l=t;
+}y=y.replace(g.match[AI],"");
+if(!s){return[];
+}break;
+}}}if(y==q){if(s==null){throw"Syntax error, unrecognized expression: "+y;
+}else{break;
+}}q=y;
+}return l;
+};
+var g=j.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF_-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF_-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF_-]|\\.)+)(?:\((['"]*)((?: [...]
+}},relative:{"+":function(l,s,m){var o=typeof s==="string",t=o&&!/\W/.test(s),n=o&&!t;
+if(t&&!m){s=s.toUpperCase();
+}for(var p=0,q=l.length,r;
+p<q;
+p++){if((r=l[p])){while((r=r.previousSibling)&&r.nodeType!==1){}l[p]=n||r&&r.nodeName===s?r||false:r===s;
+}}if(n){j.filter(s,l,true);
+}},">":function(q,n,p){var s=typeof n==="string";
+if(s&&!/\W/.test(n)){n=p?n:n.toUpperCase();
+for(var m=0,o=q.length;
+m<o;
+m++){var r=q[m];
+if(r){var l=r.parentNode;
+q[m]=l.nodeName===n?l:false;
+}}}else{for(var m=0,o=q.length;
+m<o;
+m++){var r=q[m];
+if(r){q[m]=s?r.parentNode:r.parentNode===n;
+}}if(s){j.filter(n,q,true);
+}}},"":function(l,n,p){var m=d++,o=A;
+if(!n.match(/\W/)){var q=n=p?n:n.toUpperCase();
+o=D;
+}o("parentNode",n,m,l,q,p);
+},"~":function(l,n,p){var m=d++,o=A;
+if(typeof n==="string"&&!n.match(/\W/)){var q=n=p?n:n.toUpperCase();
+o=D;
+}o("previousSibling",n,m,l,q,p);
+}},find:{ID:function(n,m,l){if(typeof m.getElementById!=="undefined"&&!l){var o=m.getElementById(n[1]);
+return o?[o]:[];
+}},NAME:function(m,q,p){if(typeof q.getElementsByName!=="undefined"){var n=[],r=q.getElementsByName(m[1]);
+for(var l=0,o=r.length;
+l<o;
+l++){if(r[l].getAttribute("name")===m[1]){n.push(r[l]);
+}}return n.length===0?null:n;
+}},TAG:function(m,l){return l.getElementsByTagName(m[1]);
+}},preFilter:{CLASS:function(l,n,m,o,q,p){l=" "+l[1].replace(/\\/g,"")+" ";
+if(p){return l;
+}for(var s=0,r;
+(r=n[s])!=null;
+s++){if(r){if(q^(r.className&&(" "+r.className+" ").indexOf(l)>=0)){if(!m){o.push(r);
+}}else{if(m){n[s]=false;
+}}}}return false;
+},ID:function(l){return l[1].replace(/\\/g,"");
+},TAG:function(m,n){for(var l=0;
+n[l]===false;
+l++){}return n[l]&&C(n[l])?m[1]:m[1].toUpperCase();
+},CHILD:function(m){if(m[1]=="nth"){var l=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(m[2]=="even"&&"2n"||m[2]=="odd"&&"2n+1"||!/\D/.test(m[2])&&"0n+"+m[2]||m[2]);
+m[2]=(l[1]+(l[2]||1))-0;
+m[3]=l[3]-0;
+}m[0]=d++;
+return m;
+},ATTR:function(r,n,m,o,q,p){var l=r[1].replace(/\\/g,"");
+if(!p&&g.attrMap[l]){r[1]=g.attrMap[l];
+}if(r[2]==="~="){r[4]=" "+r[4]+" ";
+}return r;
+},PSEUDO:function(q,n,m,o,p){if(q[1]==="not"){if(q[3].match(B).length>1||/^\w/.test(q[3])){q[3]=j(q[3],null,null,n);
+}else{var l=j.filter(q[3],n,m,true^p);
+if(!m){o.push.apply(o,l);
+}return false;
+}}else{if(g.match.POS.test(q[0])||g.match.CHILD.test(q[0])){return true;
+}}return q;
+},POS:function(l){l.unshift(true);
+return l;
+}},filters:{enabled:function(l){return l.disabled===false&&l.type!=="hidden";
+},disabled:function(l){return l.disabled===true;
+},checked:function(l){return l.checked===true;
+},selected:function(l){l.parentNode.selectedIndex;
+return l.selected===true;
+},parent:function(l){return !!l.firstChild;
+},empty:function(l){return !l.firstChild;
+},has:function(l,m,n){return !!j(n[3],l).length;
+},header:function(l){return/h\d/i.test(l.nodeName);
+},text:function(l){return"text"===l.type;
+},radio:function(l){return"radio"===l.type;
+},checkbox:function(l){return"checkbox"===l.type;
+},file:function(l){return"file"===l.type;
+},password:function(l){return"password"===l.type;
+},submit:function(l){return"submit"===l.type;
+},image:function(l){return"image"===l.type;
+},reset:function(l){return"reset"===l.type;
+},button:function(l){return"button"===l.type||l.nodeName.toUpperCase()==="BUTTON";
+},input:function(l){return/input|select|textarea|button/i.test(l.nodeName);
+}},setFilters:{first:function(l,m){return m===0;
+},last:function(m,n,o,l){return n===l.length-1;
+},even:function(l,m){return m%2===0;
+},odd:function(l,m){return m%2===1;
+},lt:function(l,m,n){return m<n[3]-0;
+},gt:function(l,m,n){return m>n[3]-0;
+},nth:function(l,m,n){return n[3]-0==m;
+},eq:function(l,m,n){return n[3]-0==m;
+}},filter:{PSEUDO:function(q,m,l,p){var n=m[1],s=g.filters[n];
+if(s){return s(q,l,m,p);
+}else{if(n==="contains"){return(q.textContent||q.innerText||"").indexOf(m[3])>=0;
+}else{if(n==="not"){var r=m[3];
+for(var l=0,o=r.length;
+l<o;
+l++){if(r[l]===q){return false;
+}}return true;
+}}}},CHILD:function(s,p){var m=p[1],r=s;
+switch(m){case"only":case"first":while(r=r.previousSibling){if(r.nodeType===1){return false;
+}}if(m=="first"){return true;
+}r=s;
+case"last":while(r=r.nextSibling){if(r.nodeType===1){return false;
+}}return true;
+case"nth":var q=p[2],t=p[3];
+if(q==1&&t==0){return true;
+}var n=p[0],u=s.parentNode;
+if(u&&(u.sizcache!==n||!s.nodeIndex)){var o=0;
+for(r=u.firstChild;
+r;
+r=r.nextSibling){if(r.nodeType===1){r.nodeIndex=++o;
+}}u.sizcache=n;
+}var l=s.nodeIndex-t;
+if(q==0){return l==0;
+}else{return(l%q==0&&l/q>=0);
+}}},ID:function(l,m){return l.nodeType===1&&l.getAttribute("id")===m;
+},TAG:function(l,m){return(m==="*"&&l.nodeType===1)||l.nodeName===m;
+},CLASS:function(l,m){return(" "+(l.className||l.getAttribute("class"))+" ").indexOf(m)>-1;
+},ATTR:function(q,l){var m=l[1],o=g.attrHandle[m]?g.attrHandle[m](q):q[m]!=null?q[m]:q.getAttribute(m),p=o+"",r=l[2],n=l[4];
+return o==null?r==="!=":r==="="?p===n:r==="*="?p.indexOf(n)>=0:r==="~="?(" "+p+" ").indexOf(n)>=0:!n?p&&o!==false:r==="!="?p!=n:r==="^="?p.indexOf(n)===0:r==="$="?p.substr(p.length-n.length)===n:r==="|="?p===n||p.substr(0,n.length+1)===n+"-":false;
+},POS:function(q,n,m,p){var o=n[2],l=g.setFilters[o];
+if(l){return l(q,m,n,p);
+}}}};
+var c=g.match.POS;
+for(var a in g.match){g.match[a]=RegExp(g.match[a].source+/(?![^\[]*\])(?![^\(]*\))/.source);
+}var k=function(l,m){l=Array.prototype.slice.call(l);
+if(m){m.push.apply(m,l);
+return m;
+}return l;
+};
+try{Array.prototype.slice.call(document.documentElement.childNodes);
+}catch(b){k=function(p,l){var n=l||[];
+if(h.call(p)==="[object Array]"){Array.prototype.push.apply(n,p);
+}else{if(typeof p.length==="number"){for(var m=0,o=p.length;
+m<o;
+m++){n.push(p[m]);
+}}else{for(var m=0;
+p[m];
+m++){n.push(p[m]);
+}}}return n;
+};
+}var i;
+if(document.documentElement.compareDocumentPosition){i=function(m,n){var l=m.compareDocumentPosition(n)&4?-1:m===n?0:1;
+if(l===0){hasDuplicate=true;
+}return l;
+};
+}else{if("sourceIndex" in document.documentElement){i=function(m,n){var l=m.sourceIndex-n.sourceIndex;
+if(l===0){hasDuplicate=true;
+}return l;
+};
+}else{if(document.createRange){i=function(l,n){var m=l.ownerDocument.createRange(),o=n.ownerDocument.createRange();
+m.selectNode(l);
+m.collapse(true);
+o.selectNode(n);
+o.collapse(true);
+var p=m.compareBoundaryPoints(Range.START_TO_END,o);
+if(p===0){hasDuplicate=true;
+}return p;
+};
+}}}(function(){var m=document.createElement("form"),l="script"+(new Date).getTime();
+m.innerHTML="<input name='"+l+"'/>";
+var n=document.documentElement;
+n.insertBefore(m,n.firstChild);
+if(!!document.getElementById(l)){g.find.ID=function(r,q,p){if(typeof q.getElementById!=="undefined"&&!p){var o=q.getElementById(r[1]);
+return o?o.id===r[1]||typeof o.getAttributeNode!=="undefined"&&o.getAttributeNode("id").nodeValue===r[1]?[o]:AB:[];
+}};
+g.filter.ID=function(p,o){var q=typeof p.getAttributeNode!=="undefined"&&p.getAttributeNode("id");
+return p.nodeType===1&&q&&q.nodeValue===o;
+};
+}n.removeChild(m);
+})();
+(function(){var l=document.createElement("div");
+l.appendChild(document.createComment(""));
+if(l.getElementsByTagName("*").length>0){g.find.TAG=function(o,p){var q=p.getElementsByTagName(o[1]);
+if(o[1]==="*"){var m=[];
+for(var n=0;
+q[n];
+n++){if(q[n].nodeType===1){m.push(q[n]);
+}}q=m;
+}return q;
+};
+}l.innerHTML="<a href='#'></a>";
+if(l.firstChild&&typeof l.firstChild.getAttribute!=="undefined"&&l.firstChild.getAttribute("href")!=="#"){g.attrHandle.href=function(m){return m.getAttribute("href",2);
+};
+}})();
+if(document.querySelectorAll){(function(){var m=j,l=document.createElement("div");
+l.innerHTML="<p class='TEST'></p>";
+if(l.querySelectorAll&&l.querySelectorAll(".TEST").length===0){return ;
+}j=function(q,r,o,n){r=r||document;
+if(!n&&r.nodeType===9&&!C(r)){try{return k(r.querySelectorAll(q),o);
+}catch(p){}}return m(q,r,o,n);
+};
+j.find=m.find;
+j.filter=m.filter;
+j.selectors=m.selectors;
+j.matches=m.matches;
+})();
+}if(document.getElementsByClassName&&document.documentElement.getElementsByClassName){(function(){var l=document.createElement("div");
+l.innerHTML="<div class='test e'></div><div class='test'></div>";
+if(l.getElementsByClassName("e").length===0){return ;
+}l.lastChild.className="e";
+if(l.getElementsByClassName("e").length===1){return ;
+}g.order.splice(1,0,"CLASS");
+g.find.CLASS=function(o,n,m){if(typeof n.getElementsByClassName!=="undefined"&&!m){return n.getElementsByClassName(o[1]);
+}};
+})();
+}function D(r,m,n,t,l,u){var v=r=="previousSibling"&&!u;
+for(var p=0,q=t.length;
+p<q;
+p++){var s=t[p];
+if(s){if(v&&s.nodeType===1){s.sizcache=n;
+s.sizset=p;
+}s=s[r];
+var o=false;
+while(s){if(s.sizcache===n){o=t[s.sizset];
+break;
+}if(s.nodeType===1&&!u){s.sizcache=n;
+s.sizset=p;
+}if(s.nodeName===m){o=s;
+break;
+}s=s[r];
+}t[p]=o;
+}}}function A(r,m,n,t,l,u){var v=r=="previousSibling"&&!u;
+for(var p=0,q=t.length;
+p<q;
+p++){var s=t[p];
+if(s){if(v&&s.nodeType===1){s.sizcache=n;
+s.sizset=p;
+}s=s[r];
+var o=false;
+while(s){if(s.sizcache===n){o=t[s.sizset];
+break;
+}if(s.nodeType===1){if(!u){s.sizcache=n;
+s.sizset=p;
+}if(typeof m!=="string"){if(s===m){o=true;
+break;
+}}else{if(j.filter(m,[s]).length>0){o=s;
+break;
+}}}s=s[r];
+}t[p]=o;
+}}}var e=document.compareDocumentPosition?function(l,m){return l.compareDocumentPosition(m)&16;
+}:function(l,m){return l!==m&&(l.contains?l.contains(m):true);
+};
+var C=function(l){return l.nodeType===9&&l.documentElement.nodeName!=="HTML"||!!l.ownerDocument&&C(l.ownerDocument);
+};
+var f=function(o,q){var l=[],s="",r,m=q.nodeType?[q]:q;
+while((r=g.match.PSEUDO.exec(o))){s+=r[0];
+o=o.replace(g.match.PSEUDO,"");
+}o=g.relative[o]?o+"*":o;
+for(var p=0,n=m.length;
+p<n;
+p++){j(o,m[p],l);
+}return j.filter(s,l);
+};
+T.find=j;
+T.filter=j.filter;
+T.expr=j.selectors;
+T.expr[":"]=T.expr.filters;
+j.selectors.filters.hidden=function(l){return l.offsetWidth===0||l.offsetHeight===0;
+};
+j.selectors.filters.visible=function(l){return l.offsetWidth>0||l.offsetHeight>0;
+};
+j.selectors.filters.animated=function(l){return T.grep(T.timers,function(m){return l===m.elem;
+}).length;
+};
+T.multiFilter=function(l,n,m){if(m){l=":not("+l+")";
+}return j.matches(l,n);
+};
+T.dir=function(m,n){var o=[],l=m[n];
+while(l&&l!=document){if(l.nodeType==1){o.push(l);
+}l=l[n];
+}return o;
+};
+T.nth=function(p,o,m,l){o=o||1;
+var n=0;
+for(;
+p;
+p=p[m]){if(p.nodeType==1&&++n==o){break;
+}}return p;
+};
+T.sibling=function(l,m){var n=[];
+for(;
+l;
+l=l.nextSibling){if(l.nodeType==1&&l!=m){n.push(l);
+}}return n;
+};
+return ;
+W.Sizzle=j;
+})();
+T.event={add:function(C,b,D,A){if(C.nodeType==3||C.nodeType==8){return ;
+}if(C.setInterval&&C!=W){C=W;
+}if(!D.guid){D.guid=this.guid++;
+}if(A!==AB){var a=D;
+D=this.proxy(a);
+D.data=A;
+}var c=T.data(C,"events")||T.data(C,"events",{}),B=T.data(C,"handle")||T.data(C,"handle",function(){return typeof T!=="undefined"&&!T.event.triggered?T.event.handle.apply(arguments.callee.elem,arguments):AB;
+});
+B.elem=C;
+T.each(b.split(/\s+/),function(g,f){var e=f.split(".");
+f=e.shift();
+D.type=e.slice().sort().join(".");
+var d=c[f];
+if(T.event.specialAll[f]){T.event.specialAll[f].setup.call(C,A,e);
+}if(!d){d=c[f]={};
+if(!T.event.special[f]||T.event.special[f].setup.call(C,A,e)===false){if(C.addEventListener){C.addEventListener(f,B,false);
+}else{if(C.attachEvent){C.attachEvent("on"+f,B);
+}}}}d[D.guid]=D;
+T.event.global[f]=true;
+});
+C=null;
+},guid:1,global:{},remove:function(B,a,C){if(B.nodeType==3||B.nodeType==8){return ;
+}var b=T.data(B,"events"),c,d;
+if(b){if(a===AB||(typeof a==="string"&&a.charAt(0)==".")){for(var D in b){this.remove(B,D+(a||""));
+}}else{if(a.type){C=a.handler;
+a=a.type;
+}T.each(a.split(/\s+/),function(i,g){var e=g.split(".");
+g=e.shift();
+var h=RegExp("(^|\\.)"+e.slice().sort().join(".*\\.")+"(\\.|$)");
+if(b[g]){if(C){delete b[g][C.guid];
+}else{for(var f in b[g]){if(h.test(b[g][f].type)){delete b[g][f];
+}}}if(T.event.specialAll[g]){T.event.specialAll[g].teardown.call(B,e);
+}for(c in b[g]){break;
+}if(!c){if(!T.event.special[g]||T.event.special[g].teardown.call(B,e)===false){if(B.removeEventListener){B.removeEventListener(g,T.data(B,"handle"),false);
+}else{if(B.detachEvent){B.detachEvent("on"+g,T.data(B,"handle"));
+}}}c=null;
+delete b[g];
+}}});
+}for(c in b){break;
+}if(!c){var A=T.data(B,"handle");
+if(A){A.elem=null;
+}T.removeData(B,"events");
+T.removeData(B,"handle");
+}}},trigger:function(D,B,a,d){var b=D.type||D;
+if(!d){D=typeof D==="object"?D[AA]?D:T.extend(T.Event(b),D):T.Event(b);
+if(b.indexOf("!")>=0){D.type=b=b.slice(0,-1);
+D.exclusive=true;
+}if(!a){D.stopPropagation();
+if(this.global[b]){T.each(T.cache,function(){if(this.events&&this.events[b]){T.event.trigger(D,B,this.handle.elem);
+}});
+}}if(!a||a.nodeType==3||a.nodeType==8){return AB;
+}D.result=AB;
+D.target=a;
+B=T.makeArray(B);
+B.unshift(D);
+}D.currentTarget=a;
+var C=T.data(a,"handle");
+if(C){C.apply(a,B);
+}if((!a[b]||(T.nodeName(a,"a")&&b=="click"))&&a["on"+b]&&a["on"+b].apply(a,B)===false){D.result=false;
+}if(!d&&a[b]&&!D.isDefaultPrevented()&&!(T.nodeName(a,"a")&&b=="click")){this.triggered=true;
+try{a[b]();
+}catch(A){}}this.triggered=false;
+if(!D.isPropagationStopped()){var c=a.parentNode||a.ownerDocument;
+if(c){T.event.trigger(D,B,c,true);
+}}},handle:function(B){var C,d;
+B=arguments[0]=T.event.fix(B||W.event);
+B.currentTarget=this;
+var A=B.type.split(".");
+B.type=A.shift();
+C=!A.length&&!B.exclusive;
+var D=RegExp("(^|\\.)"+A.slice().sort().join(".*\\.")+"(\\.|$)");
+d=(T.data(this,"events")||{})[B.type];
+for(var b in d){var a=d[b];
+if(C||D.test(a.type)){B.handler=a;
+B.data=a.data;
+var c=a.apply(this,arguments);
+if(c!==AB){B.result=c;
+if(c===false){B.preventDefault();
+B.stopPropagation();
+}}if(B.isImmediatePropagationStopped()){break;
+}}}},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(C){if(C[AA]){return C;
+}var a=C;
+C=T.Event(a);
+for(var D=this.props.length,A;
+D;
+){A=this.props[--D];
+C[A]=a[A];
+}if(!C.target){C.target=C.srcElement||document;
+}if(C.target.nodeType==3){C.target=C.target.parentNode;
+}if(!C.relatedTarget&&C.fromElement){C.relatedTarget=C.fromElement==C.target?C.toElement:C.fromElement;
+}if(C.pageX==null&&C.clientX!=null){var B=document.documentElement,b=document.body;
+C.pageX=C.clientX+(B&&B.scrollLeft||b&&b.scrollLeft||0)-(B.clientLeft||0);
+C.pageY=C.clientY+(B&&B.scrollTop||b&&b.scrollTop||0)-(B.clientTop||0);
+}if(!C.which&&((C.charCode||C.charCode===0)?C.charCode:C.keyCode)){C.which=C.charCode||C.keyCode;
+}if(!C.metaKey&&C.ctrlKey){C.metaKey=C.ctrlKey;
+}if(!C.which&&C.button){C.which=(C.button&1?1:(C.button&2?3:(C.button&4?2:0)));
+}return C;
+},proxy:function(A,B){B=B||function(){return A.apply(this,arguments);
+};
+B.guid=A.guid=A.guid||B.guid||this.guid++;
+return B;
+},special:{ready:{setup:P,teardown:function(){}}},specialAll:{live:{setup:function(B,A){T.event.add(this,A[0],AF);
+},teardown:function(A){if(A.length){var C=0,B=RegExp("(^|\\.)"+A[0]+"(\\.|$)");
+T.each((T.data(this,"events").live||{}),function(){if(B.test(this.type)){C++;
+}});
+if(C<1){T.event.remove(this,A[0],AF);
+}}}}}};
+T.Event=function(A){if(!this.preventDefault){return new T.Event(A);
+}if(A&&A.type){this.originalEvent=A;
+this.type=A.type;
+}else{this.type=A;
+}this.timeStamp=AD();
+this[AA]=true;
+};
+function X(){return false;
+}function J(){return true;
+}T.Event.prototype={preventDefault:function(){this.isDefaultPrevented=J;
+var A=this.originalEvent;
+if(!A){return ;
+}if(A.preventDefault){A.preventDefault();
+}A.returnValue=false;
+},stopPropagation:function(){this.isPropagationStopped=J;
+var A=this.originalEvent;
+if(!A){return ;
+}if(A.stopPropagation){A.stopPropagation();
+}A.cancelBubble=true;
+},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=J;
+this.stopPropagation();
+},isDefaultPrevented:X,isPropagationStopped:X,isImmediatePropagationStopped:X};
+var AH=function(B){var C=B.relatedTarget;
+while(C&&C!=this){try{C=C.parentNode;
+}catch(A){C=this;
+}}if(C!=this){B.type=B.data;
+T.event.handle.apply(this,arguments);
+}};
+T.each({mouseover:"mouseenter",mouseout:"mouseleave"},function(A,B){T.event.special[B]={setup:function(){T.event.add(this,A,AH,B);
+},teardown:function(){T.event.remove(this,A,AH);
+}};
+});
+T.fn.extend({bind:function(B,A,C){return B=="unload"?this.one(B,A,C):this.each(function(){T.event.add(this,B,C||A,C&&A);
+});
+},one:function(B,A,C){var D=T.event.proxy(C||A,function(a){T(this).unbind(a,D);
+return(C||A).apply(this,arguments);
+});
+return this.each(function(){T.event.add(this,B,D,C&&A);
+});
+},unbind:function(A,B){return this.each(function(){T.event.remove(this,A,B);
+});
+},trigger:function(B,A){return this.each(function(){T.event.trigger(B,A,this);
+});
+},triggerHandler:function(C,A){if(this[0]){var B=T.Event(C);
+B.preventDefault();
+B.stopPropagation();
+T.event.trigger(B,A,this[0]);
+return B.result;
+}},toggle:function(A){var C=arguments,B=1;
+while(B<C.length){T.event.proxy(A,C[B++]);
+}return this.click(T.event.proxy(A,function(D){this.lastToggle=(this.lastToggle||0)%B;
+D.preventDefault();
+return C[this.lastToggle++].apply(this,arguments)||false;
+}));
+},hover:function(B,A){return this.mouseenter(B).mouseleave(A);
+},ready:function(A){P();
+if(T.isReady){A.call(document,T);
+}else{T.readyList.push(A);
+}return this;
+},live:function(A,B){var C=T.event.proxy(B);
+C.guid+=this.selector+A;
+T(document).bind(Z(A,this.selector),this.selector,C);
+return this;
+},die:function(A,B){T(document).unbind(Z(A,this.selector),B?{guid:B.guid+this.selector+A}:null);
+return this;
+}});
+function AF(A){var D=RegExp("(^|\\.)"+A.type+"(\\.|$)"),B=true,C=[];
+T.each(T.data(this,"events").live||[],function(c,b){if(D.test(b.type)){var a=T(A.target).closest(b.data)[0];
+if(a){C.push({elem:a,fn:b});
+}}});
+C.sort(function(a,b){return T.data(a.elem,"closest")-T.data(b.elem,"closest");
+});
+T.each(C,function(){if(this.fn.call(this.elem,A,this.fn.data)===false){return(B=false);
+}});
+return B;
+}function Z(A,B){return["live",A,B.replace(/\./g,"`").replace(/ /g,"|")].join(".");
+}T.extend({isReady:false,readyList:[],ready:function(){if(!T.isReady){T.isReady=true;
+if(T.readyList){T.each(T.readyList,function(){this.call(document,T);
+});
+T.readyList=null;
+}T(document).triggerHandler("ready");
+}}});
+var G=false;
+function P(){if(G){return ;
+}G=true;
+if(document.addEventListener){document.addEventListener("DOMContentLoaded",function(){document.removeEventListener("DOMContentLoaded",arguments.callee,false);
+T.ready();
+},false);
+}else{if(document.attachEvent){document.attachEvent("onreadystatechange",function(){if(document.readyState==="complete"){document.detachEvent("onreadystatechange",arguments.callee);
+T.ready();
+}});
+if(document.documentElement.doScroll&&W==W.top){(function(){if(T.isReady){return ;
+}try{document.documentElement.doScroll("left");
+}catch(A){setTimeout(arguments.callee,0);
+return ;
+}T.ready();
+})();
+}}}T.event.add(W,"load",T.ready);
+}T.each(("blur,focus,load,resize,scroll,unload,click,dblclick,mousedown,mouseup,mousemove,mouseover,mouseout,mouseenter,mouseleave,change,select,submit,keydown,keypress,keyup,error").split(","),function(A,B){T.fn[B]=function(C){return C?this.bind(B,C):this.trigger(B);
+};
+});
+T(W).bind("unload",function(){for(var A in T.cache){if(A!=1&&T.cache[A].handle){T.event.remove(T.cache[A].handle.elem);
+}}});
+(function(){T.support={};
+var b=document.documentElement,a=document.createElement("script"),A=document.createElement("div"),B="script"+(new Date).getTime();
+A.style.display="none";
+A.innerHTML='   <link/><table></table><a href="/a" style="color:red;float:left;opacity:.5;">a</a><select><option>text</option></select><object><param/></object>';
+var D=A.getElementsByTagName("*"),c=A.getElementsByTagName("a")[0];
+if(!D||!D.length||!c){return ;
+}T.support={leadingWhitespace:A.firstChild.nodeType==3,tbody:!A.getElementsByTagName("tbody").length,objectAll:!!A.getElementsByTagName("object")[0].getElementsByTagName("*").length,htmlSerialize:!!A.getElementsByTagName("link").length,style:/red/.test(c.getAttribute("style")),hrefNormalized:c.getAttribute("href")==="/a",opacity:c.style.opacity==="0.5",cssFloat:!!c.style.cssFloat,scriptEval:false,noCloneEvent:true,boxModel:null};
+a.type="text/javascript";
+try{a.appendChild(document.createTextNode("window."+B+"=1;"));
+}catch(C){}b.insertBefore(a,b.firstChild);
+if(W[B]){T.support.scriptEval=true;
+delete W[B];
+}b.removeChild(a);
+if(A.attachEvent&&A.fireEvent){A.attachEvent("onclick",function(){T.support.noCloneEvent=false;
+A.detachEvent("onclick",arguments.callee);
+});
+A.cloneNode(true).fireEvent("onclick");
+}T(function(){var d=document.createElement("div");
+d.style.width=d.style.paddingLeft="1px";
+document.body.appendChild(d);
+T.boxModel=T.support.boxModel=d.offsetWidth===2;
+document.body.removeChild(d).style.display="none";
+});
+})();
+var H=T.support.cssFloat?"cssFloat":"styleFloat";
+T.props={"for":"htmlFor","class":"className","float":H,cssFloat:H,styleFloat:H,readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",tabindex:"tabIndex"};
+T.fn.extend({_load:T.fn.load,load:function(a,B,A){if(typeof a!=="string"){return this._load(a);
+}var C=a.indexOf(" ");
+if(C>=0){var c=a.slice(C,a.length);
+a=a.slice(0,C);
+}var D="GET";
+if(B){if(T.isFunction(B)){A=B;
+B=null;
+}else{if(typeof B==="object"){B=T.param(B);
+D="POST";
+}}}var b=this;
+T.ajax({url:a,type:D,dataType:"html",data:B,complete:function(e,d){if(d=="success"||d=="notmodified"){b.html(c?T("<div/>").append(e.responseText.replace(/<script(.|\s)*?\/script>/g,"")).find(c):e.responseText);
+}if(A){b.each(A,[e.responseText,d,e]);
+}}});
+return this;
+},serialize:function(){return T.param(this.serializeArray());
+},serializeArray:function(){return this.map(function(){return this.elements?T.makeArray(this.elements):this;
+}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password|search/i.test(this.type));
+}).map(function(C,B){var A=T(this).val();
+return A==null?null:T.isArray(A)?T.map(A,function(D,a){return{name:B.name,value:D};
+}):{name:B.name,value:A};
+}).get();
+}});
+T.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(B,A){T.fn[A]=function(C){return this.bind(A,C);
+};
+});
+var N=AD();
+T.extend({get:function(D,B,A,C){if(T.isFunction(B)){A=B;
+B=null;
+}return T.ajax({type:"GET",url:D,data:B,success:A,dataType:C});
+},getScript:function(B,A){return T.get(B,null,A,"script");
+},getJSON:function(C,B,A){return T.get(C,B,A,"json");
+},post:function(D,B,A,C){if(T.isFunction(B)){A=B;
+B={};
+}return T.ajax({type:"POST",url:D,data:B,success:A,dataType:C});
+},ajaxSetup:function(A){T.extend(T.ajaxSettings,A);
+},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return W.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest();
+},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(g){g=T.extend(true,g,T.extend(true,{},T.ajaxSettings,g));
+var A,n=/=\?(&|$)/g,b,B,m=g.type.toUpperCase();
+if(g.data&&g.processData&&typeof g.data!=="string"){g.data=T.param(g.data);
+}if(g.dataType=="jsonp"){if(m=="GET"){if(!g.url.match(n)){g.url+=(g.url.match(/\?/)?"&":"?")+(g.jsonp||"callback")+"=?";
+}}else{if(!g.data||!g.data.match(n)){g.data=(g.data?g.data+"&":"")+(g.jsonp||"callback")+"=?";
+}}g.dataType="json";
+}if(g.dataType=="json"&&(g.data&&g.data.match(n)||g.url.match(n))){A="jsonp"+N++;
+if(g.data){g.data=(g.data+"").replace(n,"="+A+"$1");
+}g.url=g.url.replace(n,"="+A+"$1");
+g.dataType="script";
+W[A]=function(q){B=q;
+k();
+h();
+W[A]=AB;
+try{delete W[A];
+}catch(p){}if(l){l.removeChild(D);
+}};
+}if(g.dataType=="script"&&g.cache==null){g.cache=false;
+}if(g.cache===false&&m=="GET"){var o=AD();
+var C=g.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+o+"$2");
+g.url=C+((C==g.url)?(g.url.match(/\?/)?"&":"?")+"_="+o:"");
+}if(g.data&&m=="GET"){g.url+=(g.url.match(/\?/)?"&":"?")+g.data;
+g.data=null;
+}if(g.global&&!T.active++){T.event.trigger("ajaxStart");
+}var c=/^(\w+:)?\/\/([^\/?#]+)/.exec(g.url);
+if(g.dataType=="script"&&m=="GET"&&c&&(c[1]&&c[1]!=location.protocol||c[2]!=location.host)){var l=document.getElementsByTagName("head")[0];
+var D=document.createElement("script");
+D.src=g.url;
+if(g.scriptCharset){D.charset=g.scriptCharset;
+}if(!A){var e=false;
+D.onload=D.onreadystatechange=function(){if(!e&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){e=true;
+k();
+h();
+D.onload=D.onreadystatechange=null;
+l.removeChild(D);
+}};
+}l.appendChild(D);
+return AB;
+}var i=false;
+var j=g.xhr();
+if(g.username){j.open(m,g.url,g.async,g.username,g.password);
+}else{j.open(m,g.url,g.async);
+}try{if(g.data){j.setRequestHeader("Content-Type",g.contentType);
+}if(g.ifModified){j.setRequestHeader("If-Modified-Since",T.lastModified[g.url]||"Thu, 01 Jan 1970 00:00:00 GMT");
+}j.setRequestHeader("X-Requested-With","XMLHttpRequest");
+j.setRequestHeader("Accept",g.dataType&&g.accepts[g.dataType]?g.accepts[g.dataType]+", */*":g.accepts._default);
+}catch(a){}if(g.beforeSend&&g.beforeSend(j,g)===false){if(g.global&&!--T.active){T.event.trigger("ajaxStop");
+}j.abort();
+return false;
+}if(g.global){T.event.trigger("ajaxSend",[j,g]);
+}var f=function(r){if(j.readyState==0){if(d){clearInterval(d);
+d=null;
+if(g.global&&!--T.active){T.event.trigger("ajaxStop");
+}}}else{if(!i&&j&&(j.readyState==4||r=="timeout")){i=true;
+if(d){clearInterval(d);
+d=null;
+}b=r=="timeout"?"timeout":!T.httpSuccess(j)?"error":g.ifModified&&T.httpNotModified(j,g.url)?"notmodified":"success";
+if(b=="success"){try{B=T.httpData(j,g.dataType,g);
+}catch(p){b="parsererror";
+}}if(b=="success"){var q;
+try{q=j.getResponseHeader("Last-Modified");
+}catch(p){}if(g.ifModified&&q){T.lastModified[g.url]=q;
+}if(!A){k();
+}}else{T.handleError(g,j,b);
+}h();
+if(r){j.abort();
+}if(g.async){j=null;
+}}}};
+if(g.async){var d=setInterval(f,13);
+if(g.timeout>0){setTimeout(function(){if(j&&!i){f("timeout");
+}},g.timeout);
+}}try{j.send(g.data);
+}catch(a){T.handleError(g,j,null,a);
+}if(!g.async){f();
+}function k(){if(g.success){g.success(B,b);
+}if(g.global){T.event.trigger("ajaxSuccess",[j,g]);
+}}function h(){if(g.complete){g.complete(j,b);
+}if(g.global){T.event.trigger("ajaxComplete",[j,g]);
+}if(g.global&&!--T.active){T.event.trigger("ajaxStop");
+}}return j;
+},handleError:function(C,A,D,B){if(C.error){C.error(A,D,B);
+}if(C.global){T.event.trigger("ajaxError",[A,C,B]);
+}},active:0,httpSuccess:function(A){try{return !A.status&&location.protocol=="file:"||(A.status>=200&&A.status<300)||A.status==304||A.status==1223;
+}catch(B){}return false;
+},httpNotModified:function(B,D){try{var A=B.getResponseHeader("Last-Modified");
+return B.status==304||A==T.lastModified[D];
+}catch(C){}return false;
+},httpData:function(A,C,D){var a=A.getResponseHeader("content-type"),b=C=="xml"||!C&&a&&a.indexOf("xml")>=0,B=b?A.responseXML:A.responseText;
+if(b&&B.documentElement.tagName=="parsererror"){throw"parsererror";
+}if(D&&D.dataFilter){B=D.dataFilter(B,C);
+}if(typeof B==="string"){if(C=="script"){T.globalEval(B);
+}if(C=="json"){B=W["eval"]("("+B+")");
+}}return B;
+},param:function(D){var B=[];
+function A(b,a){B[B.length]=encodeURIComponent(b)+"="+encodeURIComponent(a);
+}if(T.isArray(D)||D.jquery){T.each(D,function(){A(this.name,this.value);
+});
+}else{for(var C in D){if(T.isArray(D[C])){T.each(D[C],function(){A(C,this);
+});
+}else{A(C,T.isFunction(D[C])?D[C]():D[C]);
+}}}return B.join("&").replace(/%20/g,"+");
+}});
+var V={},U,AE=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];
+function K(B,C){var A={};
+T.each(AE.concat.apply([],AE.slice(0,C)),function(){A[this]=B;
+});
+return A;
+}T.fn.extend({show:function(C,A){if(C){return this.animate(K("show",3),C,A);
+}else{for(var a=0,c=this.length;
+a<c;
+a++){var d=T.data(this[a],"olddisplay");
+this[a].style.display=d||"";
+if(T.css(this[a],"display")==="none"){var b=this[a].tagName,B;
+if(V[b]){B=V[b];
+}else{var D=T("<"+b+" />").appendTo("body");
+B=D.css("display");
+if(B==="none"){B="block";
+}D.remove();
+V[b]=B;
+}T.data(this[a],"olddisplay",B);
+}}for(var a=0,c=this.length;
+a<c;
+a++){this[a].style.display=T.data(this[a],"olddisplay")||"";
+}return this;
+}},hide:function(B,A){if(B){return this.animate(K("hide",3),B,A);
+}else{for(var C=0,D=this.length;
+C<D;
+C++){var a=T.data(this[C],"olddisplay");
+if(!a&&a!=="none"){T.data(this[C],"olddisplay",T.css(this[C],"display"));
+}}for(var C=0,D=this.length;
+C<D;
+C++){this[C].style.display="none";
+}return this;
+}},_toggle:T.fn.toggle,toggle:function(A,B){var C=typeof A==="boolean";
+return T.isFunction(A)&&T.isFunction(B)?this._toggle.apply(this,arguments):A==null||C?this.each(function(){var D=C?A:T(this).is(":hidden");
+T(this)[D?"show":"hide"]();
+}):this.animate(K("toggle",3),A,B);
+},fadeTo:function(C,A,B){return this.animate({opacity:A},C,B);
+},animate:function(A,D,B,C){var a=T.speed(D,B,C);
+return this[a.queue===false?"each":"queue"](function(){var c=T.extend({},a),e,b=this.nodeType==1&&T(this).is(":hidden"),d=this;
+for(e in A){if(A[e]=="hide"&&b||A[e]=="show"&&!b){return c.complete.call(this);
+}if((e=="height"||e=="width")&&this.style){c.display=T.css(this,"display");
+c.overflow=this.style.overflow;
+}}if(c.overflow!=null){this.style.overflow="hidden";
+}c.curAnim=T.extend({},A);
+T.each(A,function(k,g){var h=new T.fx(d,c,k);
+if(/toggle|show|hide/.test(g)){h[g=="toggle"?b?"show":"hide":g](A);
+}else{var i=g.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),f=h.cur(true)||0;
+if(i){var l=parseFloat(i[2]),j=i[3]||"px";
+if(j!="px"){d.style[k]=(l||1)+j;
+f=((l||1)/h.cur(true))*f;
+d.style[k]=f+j;
+}if(i[1]){l=((i[1]=="-="?-1:1)*l)+f;
+}h.custom(f,l,j);
+}else{h.custom(f,g,"");
+}}});
+return true;
+});
+},stop:function(B,C){var A=T.timers;
+if(B){this.queue([]);
+}this.each(function(){for(var D=A.length-1;
+D>=0;
+D--){if(A[D].elem==this){if(C){A[D](true);
+}A.splice(D,1);
+}}});
+if(!C){this.dequeue();
+}return this;
+}});
+T.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(B,A){T.fn[B]=function(D,C){return this.animate(A,D,C);
+};
+});
+T.extend({speed:function(B,A,C){var D=typeof B==="object"?B:{complete:C||!C&&A||T.isFunction(B)&&B,duration:B,easing:C&&A||A&&!T.isFunction(A)&&A};
+D.duration=T.fx.off?0:typeof D.duration==="number"?D.duration:T.fx.speeds[D.duration]||T.fx.speeds._default;
+D.old=D.complete;
+D.complete=function(){if(D.queue!==false){T(this).dequeue();
+}if(T.isFunction(D.old)){D.old.call(this);
+}};
+return D;
+},easing:{linear:function(B,A,D,C){return D+C*B;
+},swing:function(B,A,D,C){return((-Math.cos(B*Math.PI)/2)+0.5)*C+D;
+}},timers:[],fx:function(B,C,A){this.options=C;
+this.elem=B;
+this.prop=A;
+if(!C.orig){C.orig={};
+}}});
+T.fx.prototype={update:function(){if(this.options.step){this.options.step.call(this.elem,this.now,this);
+}(T.fx.step[this.prop]||T.fx.step._default)(this);
+if((this.prop=="height"||this.prop=="width")&&this.elem.style){this.elem.style.display="block";
+}},cur:function(A){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null)){return this.elem[this.prop];
+}var B=parseFloat(T.css(this.elem,this.prop,A));
+return B&&B>-10000?B:parseFloat(T.curCSS(this.elem,this.prop))||0;
+},custom:function(A,B,C){this.startTime=AD();
+this.start=A;
+this.end=B;
+this.unit=C||this.unit||"px";
+this.now=this.start;
+this.pos=this.state=0;
+var a=this;
+function D(b){return a.step(b);
+}D.elem=this.elem;
+if(D()&&T.timers.push(D)&&!U){U=setInterval(function(){var b=T.timers;
+for(var c=0;
+c<b.length;
+c++){if(!b[c]()){b.splice(c--,1);
+}}if(!b.length){clearInterval(U);
+U=AB;
+}},13);
+}},show:function(){this.options.orig[this.prop]=T.attr(this.elem.style,this.prop);
+this.options.show=true;
+this.custom(this.prop=="width"||this.prop=="height"?1:0,this.cur());
+T(this.elem).show();
+},hide:function(){this.options.orig[this.prop]=T.attr(this.elem.style,this.prop);
+this.options.hide=true;
+this.custom(this.cur(),0);
+},step:function(C){var D=AD();
+if(C||D>=this.options.duration+this.startTime){this.now=this.end;
+this.pos=this.state=1;
+this.update();
+this.options.curAnim[this.prop]=true;
+var b=true;
+for(var a in this.options.curAnim){if(this.options.curAnim[a]!==true){b=false;
+}}if(b){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;
+this.elem.style.display=this.options.display;
+if(T.css(this.elem,"display")=="none"){this.elem.style.display="block";
+}}if(this.options.hide){T(this.elem).hide();
+}if(this.options.hide||this.options.show){for(var B in this.options.curAnim){T.attr(this.elem.style,B,this.options.orig[B]);
+}}this.options.complete.call(this.elem);
+}return false;
+}else{var A=D-this.startTime;
+this.state=A/this.options.duration;
+this.pos=T.easing[this.options.easing||(T.easing.swing?"swing":"linear")](this.state,A,0,1,this.options.duration);
+this.now=this.start+((this.end-this.start)*this.pos);
+this.update();
+}return true;
+}};
+T.extend(T.fx,{speeds:{slow:600,fast:200,_default:400},step:{opacity:function(A){T.attr(A.elem.style,"opacity",A.now);
+},_default:function(A){if(A.elem.style&&A.elem.style[A.prop]!=null){A.elem.style[A.prop]=A.now+A.unit;
+}else{A.elem[A.prop]=A.now;
+}}}});
+if(document.documentElement.getBoundingClientRect){T.fn.offset=function(){if(!this[0]){return{top:0,left:0};
+}if(this[0]===this[0].ownerDocument.body){return T.offset.bodyOffset(this[0]);
+}var b=this[0].getBoundingClientRect(),C=this[0].ownerDocument,c=C.body,d=C.documentElement,A=d.clientTop||c.clientTop||0,B=d.clientLeft||c.clientLeft||0,D=b.top+(self.pageYOffset||T.boxModel&&d.scrollTop||c.scrollTop)-A,a=b.left+(self.pageXOffset||T.boxModel&&d.scrollLeft||c.scrollLeft)-B;
+return{top:D,left:a};
+};
+}else{T.fn.offset=function(){if(!this[0]){return{top:0,left:0};
+}if(this[0]===this[0].ownerDocument.body){return T.offset.bodyOffset(this[0]);
+}T.offset.initialized||T.offset.initialize();
+var b=this[0],e=b.offsetParent,f=b,A=b.ownerDocument,C,d=A.documentElement,a=A.body,D=A.defaultView,g=D.getComputedStyle(b,null),B=b.offsetTop,c=b.offsetLeft;
+while((b=b.parentNode)&&b!==a&&b!==d){C=D.getComputedStyle(b,null);
+B-=b.scrollTop,c-=b.scrollLeft;
+if(b===e){B+=b.offsetTop,c+=b.offsetLeft;
+if(T.offset.doesNotAddBorder&&!(T.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(b.tagName))){B+=parseInt(C.borderTopWidth,10)||0,c+=parseInt(C.borderLeftWidth,10)||0;
+}f=e,e=b.offsetParent;
+}if(T.offset.subtractsBorderForOverflowNotVisible&&C.overflow!=="visible"){B+=parseInt(C.borderTopWidth,10)||0,c+=parseInt(C.borderLeftWidth,10)||0;
+}g=C;
+}if(g.position==="relative"||g.position==="static"){B+=a.offsetTop,c+=a.offsetLeft;
+}if(g.position==="fixed"){B+=Math.max(d.scrollTop,a.scrollTop),c+=Math.max(d.scrollLeft,a.scrollLeft);
+}return{top:B,left:c};
+};
+}T.offset={initialize:function(){if(this.initialized){return ;
+}var C=document.body,e=document.createElement("div"),c,d,A,b,B,f,a=C.style.marginTop,D='<div style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;"><div></div></div><table style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;" cellpadding="0" cellspacing="0"><tr><td></td></tr></table>';
+B={position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"};
+for(f in B){e.style[f]=B[f];
+}e.innerHTML=D;
+C.insertBefore(e,C.firstChild);
+c=e.firstChild,d=c.firstChild,b=c.nextSibling.firstChild.firstChild;
+this.doesNotAddBorder=(d.offsetTop!==5);
+this.doesAddBorderForTableAndCells=(b.offsetTop===5);
+c.style.overflow="hidden",c.style.position="relative";
+this.subtractsBorderForOverflowNotVisible=(d.offsetTop===-5);
+C.style.marginTop="1px";
+this.doesNotIncludeMarginInBodyOffset=(C.offsetTop===0);
+C.style.marginTop=a;
+C.removeChild(e);
+this.initialized=true;
+},bodyOffset:function(C){T.offset.initialized||T.offset.initialize();
+var A=C.offsetTop,B=C.offsetLeft;
+if(T.offset.doesNotIncludeMarginInBodyOffset){A+=parseInt(T.curCSS(C,"marginTop",true),10)||0,B+=parseInt(T.curCSS(C,"marginLeft",true),10)||0;
+}return{top:A,left:B};
+}};
+T.fn.extend({position:function(){var B=0,C=0,a;
+if(this[0]){var D=this.offsetParent(),A=this.offset(),b=/^body|html$/i.test(D[0].tagName)?{top:0,left:0}:D.offset();
+A.top-=Y(this,"marginTop");
+A.left-=Y(this,"marginLeft");
+b.top+=Y(D,"borderTopWidth");
+b.left+=Y(D,"borderLeftWidth");
+a={top:A.top-b.top,left:A.left-b.left};
+}return a;
+},offsetParent:function(){var A=this[0].offsetParent||document.body;
+while(A&&(!/^body|html$/i.test(A.tagName)&&T.css(A,"position")=="static")){A=A.offsetParent;
+}return T(A);
+}});
+T.each(["Left","Top"],function(B,C){var A="scroll"+C;
+T.fn[A]=function(D){if(!this[0]){return null;
+}return D!==AB?this.each(function(){this==W||this==document?W.scrollTo(!B?D:T(W).scrollLeft(),B?D:T(W).scrollTop()):this[A]=D;
+}):this[0]==W||this[0]==document?self[B?"pageYOffset":"pageXOffset"]||T.boxModel&&document.documentElement[A]||document.body[A]:this[0][A];
+};
+});
+T.each(["Height","Width"],function(B,D){var b=B?"Left":"Top",C=B?"Right":"Bottom",a=D.toLowerCase();
+T.fn["inner"+D]=function(){return this[0]?T.css(this[0],a,false,"padding"):null;
+};
+T.fn["outer"+D]=function(c){return this[0]?T.css(this[0],a,false,c?"margin":"border"):null;
+};
+var A=D.toLowerCase();
+T.fn[A]=function(c){return this[0]==W?document.compatMode=="CSS1Compat"&&document.documentElement["client"+D]||document.body["client"+D]:this[0]==document?Math.max(document.documentElement["client"+D],document.body["scroll"+D],document.documentElement["scroll"+D],document.body["offset"+D],document.documentElement["offset"+D]):c===AB?(this.length?T.css(this[0],A):null):this.css(A,typeof c==="string"?c:c+"px");
+};
+});
+})();
+}
+
+/* platform.js */
+SimileAjax.version="2.2.1";
+SimileAjax.jQuery=jQuery.noConflict(true);
+if(typeof window["$"]=="undefined"){window.$=SimileAjax.jQuery;
+}SimileAjax.Platform.os={isMac:false,isWin:false,isWin32:false,isUnix:false};
+SimileAjax.Platform.browser={isIE:false,isNetscape:false,isMozilla:false,isFirefox:false,isOpera:false,isSafari:false,majorVersion:0,minorVersion:0};
+(function(){var C=navigator.appName.toLowerCase();
+var A=navigator.userAgent.toLowerCase();
+SimileAjax.Platform.os.isMac=(A.indexOf("mac")!=-1);
+SimileAjax.Platform.os.isWin=(A.indexOf("win")!=-1);
+SimileAjax.Platform.os.isWin32=SimileAjax.Platform.isWin&&(A.indexOf("95")!=-1||A.indexOf("98")!=-1||A.indexOf("nt")!=-1||A.indexOf("win32")!=-1||A.indexOf("32bit")!=-1);
+SimileAjax.Platform.os.isUnix=(A.indexOf("x11")!=-1);
+SimileAjax.Platform.browser.isIE=(C.indexOf("microsoft")!=-1);
+SimileAjax.Platform.browser.isNetscape=(C.indexOf("netscape")!=-1);
+SimileAjax.Platform.browser.isMozilla=(A.indexOf("mozilla")!=-1);
+SimileAjax.Platform.browser.isFirefox=(A.indexOf("firefox")!=-1);
+SimileAjax.Platform.browser.isOpera=(C.indexOf("opera")!=-1);
+SimileAjax.Platform.browser.isSafari=(C.indexOf("safari")!=-1);
+var E=function(G){var F=G.split(".");
+SimileAjax.Platform.browser.majorVersion=parseInt(F[0]);
+SimileAjax.Platform.browser.minorVersion=parseInt(F[1]);
+};
+var B=function(H,G,I){var F=H.indexOf(G,I);
+return F>=0?F:H.length;
+};
+if(SimileAjax.Platform.browser.isMozilla){var D=A.indexOf("mozilla/");
+if(D>=0){E(A.substring(D+8,B(A," ",D)));
+}}if(SimileAjax.Platform.browser.isIE){var D=A.indexOf("msie ");
+if(D>=0){E(A.substring(D+5,B(A,";",D)));
+}}if(SimileAjax.Platform.browser.isNetscape){var D=A.indexOf("rv:");
+if(D>=0){E(A.substring(D+3,B(A,")",D)));
+}}if(SimileAjax.Platform.browser.isFirefox){var D=A.indexOf("firefox/");
+if(D>=0){E(A.substring(D+8,B(A," ",D)));
+}}if(!("localeCompare" in String.prototype)){String.prototype.localeCompare=function(F){if(this<F){return -1;
+}else{if(this>F){return 1;
+}else{return 0;
+}}};
+}})();
+SimileAjax.Platform.getDefaultLocale=function(){return SimileAjax.Platform.clientLocale;
+};
+
+
+/* ajax.js */
+SimileAjax.ListenerQueue=function(A){this._listeners=[];
+this._wildcardHandlerName=A;
+};
+SimileAjax.ListenerQueue.prototype.add=function(A){this._listeners.push(A);
+};
+SimileAjax.ListenerQueue.prototype.remove=function(C){var B=this._listeners;
+for(var A=0;
+A<B.length;
+A++){if(B[A]==C){B.splice(A,1);
+break;
+}}};
+SimileAjax.ListenerQueue.prototype.fire=function(B,A){var D=[].concat(this._listeners);
+for(var C=0;
+C<D.length;
+C++){var E=D[C];
+if(B in E){try{E[B].apply(E,A);
+}catch(F){SimileAjax.Debug.exception("Error firing event of name "+B,F);
+}}else{if(this._wildcardHandlerName!=null&&this._wildcardHandlerName in E){try{E[this._wildcardHandlerName].apply(E,[B]);
+}catch(F){SimileAjax.Debug.exception("Error firing event of name "+B+" to wildcard handler",F);
+}}}}};
+
+
+/* data-structure.js */
+SimileAjax.Set=function(A){this._hash={};
+this._count=0;
+if(A instanceof Array){for(var B=0;
+B<A.length;
+B++){this.add(A[B]);
+}}else{if(A instanceof SimileAjax.Set){this.addSet(A);
+}}};
+SimileAjax.Set.prototype.add=function(A){if(!(A in this._hash)){this._hash[A]=true;
+this._count++;
+return true;
+}return false;
+};
+SimileAjax.Set.prototype.addSet=function(B){for(var A in B._hash){this.add(A);
+}};
+SimileAjax.Set.prototype.remove=function(A){if(A in this._hash){delete this._hash[A];
+this._count--;
+return true;
+}return false;
+};
+SimileAjax.Set.prototype.removeSet=function(B){for(var A in B._hash){this.remove(A);
+}};
+SimileAjax.Set.prototype.retainSet=function(B){for(var A in this._hash){if(!B.contains(A)){delete this._hash[A];
+this._count--;
+}}};
+SimileAjax.Set.prototype.contains=function(A){return(A in this._hash);
+};
+SimileAjax.Set.prototype.size=function(){return this._count;
+};
+SimileAjax.Set.prototype.toArray=function(){var A=[];
+for(var B in this._hash){A.push(B);
+}return A;
+};
+SimileAjax.Set.prototype.visit=function(A){for(var B in this._hash){if(A(B)==true){break;
+}}};
+SimileAjax.SortedArray=function(B,A){this._a=(A instanceof Array)?A:[];
+this._compare=B;
+};
+SimileAjax.SortedArray.prototype.add=function(C){var A=this;
+var B=this.find(function(D){return A._compare(D,C);
+});
+if(B<this._a.length){this._a.splice(B,0,C);
+}else{this._a.push(C);
+}};
+SimileAjax.SortedArray.prototype.remove=function(C){var A=this;
+var B=this.find(function(D){return A._compare(D,C);
+});
+while(B<this._a.length&&this._compare(this._a[B],C)==0){if(this._a[B]==C){this._a.splice(B,1);
+return true;
+}else{B++;
+}}return false;
+};
+SimileAjax.SortedArray.prototype.removeAll=function(){this._a=[];
+};
+SimileAjax.SortedArray.prototype.elementAt=function(A){return this._a[A];
+};
+SimileAjax.SortedArray.prototype.length=function(){return this._a.length;
+};
+SimileAjax.SortedArray.prototype.find=function(D){var B=0;
+var A=this._a.length;
+while(B<A){var C=Math.floor((B+A)/2);
+var E=D(this._a[C]);
+if(C==B){return E<0?B+1:B;
+}else{if(E<0){B=C;
+}else{A=C;
+}}}return B;
+};
+SimileAjax.SortedArray.prototype.getFirst=function(){return(this._a.length>0)?this._a[0]:null;
+};
+SimileAjax.SortedArray.prototype.getLast=function(){return(this._a.length>0)?this._a[this._a.length-1]:null;
+};
+SimileAjax.EventIndex=function(B){var A=this;
+this._unit=(B!=null)?B:SimileAjax.NativeDateUnit;
+this._events=new SimileAjax.SortedArray(function(D,C){return A._unit.compare(D.getStart(),C.getStart());
+});
+this._idToEvent={};
+this._indexed=true;
+};
+SimileAjax.EventIndex.prototype.getUnit=function(){return this._unit;
+};
+SimileAjax.EventIndex.prototype.getEvent=function(A){return this._idToEvent[A];
+};
+SimileAjax.EventIndex.prototype.add=function(A){this._events.add(A);
+this._idToEvent[A.getID()]=A;
+this._indexed=false;
+};
+SimileAjax.EventIndex.prototype.removeAll=function(){this._events.removeAll();
+this._idToEvent={};
+this._indexed=false;
+};
+SimileAjax.EventIndex.prototype.getCount=function(){return this._events.length();
+};
+SimileAjax.EventIndex.prototype.getIterator=function(A,B){if(!this._indexed){this._index();
+}return new SimileAjax.EventIndex._Iterator(this._events,A,B,this._unit);
+};
+SimileAjax.EventIndex.prototype.getReverseIterator=function(A,B){if(!this._indexed){this._index();
+}return new SimileAjax.EventIndex._ReverseIterator(this._events,A,B,this._unit);
+};
+SimileAjax.EventIndex.prototype.getAllIterator=function(){return new SimileAjax.EventIndex._AllIterator(this._events);
+};
+SimileAjax.EventIndex.prototype.getEarliestDate=function(){var A=this._events.getFirst();
+return(A==null)?null:A.getStart();
+};
+SimileAjax.EventIndex.prototype.getLatestDate=function(){var A=this._events.getLast();
+if(A==null){return null;
+}if(!this._indexed){this._index();
+}var C=A._earliestOverlapIndex;
+var B=this._events.elementAt(C).getEnd();
+for(var D=C+1;
+D<this._events.length();
+D++){B=this._unit.later(B,this._events.elementAt(D).getEnd());
+}return B;
+};
+SimileAjax.EventIndex.prototype._index=function(){var D=this._events.length();
+for(var E=0;
+E<D;
+E++){var C=this._events.elementAt(E);
+C._earliestOverlapIndex=E;
... 41602 lines suppressed ...