You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openoffice.apache.org by bu...@apache.org on 2017/04/12 14:40:25 UTC
svn commit: r1010330 [19/29] - in /websites/staging/ooo-site/trunk: cgi-bin/
content/ content/da/product/ content/download/next/
content/scripts/simile-widget/ content/scripts/simile-widget/ajax/
content/scripts/simile-widget/ajax/2.2.1.fake/ content/s...
Added: websites/staging/ooo-site/trunk/content/scripts/simile-widget/ajax/2.2.4/simile-ajax-api-debug.js
==============================================================================
--- websites/staging/ooo-site/trunk/content/scripts/simile-widget/ajax/2.2.4/simile-ajax-api-debug.js (added)
+++ websites/staging/ooo-site/trunk/content/scripts/simile-widget/ajax/2.2.4/simile-ajax-api-debug.js Wed Apr 12 14:40:22 2017
@@ -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
[... 1504 lines stripped ...]