Base = {
addToEvent: function(element, ev_type, fn, sortIndex) {
if (element.addEventListener) {
element.addEventListener(ev_type, fn, false);
return true;} else if (element.attachEvent) {
var r = element.attachEvent('on' + ev_type, fn);
return r;} else {
element['on' + ev_type] = fn;}},
initialiseEventHandling: function(e) {
if (e.clientX) this.EventObject = function(e) {
return {
e: event,
mouseCoord: {x: event.clientX, y: event.clientY},
srcElement: event.srcElement};}
else this.EventObject = function(e) {
return {
e: e,
mouseCoord: {x: e.pageX, y: e.pageY},
srcElement: e.target};} },
EventObject: null}
Base.addToEvent(window, 'load', function(e) {Base.initialiseEventHandling(e);});
function text_IsEmpty(){
return ((this.value.replace(/\s*/g, '')).length ==0);}
function text_Trim(){
return ((this.value.replace(/(^\s*)|(\s*$)/g, '')));}
function WindowDimensions(){
return {	width: (window.innerWidth)?window.innerWidth:document.documentElement.clientWidth,
height: (window.innerHeight)?window.innerHeight:document.documentElement.clientHeight};}
function ParamsToQS(params){
var qs = '';
for (var p in params) 
qs+= ((qs.length)?'&':'?') + p + '=' + params[p];
return qs;}
function GetAbsolutePosition(o, stop_at_element) {
if (!o || typeof o != "object") {
return false;}
if (!o.offsetParent) {
return false;}
var position = new Object();
position["left"] = parseInt(o.offsetLeft);
position["top"] = parseInt(o.offsetTop);
while (o = o.offsetParent) {
if (o == stop_at_element) {
break;}
position["left"] += parseInt(o.offsetLeft);
position["top"] += parseInt(o.offsetTop);}
return position;}
function createCookie(name,value){
var date = new Date();
date.setTime(date.getTime()+(365*24*60*60*1000));
var expires = "; expires="+date.toGMTString();
document.cookie = name + "=" + value + "; path=/" + expires;}
function createNPCookie(name,value){
document.cookie = name + "=" + value + "; path=/";}
function readCookie(name){
name = name + "=";
var cookiePairs = document.cookie.split(';');
var cookie;
for(var i=0;i < cookiePairs.length;i++){
cookie = cookiePairs[i];
while (cookie.charAt(0)==' ') cookie = cookie.substring(1,cookie.length);
if (cookie.indexOf(name) == 0) return cookie.substring(name.length,cookie.length);}
return null;}
function eraseCookie(name){
document.cookie = name + "=;expires=-1; path=/";}
function setOpacity(el, value) {
el.style.opacity = value + '';
el.style.filter = 'alpha(opacity=' + (value * 100) + ')';	}
function kindDate(utime) {
var d = new Date();
var today = new Date();
var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'June', 'July', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
function zpad(value) {
return (value < 10)?'0'+value:''+value;	}
d.setTime(utime * 1000);
if (d.getDate() != today.getDate() || d.getMonth() != today.getMonth() || d.getFullYear() != today.getFullYear()) {
return  zpad(d.getHours()) + ':' + zpad(d.getMinutes()) + ', ' + months[d.getMonth()] + ' ' + d.getDate();}
else {
return zpad(d.getHours()) + ':' + zpad(d.getMinutes()) + ', today';}}
var J2 = { version: 1.0 };
J2.Core = new (function() {
this.eventHandler = function() {
var events = [];
this.eventIndexes = new ( function() {
var first = 999, last = -999;
this.first = function() { return first--; };
this.last = function() { return last++; };} );
this.add = function(fn, sortIndex) {
events.push([fn,sortIndex||99]);}
this.remove = function(fn) {
for (var i = events.length-1;i>=0;i--) {
if (events[i][0] === fn) {
delete events[i][0];
events.splice(i,1);}}}
this.handleEvent = function(e) {
e = e || event;
if (e.target == null) {
e.target = e.srcElement;}
var returnVal = true, funcReturnVal;
events.sort( function(a,b) {
return a[1] < b[1] ? 1 : -1;} );
for (var i = 0, j = events.length; i<j; i++) {
funcReturnVal = events[i][0].call(this, e);
if (returnVal && funcReturnVal == false) {
returnVal = false;}}
return returnVal;}};
var loadEventHandler = new this.eventHandler();
window.addLoadEvent = loadEventHandler.add;
window.loadEventIndexes = loadEventHandler.eventIndexes;
if (typeof window.onload == "function") {
loadEventHandler.add( window.onload, eventHandler.loadEventIndexes.first() );}
window.onload = loadEventHandler.handleEvent;
var domReadyEventHandler = new this.eventHandler();
window.addDOMReadyEvent = domReadyEventHandler.add;
if (navigator.userAgent.search(/WebKit/i) !== -1) {
window.DOMLoadTimer = setInterval(function () {
if (document.readyState.search(/loaded|complete/i) !== -1) {
clearInterval(window.DOMLoadTimer);
domReadyEventHandler.handleEvent({});}}, 10);} else if (document.addEventListener) {
document.addEventListener("DOMContentLoaded", domReadyEventHandler.handleEvent, false);} else {
window.addLoadEvent( domReadyEventHandler.handleEvent, 1000 );}
var boundElements = [];
this.bindElementTools = function(o, isBasicBinding) {
if (!o || o == null) {
return;}
if (o.isElementBound != true) {
copyMethods( J2.Element, o );
boundElements.push(o);}
if (isBasicBinding === true) {
return;}
if (o.isExtraOverridesBound !== true) {
try {
o.getElementsByClass = o.getElementsByClassName = J2.Core.newDOMMethods.getElementsByClassName( o.querySelectorAll ? o.querySelectorAll : o.getElementsByClassName || null );
o.getElementsByTagName = J2.Core.newDOMMethods.getElementsByTagName( o.getElementsByTagName );
o.appendChild = J2.Core.newDOMMethods.appendChild( o.appendChild );
o.replaceChild = J2.Core.newDOMMethods.replaceChild( o.replaceChild );
o.insertBefore = J2.Core.newDOMMethods.insertBefore( o.insertBefore );} catch (e) {}
o.isExtraOverridesBound = true;}}
this.addElementTool = function(id, func) {
if (typeof func != "function" || typeof id != "string") {
return;}
J2.Element[id] = func;
for (var i = boundElements.length-1, el; i>=0, el = boundElements[i]; i--) {
el[id] = func;}}
function copyMethods( source, target ) {
if (typeof source == "object") {
for (var method in source) {
target[method] = source[method];}}}
this.sortToDocumentOrder = function(elements) {
return elements.sort( function(a,b) {
return 3 - (comparePosition(a,b) & 6);} );}
function comparePosition(a,b) {
return a.compareDocumentPosition ? a.compareDocumentPosition(b) : a.contains ? (a != b && a.contains(b) && 16) + (a != b && b.contains(a) && 8) + (a.sourceIndex >= 0 && b.sourceIndex >= 0 ? (a.sourceIndex < b.sourceIndex && 4) + (a.sourceIndex > b.sourceIndex && 2) : 1) + 0 : 0;}
this.newDOMMethods = new ( function() {
document.oldGetElementById = document.getElementById;
document.oldCreateElement = document.createElement;
this.getElementById = function(id) {
var el = document.oldGetElementById(id);
J2.Core.bindElementTools(el);
return el;}
this.createElement = function(type, options) {
var el = document.oldCreateElement(type);
J2.Core.bindElementTools(el);
if (typeof options == "object") {
if (options.cssClass != null) {
el.setCssClass(options.cssClass);
delete options.cssClass;}
for (var item in options) {
el[item] = options[item];}}
return el;}
this.getElementsByTagName = function(f) {
return function(tag) {
this.getElementsByTagName = f;
var els = this.getElementsByTagName(tag);
for (var i = 0, el; el = els[i]; i++) {
J2.Core.bindElementTools(el);}
return els;}}
this.getElementsByClassName = function(f) {
var handleArguments = function(args) {
var cssClass = arguments[0].cssClass || arguments[0], tags = arguments[0].tags || "*", callback = arguments[0].callback || null;
return {tags:isArray(tags) ? tags.join(",").toLowerCase().split(",") : tags.toLowerCase().split(","), cssClass: cssClass, callback: callback};}
if (f == null) {
return function() {
var args = handleArguments(arguments[0]);
this.getElementsByClassName = getElementsByClassNameLegacy;
return this.getElementsByClassName.call(this, args.cssClass, args.tags, args.callback);}} else if (document.querySelectorAll) {
return function() {
var args = handleArguments(arguments[0]);
this.getElementsByClassName = f;
var queryText = "";
if (args.tags == "*") {
queryText = "." + args.cssClass;} else {
for (var i = 0, j = args.tags.length; i<j; i++) {
if (i != 0) {
queryText += ",";}
queryText += args.tags[i] + "." + args.cssClass;}}
var els = Array.prototype.copy.call(this.getElementsByClassName( queryText ));
if (typeof args.callback == "function") {
for (i = 0, j = els.length; i<j; i++) {
args.callback.call(els[i], els[i]);}}
return els;}} else {
return function() {
var args = handleArguments(arguments[0]);
this.getElementsByClassName = f;
var els = Array.prototype.copy.call(this.getElementsByClassName( args.cssClass )), result = [];
if (args.tags != "*") {
for (var i = 0, j = els.length; i<j; i++) {
if (args.tags.contains( els[i].tagName.toLowerCase() )) {
result.push(els[i]);
if (typeof args.callback == "function") {
args.callback.call(els[i], els[i]);}}}}
return result;}}}
this.appendChild = function(f) {
return function(el) {
this.appendChild = f;
if (typeof J2.AJAX == "function" && el instanceof J2.AJAX) {
el.setSuccessHandler( function(ajax) {
var tempNode = document.createElement("span", {innerHTML:ajax.getResponseText()});
this.appendChild(tempNode);
tempNode.remove(true);} );
el.setScope(this);
el.send();} else {
J2.Core.bindElementTools(el);
this.appendChild(el);}}}
this.replaceChild = function(f) {
return function(newEl, oldEl) {
this.replaceChild = f;
if (typeof J2.AJAX == "function" && newEl instanceof J2.AJAX) {
newEl.setSuccessHandler( function(ajax) {
var tempNode = document.createElement("span", {innerHTML:ajax.getResponseText()});
this.replaceChild(tempNode, oldEl);
tempNode.remove(true);} );
newEl.setScope(this);
newEl.send();} else {
J2.Core.bindElementTools(newEl);
this.replaceChild(newEl,oldEl);}}}
this.insertBefore = function(f) {
return function(newNode, refNode) {
this.insertBefore = f;
if (typeof J2.AJAX == "function" && newNode instanceof J2.AJAX) {
newNode.setSuccessHandler( function(ajax) {
var tempNode = document.createElement("span", {innerHTML:ajax.getResponseText()});
this.insertBefore(tempNode, refNode);
tempNode.remove(true);} );
newNode.setScope(this);
newNode.send();} else {
J2.Core.bindElementTools(newNode);
this.insertBefore(newNode,refNode);}}}
function getElementsByClassNameLegacy(cssClass, tags, callback) {
var results = [];
var elements = this.getElementsByTagNames(tags);
for (var i=0, j=elements.length, element; i<j, element = elements[i]; i++) {
if (J2.Element.hasCssClass.call(element, cssClass)) {
results.push( element );
if (typeof callback == "function") {
callback.call(element, element);}}}
return results;}} );
String.prototype.trim = function () {
return this.replace(/^\s\s*/, '').replace(/\s\s*$/, '');};
Array.prototype.contains = function(val) {
for (var i = this.length-1; i>=0; i--) {
if (this[i] == val) {
return true;}}
return false;};
Array.prototype.copy = function() {
if (this.length == null || this.length == 0) {
return [];}
var newArray;
try {
newArray = Array.prototype.slice.call(this,0);
if (newArray[0] == null) {
throw "";}} catch (e) {
newArray = [];
for (var i = 0, j = this.length; i<j; i++) {
newArray.push(this[i]);}}
return newArray;};
window.isArray = function(o) {
return o instanceof Array;}});
J2.Element = new (function() {
this.isElementBound = true
this.getElementsByTagNames = function(list) {
if (typeof list == "string") {
if (arguments.length == 1) {
var tagNames = list.split(",");} else {
var tagNames = [];
for (var i = 0, j = arguments.length; i<j; i++) {
if (typeof arguments[i] == "string") {
tagNames.push(arguments[i]);}}}} else if ( isArray(list) ) {
var tagNames = list;} else {
return null;}
var results = null;
for (var i=0, j=tagNames.length; i<j; i++) {
var elements = Array.prototype.copy.call(this.getElementsByTagName(tagNames[i].trim()));
results = (results == null) ? elements : results.concat(elements);}
return J2.Core.sortToDocumentOrder(results);}
this.hasCssClass = function(cssClass) {
return this.className.match(new RegExp("\\b" + cssClass + "\\b")) == cssClass;}
this.addCssClass = function(cssClass) {
if ( !this.hasCssClass(cssClass) ) {
this.className = this.className.trim() + " " + cssClass.trim();}
return this;}
this.removeCssClass = function(cssClass) {
if ( this.hasCssClass(cssClass) ) {
this.className = this.className.replace(cssClass.trim(), "").trim();}
return this;}
this.setCssClass = function(cssClass) {
if (cssClass != null && cssClass != "") {
this.className = cssClass;}
return this;}
this.setOpacity = function() {
if (window.ActiveXObject) {
return function(opacityLevel) {
this.style.filter = (opacityLevel == 1) ? '' : "alpha(opacity=" + opacityLevel * 100 + ")";
return this;}} else {
return function(opacityLevel) {
this.style.opacity = opacityLevel;
return this;}}}();
this.setStyle = function(property, value) {
property = property.replace(/-\D/g, function(match) {return match.charAt(1).toUpperCase();})
if (property == "opacity") {
return this.setOpacity(value);}
if (property == "float") {
property = (window.ActiveXObject) ? "styleFloat" : "cssFloat";}
if (typeof value == "number" && (!["zIndex", "zoom"].contains(property))) {
value = value + "px";}
this.style[property] = value;
return this;}
this.getComputedStyle = this.getStyle = this.getOpacity = function(property) {
property = property.replace(/-\D/g, function(match) {return match.charAt(1).toUpperCase();})
if (property == "opacity" && window.ActiveXObject) {
var opacityLevel = this.getComputedStyle("filter");
return opacityLevel == "" ? 1 : parseFloat(opacityLevel.split("=")[1]) / 100;}
if (property == "float") {
property = (window.ActiveXObject) ? "styleFloat" : "cssFloat";}
if (window.ActiveXObject && ["width", "height"].contains(property)) {
return this["offset"+property.replace(/./, function(match) {return match.toUpperCase();})];}
var property_value = null;
if (this.currentStyle) {
property_value = this.currentStyle[property];} else if (window.getComputedStyle) {
property_value = document.defaultView.getComputedStyle(this,null).getPropertyValue(property);}
return property_value;}
this.position = function(relativeTo) {
var relative = {left:0, top:0};
if (typeof relativeTo == "object" && relativeTo.isElementBound) {
relative = relativeTo.position();}
var position = {left:parseInt(this.offsetLeft) - relative.left, top:parseInt(this.offsetTop) - relative.top}, o = this;
while (o = o.offsetParent) {
position["left"] += parseInt(o.offsetLeft);
position["top"] += parseInt(o.offsetTop);}
return position;}
this.hasChildElements = function(type) {
return this.getElementsByTagName(type || "*").length > 0;}
this.getNextSibling = function(type) {
var o = this;
while (o = o.nextSibling) {
if (o.nodeType != 1) {
continue;}
if (type == null) {
break;}
if (type.toLowerCase() == o.tagName.toLowerCase()) {
break;}}
if (o == null || o.nodeType != 1) {
return null;}
J2.Core.bindElementTools(o);
return o;}
this.getPreviousSibling = function(type) {
var o = this;
while (o = o.previousSibling) {
if (o.nodeType != 1) {
continue;}
if (type == null) {
break;}
if (type.toLowerCase() == o.tagName.toLowerCase()) {
break;}}
if (o == null || o.nodeType != 1) {
o = this;}
Tools.Core.bindElementTools(o);
return o;}
this.getParentByType = function(type) {
var o = this;
if (type == null) {
Tools.Core.bindElementTools(this.parentNode);
return this.parentNode;}
while (o = o.parentNode) {
if (o.nodeType != 1) {
continue;}
if (o.tagName.toLowerCase() == type.toLowerCase()) {
J2.Core.bindElementTools(o);
return o;}}
return null;}
this.getParentByStyle = function(property, value) {
var o = this;
while (o = o.parentNode) {
if (J2.Element.getStyle.call(o, property) == value) {
J2.Core.bindElementTools(o);
return o;}}
return null;}
this.insertAfter = function(newNode, refNode) {
if (typeof J2.AJAX == "function" && newNode instanceof J2.AJAX) {
newNode.setSuccessHandler( function(ajax) {
var tempNode = document.createElement("span", {innerHTML:ajax.getResponseText()});
this.insertAfter(tempNode, refNode);
tempNode.remove(true);} );
newNode.setScope(this);
newNode.send();} else {
var sibling = refNode.getNextSibling();
if (refNode == null || sibling == refNode) {
this.appendChild(newNode);} else {
this.insertBefore(newNode, sibling);}}}
this.removeAllChildren = function() {
while (this.hasChildNodes()) {
this.removeChild( this.firstChild );}
return this;}
this.remove = function(retainChildren) {
retainChildren = retainChildren || false;
if (this.parentNode) {
if (retainChildren == true && this.hasChildElements()) {
while (this.childNodes.length > 0) {
this.parentNode.insertBefore( this.childNodes[0], this );}}
this.parentNode.removeChild(this);}}
this.addEvent = function(type, fn, sortIndex) {
if (typeof fn != "function") {
return;}
if (!this.events) {
this.events = {};}
if (!this.events[type]) {
this.events[type] = new J2.Core.eventHandler();
this["on" + type] = this.events[type].handleEvent;}
this.events[type].add(fn, sortIndex);
return fn;}
this.removeEvent = function(type, fn) {
var events = this.events;
if (events && events[type] instanceof J2.Core.eventHandler) {
events[type].remove(fn);}}
this.addDelegate = function(type, check, handler) {
if (typeof check != "function" || typeof handler != "function") {
return;}
this.delegateFunctions = {check: check, handler: handler};
this.addEvent(type, delegateHandler, 1000);}
function delegateHandler(e) {
var delegateFunctions = this.delegateFunctions;
if (delegateFunctions.check.call(e.target)) {
return delegateFunctions.handler.call(this, e);}
return true;}});
(function() {
if (typeof HTMLElement == "function") {
J2.Core.bindElementTools( HTMLElement.prototype, true );}
document.getElementsByTagName = J2.Core.newDOMMethods.getElementsByTagName( document.getElementsByTagName );
document.getElementsByClass = document.getElementsByClassName = J2.Core.newDOMMethods.getElementsByClassName( document.querySelectorAll ? document.querySelectorAll : document.getElementsByClassName || null );
document.getElementById = J2.Core.newDOMMethods.getElementById;
document.createElement = J2.Core.newDOMMethods.createElement;
document.getElementsByTagNames = J2.Element.getElementsByTagNames;
window.addEvent = function(type, fn, sortIndex) {
type = type.toLowerCase();
if (type == "load") {
window.addLoadEvent(fn, sortIndex);} else if (type == "domcontentloaded" || type == "domready") {
window.addDOMReadyEvent(fn, sortIndex);} else {
J2.Element.addEvent.apply(this, arguments);}};})();
if (typeof J2 !== "object") var J2 = {};
J2.QueryString = new ( function() {
var queryString = null;
this.get = function(key) {
if (key == null || key === "") return null;
if (queryString == null) {
queryString = {};
var qs = location.search.slice(1).split("&"), qsElements;
for (var i = 0, j = qs.length; i<j; i++) {
qsElements = qs[i].split("=");
if (queryString[qsElements[0]]) {
if (queryString[qsElements[0]] instanceof Array) {
queryString[qsElements[0]].push(qsElements[1] || "")} else {
queryString[qsElements[0]] = [queryString[qsElements[0]], qsElements[1]];}} else {
queryString[qsElements[0]] = qsElements[1] || "";}}}
return (queryString[key]!=undefined)?queryString[key]:null;};} )();
var ADIJ = function() {
var requestDetails = {
getResponseText: function() {
return this.json || "";}};
if (typeof arguments[0] == "object") {
requestDetails.URL = arguments[0].URL || null;
requestDetails.scope = (typeof arguments[0].scope == "object") ? arguments[0].scope : null;
requestDetails.onSuccess = (typeof arguments[0].onSuccess == "function") ? arguments[0].onSuccess : null;}
this.setUrl = function(url) {
requestDetails.URL = url;}
this.setSuccessHandler = function(func) {
if (typeof func == "function") {
requestDetails.onSuccess = func;}}
this.send = function() {
if (requestDetails.URL == null || requestDetails.URL == "") {
return false;}
if (requestDetails.id == null) {
requestDetails.id = this.getID();}
writeScriptTag(requestDetails);
this.Requests[requestDetails.id] = requestDetails;}
function writeScriptTag(requestDetails) {
requestDetails.scriptTag = document.createElement("script");
requestDetails.scriptTag.type = "text/javascript";
requestDetails.scriptTag.src = requestDetails.URL + (requestDetails.URL.indexOf("?") > -1 ? "&" : "?") + "uqid=" + (new Date()).getTime() + "&rid=" + requestDetails.id;
if (this.HeadTag == null) {
this.HeadTag = document.getElementsByTagName("head")[0];}
this.HeadTag.appendChild(requestDetails.scriptTag);}}
ADIJ.prototype = {
HeadTag: null,
RequestCount: 0,
Requests: {},
handleResponse: function(id, json, sys_date) {
if (this.Requests[id] == null) {
return false;}
this.SysDate = sys_date;
this.Requests[id].json = json;
this.Requests[id].SysDate = sys_date;
this.Requests[id].scriptTag.parentNode.removeChild(this.Requests[id].scriptTag);
this.Requests[id].onSuccess.call( this.Requests[id].scope || this.Requests[id].onSuccess, this.Requests[id] );},
getID: function() {
adij_request_count++;
return "ADIJ_" + adij_request_count;}}
var adij_request_count = 0;
var AJAX = function() {
var timeoutLength = 12000;
var requestObj = null;
var requestDetails = { method: "get" };
var timeout = null;
var me = this;
var data_params = {};
if (typeof arguments[0] == "object") {
requestDetails.URL = arguments[0].URL || null;
requestDetails.method = arguments[0].method || requestDetails["method"];
requestDetails.onSuccess = (typeof arguments[0].onSuccess == "function") ? arguments[0].onSuccess : null;
requestDetails.onFail = (typeof arguments[0].onFail == "function") ? arguments[0].onFail : null;
requestDetails.scope = (typeof arguments[0].scope == "object") ? arguments[0].scope : null;
requestDetails.timeoutLength = arguments[0].timeoutLength || timeoutLength;}
function addRequestHeader(key, value) {
if (key != null && value != null) {
requestDetails.requestHeaders = requestDetails.requestHeaders || {};
requestDetails.requestHeaders[key] = value;}}
this.addRequestHeader = addRequestHeader;
this.setUrl = function(url) {
requestDetails.URL = url;}
this.setSuccessHandler = function(func) {
if (typeof func == "function") {
requestDetails.onSuccess = func;}}
this.setFailHandler = function(func) {
if (typeof func == "function") {
requestDetails.onFail = func;}}
this.setTimeoutLength = function(length) {
timeoutLength = length;}
this.addParameter = function(name, value) {
data_params[name] = value;}
this.dataString = function() {
var data = "";
for (var dp in data_params)
data+=((data.length!=0)?'&':'')+dp+'='+encodeURI(data_params[dp]).replace('&', '%26').replace('+', '%2B');
return data;}
this.send = function() {
if (requestDetails.URL == null || requestDetails.URL == "") {
return false;}
if (arguments.length < 1 || data == null) {
data = "";}
requestObj = requestObj || this.getRequestObject();
requestObj.open(requestDetails.method, requestDetails.URL, true);
if (requestDetails.method.toLowerCase() == "post") {
requestObj.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");}
for (var key in requestDetails.requestHeaders) {
requestObj.setRequestHeader(key, requestDetails.requestHeaders[key]);}
requestObj.onreadystatechange = this.handleReadyStateChange;
timeout = setTimeout( this.handleTimeout, requestDetails.timeoutLength );
requestObj.send(this.dataString());}
this.close = function() {
requestObj = null;}
this.handleTimeout = function() {
if (typeof requestDetails.onFail == "function") {
me.clearTimeout();
me.close();
requestDetails.onFail.call( requestDetails.scope || requestDetails.onFail, me, AJAX.FailureCodes.timeout );}}
this.clearTimeout = function() {
clearTimeout(timeout);}
this.handleReadyStateChange = function() {
if (requestObj.readyState == 4) {
me.clearTimeout();
if (requestObj.status == 200) {
requestDetails.onSuccess.call( requestDetails.scope || requestDetails.onSuccess, me );} else {
if (typeof requestDetails.onFail == "function") {
var status = requestObj.status;
var failureCode = AJAX.FailureCodes.general;
for (var failType in AJAX.FailureCodes) {
if (AJAX.FailureCodes[failType] == status) {
failureCode = AJAX.FailureCodes[failType];
break;}}
requestDetails.onFail.call( requestDetails.scope || requestDetails.onFail, me, failureCode );}}}}
this.getResponseHeader = function(headerName) {
return requestObj.getResponseHeader(headerName);}
this.getAllResponseHeaders = function() {
return requestObj.getAllResponseHeaders();}
this.getResponseText = function() {
return requestObj.responseText;}
this.getResponseXML = function() {
return requestObj.responseXML;}
this.getUrl = function() {
return requestDetails.URL;}}
AJAX.prototype.getRequestObject = function() {
AJAX.supported = true;
if (typeof XMLHttpRequest != "undefined" && typeof XMLHttpRequest != null) {
return function() {
return new XMLHttpRequest();}; } else if (window.ActiveXObject) {
return function() {
return new ActiveXObject("Microsoft.XMLHTTP");};} else {
AJAX.supported = false;
return function() {
return null;}}}();
AJAX.FailureCodes = {
general: "xx1",
unauthorised: 401,
notFound: 404,
timeout: 408,
server: 500	}
JOB = {};
JOB.ObjectInstantiator = {
_uqid_count: 0,
_control_lib: new Object(),
_class_lib: new Object(),
onPageLoad: function() {
this._app_namespace = (JOB.APPLICATION_NAMESPACE)?JOB.APPLICATION_NAMESPACE:null;        
this._instantiateObjects();
this._callOnBoundEvents();
if (!J2) this._bindToJ2 = function() {};},
_typeCheck:function(el) {
var tc=el.id.match(/^(CTL|MEM|CLS)\_/g);
return (tc)?tc[0].replace('_',''):null;    },
_callOnBoundEvents: function() {
for (var ob in this._control_lib) 
if (this._control_lib[ob].onBound) this._control_lib[ob].onBound();},
_instantiateObjects: function() {
var root_el = document.body;
this._instantiateObjects_Recurse(root_el, null);
this._stripClassElements();}, 
_instantiateObjects_Recurse: function(parent_el, in_object) {
var children = parent_el.childNodes;
for (var i = children.length - 1; i>=0; i--)
if (children[i].nodeType == 1){
var el_type = this._typeCheck(children[i]);
switch(el_type){
case 'CTL': if (in_object == 'CTL' || in_object == 'CLS') {alert('CANNOT EMBED CONTROL IN CONTROL OR CLASS'); return;}
this._instantiateControl(children[i]);
this._instantiateObjects_Recurse(children[i], el_type);
break;
case 'CLS': 
this._instantiateClass(children[i]);
this._instantiateObjects_Recurse(children[i], el_type);
break;
default:    this._instantiateObjects_Recurse(children[i], in_object);}}},
_instantiateControl: function(obj_el)   {
var obj_name = obj_el.id.replace(/^CTL\_/, '');
var obj = this._getSourceObject(obj_name);
this._bind(obj, obj_el, obj_name);
this._control_lib[obj_name] = obj;
this._bindToJ2(obj_el);},
_bindToJ2: function(el) {
J2.Core.bindElementTools(el);}
,
_instantiateClass: function(cls_el)     {
var cls_name = cls_el.id.replace(/^CLS\_/, '');
cls_el.id = this.UQID();
this._class_lib[cls_name] = cls_el;},
_stripClassElements: function() {
var classes = new Array();
for (var c in this._class_lib) classes.push(c);
for (var i=0; i<classes.length; i++){
this._class_lib[classes[i]].parentNode.removeChild(this._class_lib[classes[i]]);}},
New: function(cls_name, init_params) {
if (!this._class_lib[cls_name]) {alert('NO CLASS DEFINED WITH NAME: ' + cls_name); return;}
var ns_obj = (this._app_namespace)?self[this._app_namespace]:self;
var obj = (ns_obj[cls_name])?(new ns_obj[cls_name](init_params)):(new Object());
var el = this._class_lib[cls_name].cloneNode(true);
el.id = this.UQID();
this._bind(obj, el, cls_name);
obj._instance_id = this.UQID();
if (obj.onBound) obj.onBound();
return obj;},
Bind: function(obj, cls_name, params) {
for (var param in params) obj[param] = params[param];
var el = this._class_lib[cls_name].cloneNode(true);
el.id = this.UQID();
this._bindToJ2(el);
this._bind(obj, el, cls_name);
obj._instance_id = this.UQID();
if (obj.onBound) obj.onBound();
return obj;		},
_bind: function(obj, el, obj_type) {
obj._el = el;
obj._type = obj_type;
this._harnessMembers(el, obj);    
obj.rootElement = el;},
_getSourceObject: function(obj_name) {
var ns_obj = (this._app_namespace)?self[this._app_namespace]:self;
if (!ns_obj[obj_name]) ns_obj[obj_name] = new Object();
return ns_obj[obj_name];},
_harnessMembers: function(src_el, target_obj)  {
var children = src_el.childNodes;
for (var i=children.length - 1; i>=0; i--)
if (children[i].nodeType == 1){
var el_type = this._typeCheck(children[i]);
if (el_type == 'MEM') {
var typecheck = children[i].className.match(/type\:\S*/g);
var member_name = children[i].id.replace(/^MEM\_/, '');
children[i].id = this.UQID();
this._bindToJ2(children[i]);
if (typecheck){
switch(typecheck[0]){
case 'type:container':      
target_obj[member_name] = new JOB.Container(children[i]);
break;
default: 
break;}}
else {  
target_obj[member_name] =  children[i];}}
if (el_type != 'CLS') this._harnessMembers(children[i], target_obj);}},
UQID: function() {return 'UQID'+this._uqid_count++;}}
JOB.Container = function(container_el) {
this._el = container_el;
this.Element = this._el;}
JOB.Container.prototype = {
Add: function(obj) {
obj._el.__bref = obj;        
this._el.appendChild(obj._el);},
AddFirst: function(obj) {
obj._el.__bref = obj;        
if (this._el.firstChild) {
this._el.insertBefore(obj._el, this._el.firstChild);}
else this._el.appendChild(obj._el);	},
AddBefore: function(obj, obj_before) {
obj._el.__bref = obj;        
this._el.insertBefore(obj._el, obj_before._el);		},
Remove: function(obj) {
this._el.removeChild(obj._el);
obj._el.__bref = null;},
GetChildren: function() {
var child_objs = new Array();
var node = this._el.firstChild;
while (node) {
if (node.__bref) child_objs.push(node.__bref);
node = node.nextSibling;}
return child_objs;},
GetMembers: function() {
var child_objs = new Array();
var node = this._el.firstChild;
while (node) {
if (node.__bref) child_objs.push(node.__bref);
node = node.nextSibling;}
return child_objs;},
RemoveAll: function() {
var child_objs = this.GetMembers();
for (var i=0, obj; i<child_objs.length, obj=child_objs[i]; i++)
this.Remove(obj);}}
window.addDOMReadyEvent(function() {JOB.ObjectInstantiator.onPageLoad();}, 0);
JOB.APPLICATION_NAMESPACE = "App";
App = {
state: null,
mouse_down: false,
polling: false,
poll_interval: null,
poll_date_ticks: null,
has_edit_bubble: false,
base_href: document.location.href.split('?')[0].split('#')[0],
base_url: document.location.href.split('?')[0].split('#')[0].replace('index.html', ''),
open_spaces: {},
params: {
name: 'Thinkbubble',
limit: 10000,
bubble_delta: {x: -20, y: -20},
bounds_buffer: 240,
load_buffer: 200,
pan_limit: 3000,
pan_steps: 15,
poll_interval_wait: 10000,
bubblebar_text_limit: 50,
bubblebar_width: 235,
show_public_spaces: true,
feed_ext_rss: 'feed/rss.xml',
feed_ext_atom: 'feed/atom.xml',
anonymous_user_name: 'lurker',
unkeyed_user_flag: 'guest'},
zoom_levels: {
z100: {number: 100, scale: 1.0, className: 'zoom100', name: 'z100', zdown: 'z50'}, 
z50: {number: 50, scale: 0.50, className: 'zoom50', name: 'z50', zup: 'z100', zdown: 'z25'}, 
z25: {number: 25, scale: 0.25, className: 'zoom25', name: 'z25', zup: 'z50', zdown: 'z10'}, 
z10: {number: 10, scale: 0.10, className: 'zoom10', name: 'z10', zup: 'z25'} },
bubblemodes: {EDIT: 0, VIEW: 1},
colours: ['green', 'pink', 'blue', 'orange', 'silver'],
states: {
STARTUP: 10,
LOGIN: 0,
DEFAULT: 1,
NEWBUBBLE: 2,
DRAGGING: 3,
PANNING: 4,
WELCOME: 5,
PROMPT: 6,
BACK: 7},
drag_types: {NONE: 0, SPACE: 1, BUBBLE: 2},
initApp: function() {
App.WelcomePanel.populate();	},
initApp_return: function() {
App.User.Initialise();
this.deeplink = false;
this.invite_space = J2.QueryString.get('invite');
var activation_code = J2.QueryString.get('activate_me_with');
var bring_me = J2.QueryString.get('bring_me');					
if (J2.QueryString.get('goto') || J2.QueryString.get('bubble')) {
this.invite_space = J2.QueryString.get('goto');
this.go_bubble = J2.QueryString.get('bubble');
if (this.invite_space || this.go_bubble) this.deeplink = true;} 
if (bring_me) createCookie('spaceprompt', bring_me);
if (activation_code) {
App.User.ActivateWithActivationCode(activation_code);}
else {
keytag = readCookie('keytag');
if (keytag && keytag.length>0) {
if (this.invite_space && !(this.open_spaces[this.invite_space])) App.User.ActivateWithKeyTag(keytag, this.invite_space); 
else App.User.ActivateWithKeyTag(keytag);} else {
if (this.invite_space && !(this.open_spaces[this.invite_space])) 
this.SwitchState(App.states.PROMPT);
else {
if (this.deeplink) this.goDeepLink();
else this.SwitchState(App.states.WELCOME);}}			}},
addOpenSpace: function(key, id) {this.open_spaces[key] = id;},
waiting: function(is_waiting) {
if (is_waiting) document.getElementById('documentBody').addCssClass('wait');
else document.getElementById('documentBody').removeCssClass('wait');},
SetPoll: function(date_ticks) {
if (this.polling) return;
this.polling = true;
this.poll_date_ticks = date_ticks;
this.poll_interval = setInterval('App.Poll()', App.params.poll_interval_wait);
this.Poll();},
ClearPoll: function() {
if (this.poll_interval) clearInterval(this.poll_interval);
this.poll_interval = null;	
this.polling = false;},
Poll: function() {
if (this.state != this.states.DEFAULT && this.state != this.states.NEWBUBBLE) return;
var async = new App.AsyncRequest({requestTarget: 'bubbleLatest', method: 'GET', scope: this, onSuccess: this.Callback_PollRequest});
async.addParameter('ticks', this.poll_date_ticks);
if (App.current_space) async.addParameter('space', App.current_space.ID+'');
async.addParameter('uauth', App.User.getAuth());
async.addParameter('username', App.User.getQualifiedName());
async.addParameter('usertype', App.User.getType());
async.send();},
Callback_PollRequest: function(adij) {
var json = adij.json;
if (json.NewBubbles && json.NewBubbles.length) {
App.BubbleBar.AddBubbles(json.NewBubbles);		}
App.BubbleBar.UpdateUserList(json.ActiveUsers);
App.BubbleViewer.Callback_BubbleRequest(adij);
this.poll_date_ticks = adij.SysDate;
createCookie('lastpoll', adij.SysDate+'');
delete adij;},
TransformContentToRender: function(content) {
var render_str = "";
render_str = content.replace(/\</g, "&lt;").replace(/\>/g, "&gt;");
var urls = render_str.match(/((https?:\/\/\w+\.)|(www\.))([a-zA-Z0-9\-\_\.\/\%\?\=\&]+)/g);
if (urls) {
for (var i = 0; i < urls.length; i++) {
if (urls[i].lastIndexOf('.') == urls[i].length - 1) 
urls[i] = urls[i].substr(0, urls[i].length - 1);
var link_str = '<a href="' + urls[i] + '" target="_new">';
link_str += (urls[i].length > 30) ? urls[i].substr(0, 30) + '...' : urls[i];
link_str += '</a>';
render_str = render_str.replace(urls[i], link_str);}}
render_arr = render_str.split('\n\n');
render_str = "";
for (var i=0, s; i<render_arr.length, s=render_arr[i]; i++) 
render_str+="<p" + ((i==render_arr.length-1)?' class="last"':'') + ">" + s.replace(/\n/g, '<br />') + "</p>";
return render_str;},
SwitchState: function(state) {
switch(state) {
case App.states.STARTUP:
break;
case App.states.WELCOME:
this.ClearPoll();
App.AccessManager.showWelcome();
break;
case App.states.LOGIN: 
this.waiting(false);
App.AccessManager.showLoginForm();				
break;
case App.states.PROMPT:
this.waiting(false);
App.AccessManager.showKeyTagPrompt();			
break;
case App.states.DEFAULT: 
this.waiting(false);
App.AccessManager.hide();
this.InitialiseWithSpace();
break;
case App.states.BACK:
this.CloseSpace();
break;}
this.state = state;},
KeytaggedUserActivated: function() {
if (this.deeplink) {
this.goDeepLink();
return;} 
if (this.invite_space) {
createCookie('spaceprompt', this.invite_space);
document.location.href = 'index.html';}
else  
this.SwitchState(App.states.WELCOME);},
goDeepLink: function() {
if (this.invite_space) this.InitialiseWithSpace(this.open_spaces[this.invite_space]);
else this.InitialiseWithSpace();},
InitialiseWithSpace: function(space_data) {
this.current_space = (space_data)?space_data:null;
if (this.current_space) App.Heading.setSubhead(this.current_space.SpaceName);
else App.Heading.clearSubhead();
App.BubbleBar.Initialise();
App.BubbleViewer.Show();
App.ZoomControl.Show();
App.BubbleViewer.Initialise(this.go_bubble);
this.SetSize();
this.state = App.states.DEFAULT;
this.SetFeedLinks();
this.go_bubble = null;
this.invite_space = null; 
this.deeplink = false;}, 
CloseSpace: function() {
App.BubbleViewer.Hide();
App.BubbleBar.Hide();
App.ZoomControl.Hide();
App.AccessManager.show();
this.ClearPoll();
this.current_space = null;
this.SetFeedLinks();	},
SetSize: function() {
App.BubbleViewer.SetSize(WindowDimensions());
App.BubbleBar.SetSize(WindowDimensions());},
onWindowResize: function() {
if (this.state == this.states.DEFAULT || this.state == this.states.NEWBUBBLE) 
this.SetSize();},
onMouseMove: function(e) {
this.mouse_current_wheel = {x: e.mouseCoord.x, y: e.mouseCoord.y};
if (this.mouse_down && this.drag_capable!=this.drag_types.NONE) {
this.state = this.states.DRAGGING;
switch(this.drag_capable) {
case this.drag_types.SPACE:
this.DragBy(e.mouseCoord);
break;	
case this.drag_types.BUBBLE:	
App.BubbleViewer.GetNewBubble().DragBy({x: e.mouseCoord.x - this.mouse_current.x, y: e.mouseCoord.y - this.mouse_current.y});
break;			}
this.mouse_current = {x: e.mouseCoord.x, y: e.mouseCoord.y};
return false;}},
onMouseDown: function(e) {
this.mouse_down = true;
this.mouse_current = {x: e.mouseCoord.x, y: e.mouseCoord.y};
this.drag_total_delta = {x: 0, y:0};
this.drag_revert_state = this.state;
if ((this.state == this.states.DEFAULT || this.state == this.states.NEWBUBBLE) && e.srcElement.className.indexOf('bubbleContainer')>-1) {
this.drag_capable = this.drag_types.SPACE;}
else if (this.state == this.states.NEWBUBBLE && e.srcElement.className.indexOf('dragbar')>-1) {
this.drag_capable = this.drag_types.BUBBLE;			}
else this.drag_capable = this.drag_types.NONE;},
onMouseUp: function(e) {
if (this.state == this.states.DRAGGING) {
switch(this.drag_capable) {
case this.drag_types.SPACE:
this.DragBy(e.mouseCoord);
App.BubbleViewer.GetBubblesWithinBounds();
break;
case this.drag_types.BUBBLE:
App.BubbleViewer.GetNewBubble().DragBy({x: e.mouseCoord.x - this.mouse_current.x, y: e.mouseCoord.y - this.mouse_current.y});
break;}
this.state = this.drag_revert_state;}
this.mouse_down = false;},	
onDoubleClick: function(e) {
if (this.state == this.states.DEFAULT && e.srcElement.className.indexOf('bubbleContainer')>-1 && App.BubbleViewer.zoom_level==App.zoom_levels.z100) 
App.BubbleViewer.CreateNewBubble(e);
else if (App.BubbleViewer.zoom_level!=App.zoom_levels.z100)
App.BubbleViewer.ZoomToMousePoint(e.mouseCoord, App.zoom_levels[App.BubbleViewer.zoom_level.zup].name);},
onWheel: function(event) {
var delta = 0;
if (!event) 
event = window.event;
if (event.wheelDelta) { 
delta = event.wheelDelta/120;
if (window.opera)
delta = -delta;} else if (event.detail) { 
delta = -event.detail/3;}
if (delta)
App.onMouseWheelDelta(delta, event);
if (event.preventDefault)
event.preventDefault();
event.returnValue = false;},
onMouseWheelDelta: function(delta) {
var mouse_coord = this.mouse_current_wheel;
switch(delta) {
case 1:	
if (App.BubbleViewer.zoom_level.zup) 
App.BubbleViewer.ZoomToMousePoint(mouse_coord, App.zoom_levels[App.BubbleViewer.zoom_level.zup].name);
break;
case -1:	
if (App.BubbleViewer.zoom_level.zdown) 
App.BubbleViewer.ZoomToMousePoint(mouse_coord, App.zoom_levels[App.BubbleViewer.zoom_level.zdown].name);
break;}},
DragBy: function(mouse_coord) {
var delta = {x: mouse_coord.x - this.mouse_current.x, y: mouse_coord.y - this.mouse_current.y};	
this.drag_total_delta.x += delta.x;
this.drag_total_delta.y += delta.y;
App.BubbleViewer.DragBy(delta);	
if (Math.abs(this.drag_total_delta.x) > App.params.load_buffer || Math.abs(this.drag_total_delta.y) > App.params.load_buffer){
this.drag_total_delta = {x: 0, y:0};
App.BubbleViewer.GetBubblesWithinBounds();}},
SetHREF: function(hash) {},
SetFeedLinks: function() {
var qs = (this.current_space)?'?space='+this.current_space.SpaceKey:'';
var url_atom = this.base_url + this.params.feed_ext_atom + qs;
var url_rss = this.base_url + this.params.feed_ext_rss + qs;
App.ZoomControl.linkFeedIcon.href = url_rss;
document.getElementById('linkRSS').href = url_rss;
document.getElementById('linkATOM').href = url_atom;		}}
Base.addToEvent(window, 'resize', function() {if (!App || !Base || !Base.EventObject) return; App.onWindowResize();});
Base.addToEvent(document, 'mousemove', function(e) {if (!App || !Base || !Base.EventObject) return; App.onMouseMove(Base.EventObject(e));});
Base.addToEvent(document, 'mousedown', function(e) {if (!App || !Base || !Base.EventObject) return; App.onMouseDown(Base.EventObject(e));});
Base.addToEvent(document, 'mouseup', function(e) {if (!App || !Base || !Base.EventObject) return; App.onMouseUp(Base.EventObject(e));});
Base.addToEvent(document, 'dblclick', function(e) {if (!App || !Base || !Base.EventObject) return; App.onDoubleClick(Base.EventObject(e));});
if (window.addEventListener)
window.addEventListener('DOMMouseScroll', App.onWheel, false);
window.onmousewheel = document.onmousewheel = App.onWheel;
window.addEvent('load', function() {App.initApp();}, 0);
App.Heading = {
onBound: function() {},
setSubhead: function(title) {
this.SubTitle.innerHTML = ' - ' + title;},
clearSubhead: function() {
this.SubTitle.innerHTML = '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'; }}
App.User = {
types: {LURKER: 2, GUEST: 1, KEYED: 0},
name: null,
keytag: null,
id: null,
logged_in: false,
keytagged: false,
spaces: [],
colour: App.colours.GREEN,
user_auth: null,  
Initialise: function() {
var auth_str = readCookie('tmpAuth');
if (auth_str && auth_str.length) this.user_auth = auth_str;
if (!this.user_auth) 
this.user_auth = (new Date()).getTime() + '_' + Math.floor(Math.random() * 100000);
createNPCookie('tmpAuth', this.user_auth);},
ActivateWithoutKeyTag: function(username) {
this.name = username + ' (' + App.params.unkeyed_user_flag + ')';
this.GetColour();
this.SetCookies();
this.setAuth();},
setAuth: function() {
if (this.keytagged) this.user_auth = this.keytag;},
getAuth: function() {return this.user_auth;},
getType: function() {
return (this.keytagged)?this.types.KEYED:((this.name)?this.types.GUEST:this.types.LURKER);	},
getQualifiedName: function() {
return (this.name)?this.name:App.params.anonymous_user_name;	},
SetCookies: function() {
if (this.keytagged) {
createCookie('keytag', this.keytag);
createCookie('username', this.name);}
else {  
createNPCookie('username', this.name);			}
createCookie('colour', this.colour);},
GetColour: function() {
var colour = readCookie('colour');
if (colour!=undefined && colour!='undefined') this.colour = colour;	
else this.colour = 'green';},
SetColour: function(colour) {
this.colour = colour;
createCookie('colour', this.colour);},
ActivateWithKeyTag: function(keytag, space_key) {	
var async = new App.AsyncRequest({requestTarget: 'keytagGetWithKeyTag', scope: this, method: 'POST', onSuccess: this.Activate_Return});
async.addParameter('keytag', keytag);
if (space_key) async.addParameter('spacekey', space_key);
async.send();},
ActivateWithActivationCode: function(activation_code) {
var async = new App.AsyncRequest({requestTarget: 'keytagGetWithActivation', scope: this, method: 'POST', onSuccess: this.ActivateWithCode_Return});
async.addParameter('activation', activation_code);
async.send();},
Activate_Return: function(ajax) {
var ajax_return = eval('('+ajax.getResponseText()+')');
switch(ajax_return.status) {
case 'OK':
this.PopulateFromAjaxData(ajax_return);
if (readCookie('newuser')) {
this.new_user = true;
eraseCookie('newuser');}				
App.KeytaggedUserActivated();
break;
case 'INVALID':
App.SwitchState(App.states.LOGIN);
break;
case 'NOTFOUND':
App.SwitchState(App.states.LOGIN);
break;			}
delete ajax;},
ActivateWithCode_Return: function(ajax) {
var ajax_return = eval('('+ajax.getResponseText()+')');
switch(ajax_return.status) {
case 'OK':
this.PopulateFromAjaxData(ajax_return);
createCookie('newuser', 'true');
document.location.href='index.html';
break;
case 'INVALID':
App.SwitchState(App.states.LOGIN);
break;
case 'NOTFOUND':
App.SwitchState(App.states.LOGIN);
break;			}
delete ajax;},
PopulateFromAjaxData: function(ajax_return) {
this.name = ajax_return.keytag.Username;
this.id = ajax_return.keytag.ID;
this.keytag = ajax_return.keytag.KeyTag;
this.spaces = App.SpaceListArray(ajax_return.keytag.Spaces);
for (var i=0; i<this.spaces.length; i++) {
App.addOpenSpace(this.spaces[i].SpaceKey, this.spaces[i]);			}
this.keytagged = true;
this.GetColour();
this.SetCookies();
this.setAuth();},
Clear: function() {
eraseCookie('username');
eraseCookie('colour');
eraseCookie('keytag');
this.name = null;
this.keytag = null;
this.colour = null;
this.id = null;
this.keytagged = false;},
getSpaceWithSpaceKey: function(space_key) {
for (var i=0; i<this.spaces.length; i++) 
if (this.spaces[i].SpaceKey && this.spaces[i].SpaceKey == space_key) 
return this.spaces[i];
return null;} }
App.Bind = function(target, source) {
for (var m in source) {
target[m] = source[m];}}
App.TextField = {
isEmpty: function() {
return ((this.value.replace(/\s*/g, '')).length ==0);	},
trim: function() {
return ((this.value.replace(/(^\s*)|(\s*$)/g, '')));},
test: function() {
alert(this.tagName);},
setDefault: function(display_class) {
var me = this;
this.hasDefault = true;
this.defaultValue = this.title?this.title:this.value;
this.value = this.defaultValue;
this.defaultDisplayClass = (display_class)?display_class:'prompt';
this.addEvent('focus', function() {me.removeDefault();});
this.addEvent('blur', function() {me.restoreDefault();});
this.addCssClass(this.defaultDisplayClass);},
removeDefault: function() {
if (this.value == this.defaultValue) this.value = '';
this.removeCssClass(this.defaultDisplayClass);},
restoreDefault: function() {
if (!this.isEmpty()) return;
this.value = this.defaultValue;
this.addCssClass(this.defaultDisplayClass);},
isValidName: function() {
if (this.hasDefault) 
return(this.trim().match(/^[A-Za-z0-9\ ]+$/)!=null && this.value!=this.defaultValue);
else 
return(this.trim().match(/^[A-Za-z0-9\ ]+$/)!=null);},
isValidEmail: function() {
return(this.trim().match(/^[_a-zA-Z0-9-]+(\.[_a-zA-Z0-9-]+)*@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.(([0-9]{1,3})|([a-zA-Z]{2,3})|(aero|coop|info|museum|name))$/)!=null);		}}
if (typeof App != 'object') var App = {};
App.RequestMethods = {GET: 'GET', POST: 'POST'};
App.AsyncRequest = function() {
this.params = {};
if (typeof arguments[0] == "object") {
this.requestTarget = arguments[0].requestTarget || null;
this.method = arguments[0].method || null;
this.scope = (typeof arguments[0].scope == "object") ? arguments[0].scope : null;
this.onSuccess = (typeof arguments[0].onSuccess == "function") ? arguments[0].onSuccess : null;}
switch(this.method) {
case App.RequestMethods.GET: this.send = this.sendADIJ; break;
case App.RequestMethods.POST: this.send = this.sendAJAX; break;}
this.url = requestResolver.URL(this.requestTarget);}
App.AsyncRequest.prototype = {
addParameter: function(key, value) {
this.params[key] = value;},
sendAJAX: function() {
var ajax = new AJAX({URL: this.url, scope: this.scope, method: 'POST', onSuccess: this.onSuccess});
for (var param in this.params) {
ajax.addParameter(param, this.params[param]);}
ajax.send();},
sendADIJ: function() {
var qs = '';
for (var param in this.params) {
qs += (qs.length)?'&' : '?';
qs += param + '=' + this.params[param]; }
var adij = new ADIJ({URL: this.url+qs, scope: this.scope, onSuccess: this.onSuccess});
adij.send();		}}
var requestResolver = {
set: null,
setResolutionSet: function(set_name) {
if (resolutionSets[set_name]) this.set = resolutionSets[set_name]; 
else alert('resolution set does not exist');},
URL: function(request_name) {
if (this.set[request_name]) return this.set[request_name];
else alert('invalid set resolution ' + request_name);
return }}
var resolutionSets = {
dotNet: {
bubbleLatest: 		'BubbleLatest.json',
recentBubbles: 		'RecentBubbles.json',
bubbleWithID: 		'BubbleWithID.json',
bubbleListInBounds: 'BubbleListInBounds.json',
bubbleNewest: 		'BubbleNewest.json',
addBubble: 			'AddBubble.json'},
PHP: {	
bubbleLatest: 		'php/bubblesLatest.php',	
recentBubbles: 		'php/bubblesRecent.php',	
bubbleWithID: 		'php/bubbleWithID.php',
bubbleListInBounds: 'php/bubblesInBounds.php',
bubbleNewest: 		'php/bubbleNewest.php',		
addBubble: 			'php/addBubble.php',
keytagCreate: 		'php/keytagCreate.php',
keytagGetWithActivation: 	'php/keytagGetWithActivation.php',
keytagGetWithKeyTag:		'php/keytagGetWithKeyTag.php',
bubbleSpaceGetPublic: 		'php/bubbleSpaceGetPublic.php',
bubbleSpaceAddPrivate:		'php/bubbleSpaceAddPrivate.php',
bubbleSpaceDeletePrivate:	'php/bubbleSpaceDeletePrivate.php',
bubbleSpaceAddUser:			'php/bubbleSpaceAddUser.php',
bubbleSpaceGetWithKey:		'php/bubbleSpaceGetWithKey.php'}	}
requestResolver.setResolutionSet('PHP');
App.BubbleViewer = {
current_coord: {x: 0, y:0},
zoom_level: App.zoom_levels.z100,
bubble_cat: {},	
onBound: function() {
this.SetZoomBoundarySize();},
DragBy: function(delta) {
this.current_coord.x -= (delta.x / this.zoom_level.scale);
this.current_coord.y -= (delta.y / this.zoom_level.scale);
if (this.current_coord.x > App.params.limit) this.current_coord.x = App.params.limit;
if (this.current_coord.y > App.params.limit) this.current_coord.y = App.params.limit;
if (this.current_coord.x < -App.params.limit) this.current_coord.x = App.params.limit;
if (this.current_coord.y < -App.params.limit) this.current_coord.y = -App.params.limit;
this.SetToZoomCentre();},
SetToCoord: function(coord) {
this.current_coord.x = coord.x;
this.current_coord.y = coord.y;
this.SetToZoomCentre();},
Hide: function() {
this.rootElement.style.display = 'none';},
Show: function() {
this.rootElement.style.display = 'block';},
SetSize: function(win_dims) {
this.rootElement.style.width = win_dims.width+'px';
this.rootElement.style.height = win_dims.height+'px';
this.SetToZoomCentre();},
GetCoordFromMouseCoord: function(e) {
win_vcentre = this.GetWindowVirtualCentre();
coord_delta = {			
dx: e.mouseCoord.x - win_vcentre.x,
dy: e.mouseCoord.y - win_vcentre.y};
return {x: (this.current_coord.x + coord_delta.dx + App.params.bubble_delta.x), y: (this.current_coord.y + coord_delta.dy + App.params.bubble_delta.y)};},
GetWindowVirtualCentre: function() {
var win_dims = WindowDimensions();
return {
x: Math.floor((win_dims.width - App.params.bubblebar_width) / 2),
y: Math.floor(win_dims.height / 2)};},
CreateNewBubble: function(e) {
var coord = this.GetCoordFromMouseCoord(e);
var params = {
Content: '',
OwnerName: App.User.name,
Colour: App.User.colour,
X: coord.x,
Y: coord.y,
ID: null};
this.new_bubble = new App.Bubble(params);
this.new_bubble.SetColour(App.User.colour);
this.new_bubble.ToEditMode();
this.BubbleContainer.Add(this.new_bubble);
App.state = App.states.NEWBUBBLE;},
GetNewBubble: function() {
return this.new_bubble;	},
RemoveNewBubble: function() {
if (!this.new_bubble) return;
this.BubbleContainer.Remove(this.new_bubble);
delete this.new_bubble;
this.new_bubble = null;
App.state = App.states.DEFAULT;},
AddBubble: function(bubbledata) {
if (this.bubble_cat[bubbledata.ID]) return; 
this.bubble_cat[bubbledata.ID] = new App.Bubble(bubbledata);
this.BubbleContainer.Add(this.bubble_cat[bubbledata.ID]);},
AddBubbleToCat: function(bubble) {
if (!this.bubble_cat[bubble.ID]) 
this.bubble_cat[bubble.ID] = bubble;},
GoToBubble: function(bubble_id, no_pan) {
if (this.bubble_cat[bubble_id]) {
this.GoToCoord(this.bubble_cat[bubble_id].GetCentre(), no_pan);}
else {
var adij = new ADIJ({URL: requestResolver.URL('bubbleWithID')+'?bid=' + bubble_id, scope: this, onSuccess: this.Callback_GoToBubble});
adij.send();			}},
Callback_GoToBubble: function(adij) {
if (adij.json.length>0) {
var bubble_temp = new App.Bubble(adij.json[0]);
this.GoToCoord(bubble_temp.GetCentre());}		},
GoToCoord: function(coord, no_pan) { 
var delta = {dx: coord.x - this.current_coord.x, dy: coord.y - this.current_coord.y};
var jump_dist = Math.sqrt((delta.dx * delta.dx) + (delta.dy * delta.dy));
if (jump_dist > App.params.pan_limit || no_pan) {
this.SetToCoord(coord);
this.GetBubblesWithinBounds();}
else {
this.pan_destination = {x: coord.x, y: coord.y};
this.pan_delta = {dx: Math.floor(delta.dx / App.params.pan_steps), dy: Math.floor(delta.dy / App.params.pan_steps)};
this.pan_total_delta = {x: 0, y:0};
this.pan_step = 0;
this.pan_interval = setInterval(this.stepPan(), 10);
App.state = App.states.PANNING;}},
stepPan: function() {
var bv = this;
return function() {
bv.pan_step++;
if (bv.pan_step > App.params.pan_steps) {
clearInterval(bv.pan_interval);
App.state = App.states.DEFAULT;
bv.GetBubblesWithinBounds();}
else {
bv.current_coord.x += bv.pan_delta.dx;
bv.current_coord.y += bv.pan_delta.dy;			
bv.pan_total_delta.x += bv.pan_delta.dx;
bv.pan_total_delta.y += bv.pan_delta.dy;
if (Math.abs(bv.pan_total_delta.x) > App.params.load_buffer || Math.abs(bv.pan_total_delta.y) > App.params.load_buffer){
bv.pan_total_delta = {x: 0, y:0};
bv.GetBubblesWithinBounds();}}
bv.SetToZoomCentre();		}},
ZoomTo: function(zoom_level) {
this.zoom_level = App.zoom_levels[zoom_level];
this.rootElement.className = this.zoom_level.className;
this.SetZoomBoundarySize();
this.SetToZoomCentre();
for (var bubble_id in this.bubble_cat)
var bubble = this.bubble_cat[bubble_id].SetZoomPosition(this.zoom_level);
this.GetBubblesWithinBounds();},
SetZoomBoundarySize: function() {
this.BubbleContainer.Element.style.width = ((2 * App.params.limit) * this.zoom_level.scale) + 'px';
this.BubbleContainer.Element.style.height = ((2 * App.params.limit) * this.zoom_level.scale) + 'px';		},
SetToZoomCentre: function() {
win_vcentre = this.GetWindowVirtualCentre();
var dist_from_top = Math.floor((this.current_coord.y + App.params.limit) * this.zoom_level.scale);
var dist_from_left = Math.floor((this.current_coord.x + App.params.limit) * this.zoom_level.scale);
this.BubbleContainer.Element.style.top = -(dist_from_top - win_vcentre.y) + 'px';
this.BubbleContainer.Element.style.left = -(dist_from_left - win_vcentre.x) + 'px';		},
ZoomToMousePoint: function(mouse_coords, zoom_level) {
var win_vcentre = this.GetWindowVirtualCentre();
var old_delta_screen = {dx: mouse_coords.x - win_vcentre.x, dy: mouse_coords.y - win_vcentre.y};
var old_scale = this.zoom_level.scale, new_scale = App.zoom_levels[zoom_level].scale;
var new_delta_screen = {
dx: Math.floor(old_delta_screen.dx / old_scale) * new_scale  ,
dy: Math.floor(old_delta_screen.dy / old_scale) * new_scale };
var delta_screen_adjust = {dx: new_delta_screen.dx - old_delta_screen.dx, dy: new_delta_screen.dy - old_delta_screen.dy};
this.current_coord.x += (delta_screen_adjust.dx / new_scale);				
this.current_coord.y += (delta_screen_adjust.dy / new_scale);	
this.ZoomTo(zoom_level);
App.ZoomControl.SetZoomControls(zoom_level);		},
GetBubblesWithinBounds: function() {
var win_dims = WindowDimensions();
var bounds = {
xmin: Math.floor(this.current_coord.x - (Math.floor(win_dims.width / 2) + App.params.bounds_buffer)/this.zoom_level.scale),
ymin: Math.floor(this.current_coord.y - (Math.floor(win_dims.height / 2) + App.params.bounds_buffer)/this.zoom_level.scale),
xmax: Math.floor(this.current_coord.x + (Math.floor(win_dims.width / 2) + App.params.bounds_buffer)/this.zoom_level.scale),
ymax: Math.floor(this.current_coord.y + (Math.floor(win_dims.height / 2) + App.params.bounds_buffer)/this.zoom_level.scale)};
var qs_space = (App.current_space) ? '&space='+App.current_space.ID : '';
var adij = new ADIJ({URL: requestResolver.URL('bubbleListInBounds')+ ParamsToQS(bounds)+qs_space, scope: this, onSuccess: this.Callback_BubbleRequest});
adij.send();},
Callback_BubbleRequest: function(adij) {
if (adij.json.NewBubbles)
this.AddBubblesFromArray(adij.json.NewBubbles);
else this.AddBubblesFromArray(adij.json);
if (adij.json.UpdatedBubbles) this.UpdateBubblesFromArray(adij.json.UpdatedBubbles);
if (this.initialiser_id && this.bubble_cat[this.initialiser_id]) {
this.bubble_cat[this.initialiser_id].SetOpacity(10);
delete this.initialiser_id;}
App.SetPoll(adij.SysDate);},
AddBubblesFromArray: function(bubbles) {
for (var i=bubbles.length-1, bubbledata; i>=0, bubbledata=bubbles[i]; i--)
this.AddBubble(bubbledata);		},
UpdateBubblesFromArray: function(bubbles) {
for (var i=bubbles.length-1, bubbledata; i>=0, bubbledata=bubbles[i]; i--)
if (this.bubble_cat[bubbledata.ID])
this.bubble_cat[bubbledata.ID].UpdatePersistence(bubbledata.Persistence);},
Initialise: function(bubble_id) {
this.current_coord = {x: 0, y:0};
this.zoom_level = App.zoom_levels.z100;
delete this.bubble_cat;
this.bubble_cat = {};
this.BubbleContainer.RemoveAll();
var url = '';
if (bubble_id) url = requestResolver.URL('bubbleWithID') + '?bid=' + bubble_id; 	
else url = requestResolver.URL('bubbleNewest') + ((App.current_space) ? '?space='+App.current_space.ID : '');
var adij = new ADIJ({URL: url, scope: this, onSuccess: this.Callback_Initialise});
adij.send();			},
Callback_Initialise: function(adij) {
if (adij.json.length>0) {
var bubble_temp = new App.Bubble(adij.json[0]);
this.SetToCoord(bubble_temp.GetCentre());}		
this.GetBubblesWithinBounds();}}
App.Bubble = function(params) {
this.selected = false;
this.mode = App.bubblemodes.VIEW;
JOB.ObjectInstantiator.Bind(this, 'Bubble', params);}
App.Bubble.prototype = {
onBound: function() {
var me = this;
this.RenderContent();
this.DisplayUsername.innerHTML = '- ' + this.OwnerName;
this.Shell.className += ' '+this.Colour;
this.WriteURL();
this.SetZoomPosition();
this.TextContent.IsEmpty = text_IsEmpty;
this.TextContent.Trim = text_Trim;
this.ShellMain.onmouseover = this.Tail.onmouseover = function() {me.onMouseOver(); return false;}
this.ShellMain.onmouseout = this.Tail.onmouseout = function() {me.onMouseOut(); return false;}
this.Selector.onclick = function() {me.ToggleSelect(); return false;}
this.BtnYes.onclick = function() {me.onClickYes(); return false;}
this.BtnNo.onclick = function() {me.onClickNo(); return false;}
this.BtnColour.onclick = function() {me.onClickColour(); return false;}
this.DragBar.onmouseover = function() {me.DragBar.addCssClass('hover');}
this.DragBar.onmouseout = function() {me.DragBar.removeCssClass('hover');}
this.SetOpacity();},
SetZoomPosition: function() {
this.rootElement.style.top = Math.floor((this.Y + App.params.limit) * App.BubbleViewer.zoom_level.scale) + 'px';
this.rootElement.style.left = Math.floor((this.X + App.params.limit) * App.BubbleViewer.zoom_level.scale) + 'px';	
if (App.BubbleViewer.zoom_level.scale < 0.5) {
var v_height = Math.floor(this.Height * App.BubbleViewer.zoom_level.scale) - 8;
if (v_height < 0) v_height = 0;
this.ContentView.style.height = v_height + 'px';
this.ContentView.style.overflow = 'hidden';}
else{
this.ContentView.style.height = '';
this.ContentView.style.overflow = 'visible';}},
RenderContent: function() {		
this.ContentView.innerHTML = App.TransformContentToRender(this.Content);
if (this.TimestampCreated) this.DisplayTimestamp.innerHTML = kindDate(this.TimestampCreated);		},
ToggleSelect: function() {
this.selected = !this.selected;
this.Selector.className = (this.selected)?'selector selector_on':'selector';},
ToEditMode: function() {
this.rootElement.className = "bubble editMode";
this.SetOpacity(10);
this.mode = App.bubblemodes.EDIT;
App.has_edit_bubble = true;
var me = this;
if (App.User.name==null) {
this.GuestNamePrompt.style.display='block';
App.Bind(this.GuestName, App.TextField);
this.GuestName.setDefault();
this.disableTextContent()
this.GuestName.addEvent('keyup', function() {me.checkNameValidity();});
this.GuestName.addEvent('change', function() {me.checkNameValidity();});
this.GuestName.addEvent('blur', function() {me.hilightInvalidName();});
this.TextContent.addEvent('focus', function() {me.hilightInvalidName();});
this.TextContent.addEvent('click', function() {me.hilightInvalidName();});
this.GuestName.focus();}
else this.TextContent.focus();},
hilightInvalidName: function() {
if (!this.GuestName.isValidName()) this.GuestName.addCssClass('hilight');	},
disableTextContent: function() {
this.TextContent.addCssClass('disabled');
this.TextContentisDisabled = true;},
enableTextContent: function() {
this.TextContent.removeCssClass('disabled');
this.TextContentisDisabled = false;},
checkNameValidity: function() {
if (this.GuestName.isValidName()) this.enableTextContent();
else this.disableTextContent();},
ToViewMode: function() {
this.rootElement.className = "bubble viewMode";
this.SetOpacity();
this.mode = App.bubblemodes.VIEW;
var me = this;},
SetColour: function(colour) {
this.Colour = colour;
this.Shell.className = 'colourShell '+this.Colour;
App.User.SetColour(colour);},
onClickYes: function() {
if (this.TextContent.IsEmpty() || this.TextContentisDisabled) {this.onClickNo(); return;}
if (App.User.name == null) {
App.User.ActivateWithoutKeyTag(this.GuestName.value.trim());
this.OwnerName = App.User.name;}
var ajax=new AJAX({URL: requestResolver.URL('addBubble'), scope: this, method: 'POST', onSuccess: this.SetCreated});
this.Content = this.TextContent.Trim().replace(/\r\n/g, '\n');
this.DisplayUsername.innerHTML = '- ' + this.OwnerName;
this.RenderContent();
this.ToViewMode();
ajax.addParameter('owner_name', App.User.name);
ajax.addParameter('content', this.Content);
ajax.addParameter('colour', this.Colour);
ajax.addParameter('x', this.X);
ajax.addParameter('y', this.Y);
ajax.addParameter('width', this.rootElement.offsetWidth);
ajax.addParameter('height', this.rootElement.offsetHeight);
ajax.addParameter('height', this.rootElement.offsetHeight);
if (App.current_space) ajax.addParameter('space', App.current_space.ID);				
ajax.send();
this.in_request = true;},
SetCreated: function(ajax) {
var return_data = eval('('+ajax.getResponseText()+')');
this.ID = return_data.ID;
this.Content = return_data.Content;
this.DateCreated = return_data.DateCreated;
this.TimestampCreated = return_data.TimestampCreated;
this.RenderContent();
this.WriteURL();
this.Width = this.rootElement.offsetWidth;
this.Height = this.rootElement.offsetHeight;
App.state = App.states.DEFAULT;
App.BubbleViewer.AddBubbleToCat(this);
App.has_edit_bubble = false;
var me = this;
this.in_request = false;
App.BubbleBar.AddLocalBubble(return_data);},
WriteURL: function() {
if (this.ID) {
this.BtnLinkTo.style.visibility='visible';
this.BtnLinkTo.innerHTML = App.base_href + '?' + this.ID;
this.BtnLinkTo.href = App.base_href + '?' + this.ID;}
else 
this.BtnLinkTo.style.visibility='hidden';},
onClickNo: function() {
App.BubbleViewer.RemoveNewBubble();
App.has_edit_bubble = false;},
onClickColour: function() {
var current_col_index =0;
for (var i=0; i<App.colours.length; i++)
if (App.colours[i] == this.Colour) {
current_col_index = i;
break;}
current_col_index++; 
if (current_col_index==App.colours.length) current_col_index = 0;
this.SetColour(App.colours[current_col_index]);},
onMouseOver: function() {
if (!App) return;
if (this.mode == App.bubblemodes.EDIT || App.has_edit_bubble) return;
this.rootElement.style.zIndex = '100000';
this.SetOpacity(10);},
onMouseOut: function() {
if (!App) return;
this.BtnLinkTo.style.display = 'none';
this.rootElement.style.zIndex = '';
this.SetOpacity();},
onClick: function() {
return;
if (this.mode == App.bubblemodes.EDIT || this.in_request) return false;
if (App.BubbleViewer.zoom_level == App.zoom_levels.z100) {
document.location.href = App.base_href + '?' + this.ID ;}
else {}
return false;},
onClickLinkTo: function() {},
SetOpacity: function(opacity) {
var set_opacity = (opacity)?opacity: ((this.Persistence && this.Persistence < 10)?this.Persistence:9);
this.rootElement.style.opacity = (set_opacity/10)+'';
this.rootElement.style.filter = 'alpha(opacity=' + (set_opacity * 10) + ')';	},
UpdatePersistence: function(persistence) {
if (this.Persistence && this.Persistence == persistence) return; 
this.Persistence = persistence;
this.SetOpacity();},
GetCentre: function() {
return {
x: this.X + Math.floor(this.Width/2),
y: this.Y + Math.floor(this.Height/2)};},
DragBy: function(delta){
this.X += delta.x;
this.Y += delta.y;
this.SetZoomPosition();}}
App.BubbleBar = {
onBound: function() {},
Callback_onBound: function(adij) {
this.AddBubbles(adij.json);
delete adij;},
Hide: function() {
this.rootElement.style.display = 'none';},
Show: function() {
this.rootElement.style.display = 'block';
this.SetSize(WindowDimensions());
this.ActiveUserList.innerHTML = '';
var qs = (App.current_space) ? '?space='+App.current_space.ID : '';
var adij = new ADIJ({URL: requestResolver.URL('recentBubbles')+qs, scope: this, onSuccess: this.Callback_onBound});
adij.send();},
SetSize: function(win_dims) {
this.rootElement.style.height = win_dims.height + 'px';},
AddBubbles:function(bubbles) {
for (var i=(bubbles.length-1), bubbledata; i>=0, bubbledata = bubbles[i]; i--) 
if (!self.App.BubbleViewer.bubble_cat[bubbledata.ID])  
this.AddBubbleToBar(new App.BubbleBarItem(bubbledata));},
AddLocalBubble: function(bubbledata) {
this.AddBubbleToBar(new App.BubbleBarItem(bubbledata));		},
AddBubbleToBar:function(bubblebaritem) {
this.BubbleList.AddFirst(bubblebaritem);},
Initialise: function() {
this.BubbleList.RemoveAll();
this.Show();},
UpdateUserList: function(users) {
users_str = '<li><strong>' + (App.User.keytagged?App.User.name:'You') + '</strong>' + (users.length?', ':'') + '</li>';
for (var i=0, l=users.length; i<l; i++) 
users_str+='<li>' + users[i] + (i<l-1?', ':'') + '</li>';
this.ActiveUserList.innerHTML = users_str;}}
App.UserItem = function(params) {
JOB.ObjectInstantiator.Bind(this, 'UserItem', params);}
App.UserItem.prototype = {
onBound: function() {
this.rootElement.innerHTML = 'hello';}}
App.BubbleBarItem = function(params) {
JOB.ObjectInstantiator.Bind(this, 'BubbleBarItem', params);}
App.BubbleBarItem.prototype = {
onBound: function() {
var me = this;
this.rootElement.className = this.Colour;
this.BubbleLink.onclick = function() {me.onClick(); return false;}
this.BubbleContent.innerHTML = this.ShortenContent(this.Content);	
this.BubbleOwnerName.innerHTML = ' - ' + this.OwnerName;
this.BubbleDate.innerHTML = kindDate(this.TimestampCreated);		},
ShortenContent: function(content_in) {
if (content_in.length > App.params.bubblebar_text_limit){
var truncate_loc = content_in.indexOf(" ", App.params.bubblebar_text_limit);
if (truncate_loc > 0) return content_in.substring(0, truncate_loc) + '...';}
return content_in;},
onMouseOver: function() {},
onMouseOut: function() {},
onClick: function() {
App.BubbleViewer.GoToBubble(this.ID);}}
if (typeof App != "object") App = {};
App.ZoomControl = {
buttons: {},
current_zoom: null,
onBound: function() {
var me = this;
this.linkBackTo.href='index.html';
this.linkHelpMeOut.target="_new";
for (var z in App.zoom_levels) {
this.buttons[z] = new App.ZoomControlItem({zoom: z})
this.ZoomControlList.Add(this.buttons[z]);}},
Hide: function() {
this.rootElement.style.display = 'none';},
Show: function() {
this.rootElement.style.display = 'block';
this.SetZoomControls(App.BubbleViewer.zoom_level.name);},
SetZoomControls: function(new_zoom) {
if (this.current_zoom) this.buttons[this.current_zoom].Switch(false);
this.current_zoom = new_zoom;
this.buttons[this.current_zoom].Switch(true);},
SetZoomLevel: function(new_zoom) {
App.BubbleViewer.ZoomTo(new_zoom);
this.SetZoomControls(new_zoom);}}
App.ZoomControlItem = function(params) {
this.zoom = params.zoom;
JOB.ObjectInstantiator.Bind(this, 'ZoomControlItem', params);}
App.ZoomControlItem.prototype = {
onBound: function() {
var me = this;
this.rootElement.className = this.zoom;
this.ZoomActive.onclick = function() {App.ZoomControl.SetZoomLevel(me.zoom); return false;}},
Switch: function(is_on) {
this.rootElement.className = (is_on)?this.zoom+ ' active':this.zoom;}}
App.AccessManager = {
showLoginForm: function() {
this.show();
App.LoginForm.show();
App.OptionsLink.show();
this.current_form = App.LoginForm;},
showExtendedLogin: function() {
App.OptionsLink.hide();
this.current_form = App.WelcomePanel;
App.KeyTagForm.show();	
this.hilightFormObject(App.KeyTagForm);	},
hideExtendedLogin: function() {
App.KeyTagForm.hide();	
App.OptionsLink.show();
setOpacity(App.WelcomePanel.rootElement, 1);},
showWelcome: function() {
App.WelcomePanel.activate();},
show: function() {
document.getElementById('documentBody').addCssClass('login');		},
hide: function() {
document.getElementById('documentBody').removeCssClass('login');		},
clearUser: function() {
App.User.Clear();
App.WelcomePanel.hide();
document.location.href='index.html';},
hilightFormObject: function(form_object) {
if (this.current_form) setOpacity(this.current_form.rootElement, 0.5);
this.current_form = form_object;
setOpacity(this.current_form.rootElement, 1);},
setDisabled: function(form_object) {
setOpacity(form_object.rootElement, 0.2);},
requestKeyTag: function(name, email) {
if (this.sending_request) return;
var ajax=new AJAX({URL: requestResolver.URL('keytagCreate'), scope: this, method: 'POST', onSuccess: this.requestKeyTag_return});		
ajax.addParameter('name', name);
ajax.addParameter('email', email);
if (App.invite_space) ajax.addParameter('spacekey', App.invite_space);
this.sending_request = true;
this.setDisabled(App.LoginForm);
this.setDisabled(App.KeyTagForm);
this.setDisabled(App.WelcomePanel);
ajax.send();},
requestKeyTag_return: function(ajax) {
var ajax_return = eval('('+ajax.getResponseText()+')');
switch (ajax_return.status) {
case 'OK':
this.sending_request = false;
App.LoginForm.hide();
App.KeyTagForm.hide();
App.PromptKeyTagRequest.hide();
App.WelcomePanel.hide();
App.PromptActivationKeySent.show();
if (ajax_return.activation) App.PromptActivationKeySent.setActivation(ajax_return.activation);
break;
default: 
alert('there has been a problem...');
break;				}},
showKeyTagPrompt: function() {
var adij = new ADIJ({URL: requestResolver.URL('bubbleSpaceGetWithKey') + '?spacekey=' + App.invite_space, scope: this, onSuccess: this.showKeyTagPrompt_return});
adij.send();		},
showKeyTagPrompt_return: function(adij) {
if (adij.json.SpaceName) {
App.PromptKeyTagRequest.activate(adij.json);
App.KeyTagForm.show();
this.hilightFormObject(App.KeyTagForm);}
else {
this.showLoginForm();
return;}
delete adij;
this.show();}}
App.LoginForm = {
onBound: function() {
var username_display =  readCookie('username');
var me = this;
App.Bind(this.txtUserName, App.TextField);
this.txtUserName.addEvent('focus', function() {me.helpLogin.style.display='block';});
this.txtUserName.addEvent('focus', function() {App.AccessManager.hilightFormObject(me);});
this.rootElement.addEvent('click', function() {App.AccessManager.hilightFormObject(me);});
this.txtUserName.addEvent('keyup', function() {me.checkValidity();});
this.txtUserName.addEvent('change', function() {me.checkValidity();});
this.txtUserName.value = (username_display)?username_display:'';
this.checkValidity();		
this.formLogin.addEvent('submit', function() {me.onSubmit(); return false;});},
show: function() {
this.rootElement.addCssClass('active');},
hide: function() {		
this.rootElement.removeCssClass('active');},
isValid: function() {
return this.txtUserName.isValidName();		},
checkValidity: function() {
var visibility = (!this.isValid())?'hidden':'visible';
if (this.btnEnter.style.visibility!=visibility) 
this.btnEnter.style.visibility=visibility;		},
onSubmit: function() {
App.User.ActivateWithoutKeyTag(this.txtUserName.trim());
App.SwitchState(App.states.DEFAULT);
App.BubbleViewer.Initialise();}	}
App.OptionsLink = {
onBound: function() {
this.linkOptions.addEvent('click', function() {App.AccessManager.showExtendedLogin(); return false;});},
show: function() {
this.rootElement.addCssClass('active');},
hide: function() {		
this.rootElement.removeCssClass('active');}	}
App.KeyTagForm = {
onBound: function() {
var me = this;
App.Bind(this.txtNameCreate, App.TextField);
App.Bind(this.txtEmailCreate, App.TextField);
this.txtNameCreate.setDefault();
this.txtEmailCreate.setDefault();
this.rootElement.addEvent('click', function() {App.AccessManager.hilightFormObject(me);});
this.txtNameCreate.addEvent('focus', function() {App.AccessManager.hilightFormObject(me);});
this.txtEmailCreate.addEvent('focus', function() {App.AccessManager.hilightFormObject(me);});
this.txtNameCreate.addEvent('focus', function() {me.helpKey.style.display='block';});				
this.txtEmailCreate.addEvent('focus', function() {me.helpKey.style.display='block';});
this.txtNameCreate.addEvent('keyup', function() {me.checkValidity();});
this.txtEmailCreate.addEvent('keyup', function() {me.checkValidity();});
this.txtNameCreate.addEvent('change', function() {me.checkValidity();});
this.txtEmailCreate.addEvent('change', function() {me.checkValidity();});
this.formCreateKeyTag.addEvent('submit', function() { me.onSubmit(); return false;});				
this.labelKey.addEvent('click', function() {me.helpKey.style.display='block';})
this.linkCancelThis.addEvent('click', function() {App.AccessManager.hideExtendedLogin(); return false;});},
isValid: function() {
return (this.txtNameCreate.isValidName() && this.txtEmailCreate.isValidEmail());},
checkValidity: function() {
var visibility = (!this.isValid())?'hidden':'visible';
if (this.btnCreateKeyTag.style.visibility!=visibility) 
this.btnCreateKeyTag.style.visibility=visibility;		},
onSubmit: function() {
if (this.isValid()) 
App.AccessManager.requestKeyTag(this.txtNameCreate.trim(), this.txtEmailCreate.trim());},
show: function() {
this.rootElement.addCssClass('active');},
hide: function() {		
this.rootElement.removeCssClass('active');}	}
App.PromptKeyTagRequest = {
onBound: function() {},
activate: function(space) {
this.promptSpaceName.innerHTML = '"' + space.SpaceName + '"';
this.show();},
show: function() {
this.rootElement.addCssClass('active');},
hide: function() {		
this.rootElement.removeCssClass('active');}	}
App.PromptActivationKeySent = {
onBound: function() {},
setActivation: function(activation) {
if (this.tempKey) 
this.tempKey.innerHTML = '<a href="index.html?activate_me_with=' + activation + '">' + activation + '</a>';	},
show: function() {
this.rootElement.addCssClass('active');},
hide: function() {		
this.rootElement.removeCssClass('active');}	}
App.WelcomePanel = {
onBound: function() {
var me = this;
this.SpaceListDefault.onclick = function(){App.InitialiseWithSpace(); return false;};		
this.linkClearUserData.addEvent('click', function() {me.onClearUserData(); return false;});		},
populate: function() {
var adij = new ADIJ({URL: requestResolver.URL('bubbleSpaceGetPublic'), scope: this, onSuccess: this.populate_return});
adij.send();		},
populate_return: function(adij) {
this.populateSpaceList(this.SpaceListPublic, this.SpaceContainerPublic, App.SpaceListArray(adij.json), true);
delete adij;
App.initApp_return();},
activate: function(adij) {
if (App.User.new_user) {
this.WelcomeMessage.innerHTML = 'Hello <em>' + App.User.name + '</em>, welcome to thinkBubble';}
else if (App.User.keytagged) {
this.WelcomeMessage.innerHTML = 'Welcome back <em>' + App.User.name + '</em>';}
else { 
this.WelcomeMessage.innerHTML = 'Welcome to <em>thinkBubble</em>';
this.linkClearUserData.style.display = 'none';	}
var space_key = readCookie('spaceprompt');
if (!space_key) {
space_key = J2.QueryString.get('bring_me');}
if (space_key && space_key.length>0) {
var space = App.User.getSpaceWithSpaceKey(space_key);
this.WelcomeSubMessage.style.display = 'block';
this.linkSpaceListSpecial.innerHTML = space.SpaceName;
this.linkSpaceListSpecial.addEvent('click', function() {App.InitialiseWithSpace(space); return false;});
eraseCookie('spaceprompt'); }
if (App.User.keytagged) {
this.populateSpaceList(this.SpaceListPrivate, this.SpaceContainerPrivate, App.User.spaces, false);
this.SpaceListAdder = new App.SpaceListAdder();
this.SpaceContainerPrivate.Add(this.SpaceListAdder);}
else {
this.SpaceListPrivate.style.display = 'none';
App.OptionsLink.show(); }
App.waiting(false);
App.AccessManager.show();
this.show();		},
clearSpaceListPrompts: function() {
var space_list = this.SpaceContainerPrivate.GetMembers();
for (var i=0; i<space_list.length; i++) 
if (space_list[i].hideMessages) space_list[i].hideMessages();},	
show: function() {
this.rootElement.addCssClass('active');},
hide: function() {		
this.rootElement.removeCssClass('active');},
populateSpaceList: function(list_el, container_el, space_data, is_public ) {
if (!space_data.length) return;
for (var i = 0; i < space_data.length; i++) {
App.addOpenSpace(space_data[i].SpaceKey, space_data[i]);
container_el.Add(new App.SpaceListItem(space_data[i], is_public));}},
selectSpace: function(space) {},
addPrivateSpace: function(space_data) {
var new_item = new App.SpaceListItem(space_data);
this.SpaceContainerPrivate.AddBefore(new_item, this.SpaceListAdder);
this.SpaceListAdder.revert();
new_item.shareSpace();},
deletePrivateSpace: function(space) {
this.SpaceContainerPrivate.Remove(space);},
onClearUserData: function() {
App.AccessManager.clearUser();}}
if (typeof App != 'object') var App = {};
App.SpaceListArray = function(space_data) {
var space_arr = [];
for (var i=0; i<space_data.length; i++) {
var obj = {};
for (var param in space_data[i]) obj[param] = space_data[i][param];
space_arr.push(obj);}
return space_arr;}
App.SpaceListItem = function(params, is_public) {
JOB.ObjectInstantiator.Bind(this, 'SpaceListItem', params);
this.LinkText.innerHTML = this.SpaceName;
this.Link.SpaceID = this.ID;
var me = this;
this.Link.href = 'index.html?goto=' + this.SpaceKey;
if (!is_public) {
this._el.addEvent('mouseover', function(){me.onMouseOver();});
this._el.addEvent('mouseout', function(){me.onMouseOut();});
this.btnShareSpace.addEvent('click', function() {me.shareSpace();});
if (params.OwnerID == App.User.id)
this.btnDeleteSpace.addEvent('click', function() {me.deleteSpace();});
else this.btnDeleteSpace.style.display = 'none';}}
App.SpaceListItem.prototype = {
onMouseOver: function() {
this.Buttons.style.display='inline';},
onMouseOut: function() {
this.Buttons.style.display='none';		},
shareSpace: function() {
if (this.showing=='share') return;
if (App.LoginControl) App.LoginControl.clearSpaceListPrompts();
if (App.WelcomePanel) App.WelcomePanel.clearSpaceListPrompts();
this.msgShare.style.display='block';
this.showing = 'share';
var url_str = (document.location.href.indexOf('?')>0)?document.location.href.split('?')[0]:document.location.href;
url_str += '?invite=' + this.SpaceKey;
this.msgUrl.innerHTML = url_str;},
deleteSpace: function() {
if (this.showing=='delete') return;
if (App.LoginControl) App.LoginControl.clearSpaceListPrompts();
if (App.WelcomePanel) App.WelcomePanel.clearSpaceListPrompts();
this.msgDelete.style.display='block';
this.showing = 'delete';
var me = this;
this.linkDeleteConfirmYes.addEvent('click', function() {me.confirmDelete(); return false;});
this.linkDeleteConfirmNo.addEvent('click', function() {me.hideMessages(); return false;});},
hideMessages: function(){
this.msgShare.style.display='none';
this.msgDelete.style.display='none';
this.showing = null;	},
confirmDelete: function() {
var ajax=new AJAX({URL: requestResolver.URL('bubbleSpaceDeletePrivate'), scope: this, method: 'POST', onSuccess: this.confirmDelete_Return});		
ajax.addParameter('spaceid', this.ID);	
ajax.send();	
setOpacity(this._el, 0.5);},
confirmDelete_Return: function(ajax) {
var return_data = eval('('+ajax.getResponseText()+')');
if (return_data.status=='OK') {
if (App.LoginControl) App.LoginControl.deletePrivateSpace(this); 
if (App.WelcomePanel) App.WelcomePanel.deletePrivateSpace(this);			}}}
App.SpaceListAdder = function(params) {
JOB.ObjectInstantiator.Bind(this, 'SpaceListAdder', params);
App.Bind(this.txtNewSpaceName, App.TextField);
this.txtNewSpaceName.setDefault();
var me = this;
this.formAddNewForm.addEvent('submit', function() {me.submitAdd(); return false;});
this.txtNewSpaceName.addEvent('keyup', function() {me.validateText();});}
App.SpaceListAdder.prototype = {
submitAdd: function() {
if (this.in_request) return;
setOpacity(this.formAddNewForm, 0.5);
var ajax=new AJAX({URL: requestResolver.URL('bubbleSpaceAddPrivate'), scope: this, method: 'POST', onSuccess: this.submitAdd_Return});		
var name_val =  this.txtNewSpaceName.trim();
ajax.addParameter('spacename', name_val);
ajax.addParameter('ownerid', App.User.id);
ajax.addParameter('ownername', App.User.name);
ajax.send();
this.in_request = true;},
submitAdd_Return: function(ajax) {
var return_data = eval('('+ajax.getResponseText()+')');
if (App.LoginControl) App.LoginControl.addPrivateSpace(return_data); 
if (App.WelcomePanel) App.WelcomePanel.addPrivateSpace(return_data);			
delete ajax;},
validateText: function() {
this.btnSubmitAddSpace.style.visibility = (!this.txtNewSpaceName.isEmpty())?'visible':'hidden';	},
revert: function() {
setOpacity(this.formAddNewForm, 1);
this.txtNewSpaceName.setDefault();
this.btnSubmitAddSpace.style.visibility='hidden';	
this.in_request = false;}}
