/*extern jQuery */
/*jslint browser: true, passfail: false, undef: false */
var webslinger;
if (jQuery) (function($) {
	(function() {
		var mutators = [];
 		var localMutators = [];
		if (!jQuery.mutations) jQuery.mutations = {};
		jQuery.extend(jQuery.mutations, {
			addLocalMutator:	function(handler) {
				localMutators.push(handler);
			},
			removeLocalMutators:	function() {
				localMutators = [];
			},
			applier:	function(ctx) {
				if (!ctx) ctx = document;
				for (var i = 0; i < mutators.length; i++) {
					try {
						mutators[i](ctx);
					} catch (e) {
						alert(i + '->' + e);
					}
				}
			},
			delayedAdder:	function(f) {
				mutators[mutators.length] = f;
			},
			immediateAdder:	function(f) {
				mutators[mutators.length] = f;
				f(document);
			},
			init:	function() {
				jQuery.mutations.applier();
				jQuery(document).applyLocalMutators();
				jQuery.addMutator = jQuery.mutations.immediateAdder;
			}
		});
		jQuery.extend({
			addLocalMutator: jQuery.mutations.addLocalMutator,
			removeLocalMutators: jQuery.mutations.removeLocalMutators,
			addMutator: jQuery.mutations.delayedAdder
		});
		jQuery.fn.applyMutators = function() {
			webslinger.timer.log('applying mutators');
			webslinger.createTimer();
			this.each(function() {
				jQuery.mutations.applier(this);
			});
			webslinger.timer.pop('applied mutators');
			return this;
		};
		jQuery(function() { jQuery.mutations.init(); });
		jQuery.fn.applyLocalMutators = function() {
			(this.getLocalMutatorsApplier())();
		};
		jQuery.fn.getLocalMutatorsApplier = function() {
			var $this = this;
			var mutators = localMutators;
			return function() {
				$this.each(function() {
					var $this = jQuery(this);
					for (var i = 0; i < mutators.length; i++) {
						mutators[i]($this);
					}
				});
			};
		};
	})();

	(function() {
		jQuery.extend({
			runWhenAttached: function(element, callback) {
				var ptr = element;
				while (ptr != null) {
					if (ptr == document.body) break;
					ptr = ptr.parentNode;
				}
				if (ptr == null) {
					setTimeout(function() {
						jQuery.runIfAttached(element, callback);
					}, 50);
					return;
				}
				setTimeout(function() {
					callback.call(element);
				}, 0);
				return;
			}
		});
		jQuery.fn.extend({
			runWhenAttached:	function(callback) {
				return this.each(function() {
					jQuery.runWhenAttached(this, callback);
				});
			}
		});
	})();
	(function() {
	 	var resetter;
		var current;
		jQuery(function() {
			jQuery('body').click(function(e) {
				if (current) {
					var ptr = e.target;
					while (ptr != null) {
						if (ptr == current) return;
						ptr = ptr.parentNode;
					}
					resetter();
				}
			});
		});
		function transientActivate(checker, opener, args) {
			if (current == this) return;
			if (resetter) {
				resetter();
				return false;
			}
			var isActive = checker.apply(this, args);
			if (isActive) {
				current = this;
				var closer = opener.apply(this, args);
				resetter = function() {
					resetter = null;
					current = null;
					closer();
				};
			}
			return false;

		}
		$.fn.transientClick = function(checker, opener) {
			this.each(function() {
				var thiz = this;
				$(this).click(function() {
					var args = $.makeArray(arguments);
					return transientActivate.call(thiz, checker, opener, args);
				});
			});
		};
		$.fn.transientFocus = function(checker, opener) {
			this.each(function() {
				var thiz = this;
				$(this).find('select, input, textarea').focus(function() {
					var args = $.makeArray(arguments);
					return transientActivate.call(thiz, checker, opener, args);
				}).blur(function() {
					if (resetter) {
						resetter();
						return false;
					}
				});
				$(this).click(function() {
					var args = $.makeArray(arguments);
					return transientActivate.call(thiz, checker, opener, args);
				});
			});
		};
	})();

	if (false) if (jQuery.browser.msie || true) {
		jQuery.addMutator(function(ctx) {
			var jBody = jQuery("body");
			jQuery("form", ctx).each(function() {
				var jForm = jQuery(this);
				jForm.prepend("<input type='hidden' />");
				var hidden = jForm.get(0).firstChild;
				jForm.find("button").each(function() {
					var name = this.getAttribute("name");
					this.removeAttribute("name");
					var value = this.getAttribute("value");
					this.removeAttribute("value");
					jQuery(this).click(function() {
						hidden.setAttribute("name", name);
						hidden.setAttrbute("value", value);
						return true;
					});
				});
			});
		});
	}
/*
	Object.prototype.super = function(name) {
		var args = [];
		for (var i = 1; i < arguments.length; i++) args.push(arguments[i]);
	};
*/
	webslinger = function(path) {
		if (window == this) return new webslinger(path);
		if (path && path[path.length - 1] != '/') path += '/';
		this._path = path;
	};

	webslinger.cleanupFunction = function() {
		var args = jQuery.makeArray(arguments);
		var main = args.shift();
		return function() {
			try {
				return main.apply(this, jQuery.makeArray(arguments));
			} finally {
				for (var i = 0; i < args.length; i++) {
					try {
						args[i]();
					} catch(e) {
						// ignore
					}
				}
			}
		};
	};

	// http://www.howtocreate.co.uk/tutorials/javascript/eventinfo
	webslinger.fixMouseEvent = function(e) {
		if( !e ) {
			if( window.event ) {
				//Internet Explorer
				e = window.event;
			} else {
				//total failure, we have no way of referencing the event
				return;
			}
		}
		if( typeof( e.pageX ) == 'number' ) {
			//most browsers
			var xcoord = e.pageX;
			var ycoord = e.pageY;
		} else if( typeof( e.clientX ) == 'number' ) {
			//Internet Explorer and older browsers
			//other browsers provide this, but follow the pageX/Y branch
			var xcoord = e.clientX;
			var ycoord = e.clientY;
			var badOldBrowser = ( window.navigator.userAgent.indexOf( 'Opera' ) + 1 ) ||
			( window.ScriptEngine && ScriptEngine().indexOf( 'InScript' ) + 1 ) ||
			( navigator.vendor == 'KDE' );
			if( !badOldBrowser ) {
				if( document.body && ( document.body.scrollLeft || document.body.scrollTop ) ) {
					//IE 4, 5 & 6 (in non-standards compliant mode)
					xcoord += document.body.scrollLeft;
					ycoord += document.body.scrollTop;
				} else if( document.documentElement && ( document.documentElement.scrollLeft || document.documentElement.scrollTop ) ) {
					//IE 6 (in standards compliant mode)
					xcoord += document.documentElement.scrollLeft;
					ycoord += document.documentElement.scrollTop;
				}
			}
		} else {
			//total failure, we have no way of obtaining the mouse coordinates
			return;
		}
		return [xcoord, ycoord];
	};

	function indentingString(doSpace, doNewline) {
		var parts = []
		if (doSpace == null) doSpace = true;
		if (doNewline == null) doNewline = true;
		var lastWasNewline = false;
		var indent = '';
	
		this.newline = function() {
			lastWasNewline = true;
			if (doNewline) parts.push('\n');
			return this;
		};

		checkAfterNewline = function() {
			if (!lastWasNewline) return;
			if (doSpace) {
				parts.push(doNewline ? indent : ' ');
			}
			lastWasNewline = false;
		};

		this.push = function() {
			indent += ' ';
			return this;
		};

		this.pop = function() {
			indent = indent.substring(0, indent.length - 1);
			return this;
		};

		this.space = function() {
			checkAfterNewline();
			if (doSpace) parts.push(' ');
			return this;
		};

		this.write = function(s) {
			var i;
			var offset;
			checkAfterNewline();
			for (i = 0, offset = 0; i < s.length; i++) {
				var c = s.charAt(i);
				parts.push(c);
				if (s.charCodeAt(i) == '\n') {
					checkAfterNewline();
					lastWasNewline = true;
				}
			}
			return this;
		};

		this.toString = function() {
			return parts.join('');
		};
	};


	var writeString = function(o, s) {
		s.write('"');
		for (var i = 0; i < o.length; i++) {
			var c = o.substring(i, i + 1);
			switch (c) {
				case "\\": s.write("\\\\"); continue;
				case "/": s.write("\\/"); continue;
				case "\"": s.write("\\\""); continue;
				case "\b": s.write("\\b"); continue;
				case "\f": s.write("\\f"); continue;
				case "\n": s.write("\\n"); continue;
				case "\r": s.write("\\r"); continue;
				case "\t": s.write("\\t"); continue;
			}
			var code = o.charCodeAt(i);
			if (32 <= code && code >= 256) {
				s.write("\\u");
				var n = code.toString(16);
				for (var j = 4 - n.length; j > 0; j--) s.write("0");
				s.write(n);
			} else {
				s.write(c);
			}
  		}
		s.write('"');
	};

	var writeArray = function(o, s) {
		s.write('[').push();
		if (o.length) s.newline();
		for (var i = 0; i < o.length; i++) {
			writeObject(o[i], s);
			if (i != o.length - 1) s.write(',');
			s.newline();
		}
		s.pop().write(']');
	};

	var writeMap = function(o, s) {
		s.write('{').push();
		var i = 0;
		for (var n in o) {
			if (i == 0) {
				s.newline();
			} else {
				s.write(',').newline();
			}
			writeObject(n, s);
			s.write(':').space();
			writeObject(o[n], s);
			i++;
		}
		if (i != 0) s.write(',').newline();
		s.pop().write('}');
	};

	var writeObject = function(o, s) {
		if (o == null) {
			s.write("null");
		} else if (typeof o == 'string') {
			writeString(o, s);
		} else if (o.constructor == Array) {
			writeArray(o, s);
		} else if (typeof o == 'object') {
			writeMap(o, s);
		} else {
			s.write("" + o);
		}
	};

	webslinger.toJSON = function(o, doSpace, doNewline) {
		var s = new indentingString(doSpace, doNewline);
		writeObject(o, s);
		return s.toString();
	};

	webslinger.defaultErrorHandler = null;

	webslinger.ajax = function(path, payload, context, callback, errorHandler) {
		var args = [];
		for (var i = 0; i < arguments.length; i++) args.push(arguments[i]);
		path = args.shift();
		callback = args.pop();
		if (args[args.length - 1] instanceof Function) {
			errorHandler = callback;
			callback = args.pop();
		} else {
			if (webslinger.defaultErrorHandler) {
				errorHandler = webslinger.defaultErrorHandler;
			} else {
				errorHandler = function(type, data) {
					switch (type) {
						case "request":
							alert("error(" + data.path + ")[" + data.type + "][" + data.exc + "]");
							callback(null, {type: "request", ajax: data});
							break;
						case "client":
							alert("client error(" + data + ")");
							callback(null, {type: "client", exc: data});
							break;
						case "server":
							alert("server error");
							callback(null, {type: "server", error: data});
							break;
					}
				};
			}
		}
		if (args.length == 2) {
			if (args[0] instanceof String) {
				payload = args.shift();
				context = args.shift();
			} else if (args[0] instanceof Object) {
				context = args.shift();
				payload = args.shift();
			} else {
				payload = null;
				context = null;
			}
		} else if (args.length == 1) {
			if (args[0] instanceof String) {
				payload = args.shift();
				context = null;
			} else if (args[0] instanceof Object) {
				context = args.shift();
				payload = null;
			} else {
				payload = null;
				context = null;
			}
		} else {
			payload = null;
			context = null;
		}
		if (path.substring(0, 1) != '/') {
			path = (this._path ? this._path + (this._path[this._path.length - 1] == '/' ? '' : '/')  : '/') + path;
		}
		if (!context) context = {};
		var data = {
			payload: payload,
			context: context
		};
		var encodedData = webslinger.toJSON(data);
		return {
			async:		true,
			contentType:	"text/x-json; charset=UTF-8",
			data:		encodedData,
			dataType:	"json",
			error:		function(req, type, exc) {
				alert('error{' + req + ', ' + type + ', ' + exc + '}');
				errorHandler("request", {path: path, req: req, type: type, exc: exc});
				throw exc;
			},
			processData:	false,
			success:	function(data) {
				//alert("event data=" + webslinger.toJSON(data));
				if (data.hub) {
					for (var i = 0; i < data.hub.length; i++) {
						var item = data.hub[i];
						OpenAjax.hub.publish(item.topic, item.data);
					}
				}
				if (data.result != null) {
					try {
						callback(data.result);
					} catch (e) {
						errorHandler("client", e);
					}
				} else {
					errorHandler("server", data.error);
				}
			},
			type:		"post",
			url:		path
		};
	};
	webslinger.event = function() {
		var args = [];
		for (var i = 0; i < arguments.length; i++) args.push(arguments[i]);
		jQuery.ajax(webslinger.ajax.apply(webslinger, args));
		return this;
	};

	webslinger.prototype.event = webslinger.event;

	(function() {
		function sendWidgetMessage(jThis, msg, widgetName, context) {
			if (!(widgetName instanceof String) && !context) {
				context = widgetName;
				widgetName = null;
			}
			return jThis.each(function() {
				var possibleName = widgetName;
				if (!possibleName) possibleName = this.getAttribute("wsWidgetName");
				if (!possibleName) return;
				OpenAjax.hub.publish("widget." + possibleName + "." + msg, context);
			});
		}
		jQuery.fn.WidgetClear = function(widgetName) { return sendWidgetMessage(this, "clear", widgetName); };
		jQuery.fn.WidgetFill = function(widgetName) { return sendWidgetMessage(this, "fill", widgetName); };
		jQuery.fn.WidgetRefresh = function(widgetName, context) { return sendWidgetMessage(this, "refresh", widgetName, context); };
		jQuery.fn.WidgetSetName = function(widgetName) {
			return this.attr("wsWidgetName", widgetName);
		};
		function onResize(ptr) {
			var widget;
			while (ptr != null) {
				if (!ptr.getAttribute) break;
				var name = ptr.getAttribute("wsWidgetName");
				if (name != null) {
					OpenAjax.hub.publish("widget." + name + ".resize");
					break;
				}
				ptr = ptr.parentNode;
			}
		}
		jQuery.fn.WidgetSendResizeEvent = function() {
			this.each(function() { onResize(this.parentNode); });
		}
		jQuery.addMutator(function(ctx) {
			jQuery("img", ctx).load(function() { onResize(this.parentNode); });
		});
	})();

	jQuery.fn.CommunicationPane = function() {
		return this.each(function() {
			var jThis = jQuery(this);
			var widgetName = jThis.attr("wsWidgetName");
			var urlBase = jThis.attr("communicationsBase");
			var listUrl = jThis.attr("communicationsList");
			var messageUrl = jThis.attr("communicationsMessage");
			var editUrl = jThis.attr("communicationsEdit");
			var messagePane = jThis.find(".Communications-Message").WidgetSetName(widgetName + ":Message");
			var listContainer = jThis.find(".Communications-List").WidgetSetName(widgetName + ":List");
			var processor = new webslinger(urlBase);
			var messagePaneHide = function(callback) {
				callback();
			};
			OpenAjax.hub.subscribe(
				"widget." + widgetName + ":List.refresh",
				function(name, context) {
					processor.event(
						listUrl,
						context,
						function(result) {
							var scrollTop = listContainer.scrollTop();
							listContainer.WidgetClear().empty().append(result).scrollTop(scrollTop).WidgetFill();
						}
					);
				}
			);
			OpenAjax.hub.subscribe("widget." + widgetName + ":List.clear", function() {
				if (messageUrl && messagePane.length) {
					messagePaneHide(function() {
						messagePane.WidgetClear().empty();
					});
				}
			});
			OpenAjax.hub.subscribe("widget." + widgetName + ":List.fill", function() {
				listContainer.find("[@listSort]").css("cursor", "pointer").click(function() {
					listContainer.WidgetRefresh({sort: this.getAttribute("listSort"), sortOrder: this.getAttribute("listSortOrder")});
				});
				var lastCommChosen;
				var listRows = listContainer.find("[@listCommId][@listCommType]");
				if (messageUrl && messagePane.length) {
					listRows.css("cursor", "pointer").click(function() {
						if (lastCommChosen) lastCommChosen.removeClass("CommListMessageSelected");
						lastCommChosen = jQuery("[@listCommId='" + this.getAttribute("listCommId") + "']", this.parentNode).addClass("CommListMessageSelected");
						var commId = this.getAttribute("listCommId");
						var messageResult;
						//alert("row clicked: " + alertId);
						var latch = webslinger.Latch(
							2,
							function() {
								//alert("trigger called: loadResult=" + loadResult);
								messagePane.empty();
								messagePane.append(messageResult);
								/*
								messagePane.find("*[@lprCommId][@lprActionType]").click(function() {
									var commId = this.getAttribute("lprCommId");
									var type = this.getAttribute("lprActionType");

								});
								*/
								messagePane.show(500);
								messagePane.WidgetFill();
								messagePaneHide = function(callback) {
									if (messagePane.is(":visible")) {
										messagePane.hide(500, callback);
									} else {
										callback();
									}
								};
							}
						);
						messagePaneHide(latch);
						processor.event(
							messageUrl,
							{
								commId: commId
							},
							function(result) {
								messageResult = result;
								latch();
							}
						);
					});
					messagePane.empty();
				}
				if (editUrl) {
					listRows.css("cursor", "pointer").dblclick(function() {
						if (lastCommChosen) lastCommChosen.removeClass("CommListMessageSelected");
						lastCommChosen = jQuery("[@listCommId='" + this.getAttribute("listCommId") + "']", this.parentNode).addClass("CommListMessageSelected");
						var commId = this.getAttribute("listCommId");
						jQuery(this).openDialog(urlBase + "/" + editUrl + "/" + commId, "EditShowing", "Edit"); 
					});
				}
			});
			listContainer.WidgetFill();
			jThis.WidgetFill();
		});
	};

	webslinger.remotePane = function(id, path) {
		OpenAjax.hub.subscribe(
			"widget." + id + ".refresh",
			function(name, context) {
				webslinger.event(
					path,
					context,
					function(result) {
						var container = jQuery("#" + id);
						OpenAjax.hub.publish("widget." + id + ".clear", container);
						container.empty().append(result);
						try {
							container.applyMutators();
						} catch (e) {
							alert("e=" + e);
						}
						OpenAjax.hub.publish("widget." + id + ".fill", container);
					}
				);
			}
		);
		jQuery(
			function() {
				OpenAjax.hub.publish("widget." + id + ".fill", jQuery("#" + id));
			}
		);
		var f = function(name, context) {
			OpenAjax.hub.publish("widget." + id + ".refresh", context);
		}
		for (var i = 2; i < arguments.length; i++) {
			OpenAjax.hub.subscribe(arguments[i], f);
		}
		var refreshTimer;
		var thiz = {
			refreshPeriod:	function(timeout) {
				if (refreshTimer) clearTimeout(refreshTimer);
				var timerFunction = function() {
					OpenAjax.hub.publish("widget." + id + ".refresh");
					setTimeout(timerFunction, timeout);
				};
				refreshTimer = setTimeout(timerFunction, timeout);
				return thiz;
			}
		};
		return thiz;
	};

	webslinger.Latch = function(count, trigger) {
		var f = function() {
			f.count--;
			if (f.count == 0) trigger();
		};
		f.incr = function() {
			f.count++;
		};
		f.count = count;
		if (f.count == 0) trigger();
		return f;
	};

	webslinger.chainFunctions = function() {
		var funcs = jQuery.makeArray(arguments);
		return function() {
			for (var i = 0; i < funcs.length; i++) {
				if (typeof funcs[i] == 'function') {
					funcs[i]();
				}
			}
		};
	};

	webslinger.createTimer = function() {
		var oldTimer = webslinger.timer;
		var startTime = new Date().getTime();
		var times = [];
		return webslinger.timer = {
			log:	function(msg) {
				times[times.length] = [msg, new Date().getTime()];
			},
			msg:	function() {
				var m = [];
				var lastTime = startTime;
				for (var i = 0; i < times.length; i++) {
					var line = times[i];
					m.push(', ', line[0], ': ', (line[1] - lastTime));
					lastTime = line[1];
				}
				m.shift();
				webslinger.timer = oldTimer;
				m.push(', total: ', (new Date().getTime() - startTime));
				return m.join('');
			},
			pop:	function(msg) {
				var m = webslinger.timer.msg();
				webslinger.timer.log(msg + '(' + m + ')');
			}
		};
	};
	webslinger.timer = {
		log:	function(msg) { },
		msg:	function() { },
		pop:	function(msg) { }
	};

	(function() {
		// $1 = path
		// $2 = search
		// $3 = hash
		var pathRegex = /^([^?#]*)(?:\?([^#]*))?(?:#(.*))?$/;
		// $1 = protocol
		// $2 = host
		// $3 = port
		// $4 = path
		var urlRegex = /^([^:\/]+:)(?:\/\/([^:\/]+)(?:(:[0-9]+))?)?(.*)$/;
		var defaultPorts = {
			'ftp:':		21,
			'gopher:':	70,
			'http:':	80,
			'https:':	443
		};

		webslinger.url = function() {
			var base, pathname
			if (arguments.length == 0) throw 'Not enough arguments to  url constructor';
			if (arguments.length == 1) {
				pathname = arguments[0];
				base = webslinger.getLocation();
			} else {
				if (typeof arguments[0] == 'string') {
					pathname = arguments[0];
					base = arguments[1];
				} else {
					base = arguments[0];
					pathname = arguments[1];
				}
			}
			var baseParts = urlRegex.exec(pathname);
			this.protocol = null;
			this.host = null;
			this.port = null;
			var pathname;
			if (!baseParts || baseParts.length == 0) {
				// only pathname
				if (base) {
					this.protocol = base.protocol;
					this.host = base.host;
					this.port = base.port;
				} else {
					this.protocol = null;
					this.host = null;
					this.port = null;
				}
			} else {
				this.protocol = baseParts[1];
				this.host = baseParts[2] != null ? baseParts[2] : null;
				this.port = baseParts[3] != null ? parseInt(baseParts[3]) : null;
				pathname = baseParts[4];
				if (!pathname) pathname = '/';
			}
			var pathParts = pathRegex.exec(pathname);
			this.pathname = pathParts[1];
			this.search = pathParts[2];
			this.hash = pathParts[3];
			if (this.search && this.search.length == 0) this.search = null;
			if (this.hash && this.hash.length == 0) this.hash = null;
			if (base) {
				if (!this.pathname) {
					this.pathname = base.pathname;
				} else if (this.pathname.charAt(0) != '/') {
					this.pathname = base.pathname.replace(/[^\/]*$/, '') + this.pathname;
				}
			}
			if (!this.port) this.port = defaultPorts[this.protocol];

			var s = this.protocol;
			if (this.host != null) s += '//' + this.host;
			if (this.port != null && defaultPorts[this.protocol] != this.port) s += ':' + this.port;
			s += this.pathname;
			if (this.search) s += '?' + this.search;
			if (this.hash) s += '#' + this.hash;
			this.href = s;
		};

		webslinger.url.prototype.toString = function() {
			return this.href;
		};

		webslinger.url.prototype.isSameHost = function(o) {
			return this.protocol == o.protocol && this.host == o.host && this.port == o.port;
		};

		webslinger.url.prototype.resolve = function(path) {
			return new webslinger.url(this, path);
		};

		webslinger.url.removeProtocol = function(href) {
			var parts = urlRegex.exec(href);
			return parts ? parts[4] : href;
		};

		webslinger.url.extractBase = function(href) {
			href = webslinger.url.removeProtocol(href);
			if (href.charAt(href.length - 1) == "/") return href;
			//alert("stripping");
			var slash = href.lastIndexOf('/');
			return href.substring(0, slash + 1);
		};

		webslinger.url.fix = function(href) {
			var base = webslinger.getLocation();
			var fixed = new webslinger.url(base, href);
			if (base.isSameHost(fixed)) return urlRegex.exec(fixed.href)[4];
			return fixed.href;
		};
	})();

	(function() {
		var location;
		var listeners = [];
		webslinger.getLocation = function() {
			return location;
		};
		webslinger.setPath = function(path) {
			location = new webslinger.url(location, path);
			for (var i = 0; i < listeners.length; i++) {
				listeners[i](location);
			}
		};
		webslinger.getPath = function() {
			return location.pathname;
		};
		webslinger.pageReload = function() {
			window.location.href = location.href;
		};
		webslinger.addPathListener = function(listener) {
			for (var i = 0; i < listeners.length; i++) {
				if (listeners[i] === listener) {
					return;
				}
			}
			listeners[listeners.length] = listener;
		}
		webslinger.removePathListener = function(listener) {
			for (var i = 0; i < listeners.length; i++) {
				if (listeners[i] === listener) {
					listeners.splice(i, 1);
					return;
				}
			}
		}
		location = new webslinger.url(window.location.href);
	})();

	OpenAjax.hub.subscribe(
		"mailto",
		function(name, data) {
			alert("mailto[" + webslinger.toJSON(data) + "]");
		}
	);
	OpenAjax.hub.subscribe(
		"page.refresh",
		function(name, data) {
			webslinger.pageReload();
		}
	);
	OpenAjax.hub.subscribe(
		"page.redirect",
		function(name, data) {
			window.location = data.location;
		}
	);

	(function() {
		var loadedScripts = {};
		jQuery.extend({
			getScripts: function() {
				var args = jQuery.makeArray(arguments);
				var callback = args.pop();
				var latch = webslinger.Latch(args.length, callback);
				for (var i = 0; i < args.length; i++) {
					var script = args[i];
					var f = loadedScripts[script];
					if (!f) {
						var queue = [];
						f = loadedScripts[script] = function(callback) {
							queue.push(callback);
						};
						var doneCallback = function() {
							var script = arguments.callee.script;
							//alert('script(' +  script + ') loaded');
							loadedScripts[script] = function(callback) {
								callback();
							};
							for (var i = 0; i < queue.length; i++) queue[i]();
						};
						doneCallback.script = script;
						jQuery.getScript(script, doneCallback);
					} 
					f(latch);
				}
			}
		});
	})();

	(function() {
		var busyHandlers = [];
		var depth = 0;
		var debugLine = 1;
		function busyDebug(msg) {
			jQuery('#webslinger-busy-debug').append('<pre>' + debugLine++ + ':' + msg + '</pre>');
		};
		var busyTimeout = null;
		function sendBusySignal() {
			busyDebug('sendBusySignal');
			clearTimeout(busyTimeout);
			busyTimeout = setTimeout(function() {
				busyTimeout = null;
				busyDebug('processBusySignal');
				for (var i = 0; i < busyHandlers.length; i++) {
					busyHandlers[i]('start');
				}
			}, 500);
		}
		function clearBusySignal() {
			busyDebug('clearBusySignal');
			if (busyTimeout) {
				clearTimeout(busyTimeout);
				busyTimeout = null;
				return;
			}
			for (var i = 0; i < busyHandlers.length; i++) {
				busyHandlers[i]('end');
			}
		}
		webslinger.addBusyHandler = function(handler) {
			busyHandlers[busyHandlers.length] = handler;
		};
		webslinger.removeBusyHandler = function(handler) {
			for (var i = 0; i < busyHandlers.length; i++) {
				if (busyHandlers[i] == handler) {
					busyHandlers.splice(i, 1);
					break;
				}
			}
		};
		webslinger.isBusy = function() {
			return depth > 0;
		};
		webslinger.incrementBusy = function(label) {
			busyDebug('incrementBusy(' + label + ')');
			if (depth == 0) sendBusySignal();
			depth++;
		};
		webslinger.decrementBusy = function(label) {
			busyDebug('decrementBusy(' + label + ')');
			depth--;
			if (depth == 0) clearBusySignal();
		};
		webslinger.createBusyLatch = function(label) {
			var delegate;
			delegate = function() {
				webslinger.decrementBusy('latch(' + label + ')');
				delegate = function() { };
			};
			webslinger.incrementBusy('latch(' + label + ')');
			return function() {
				delegate();
			};
		};
		jQuery(function() {
			jQuery('body')
				.ajaxStart(function() { webslinger.incrementBusy('jquery-ajax'); })
				.ajaxStop(function() { webslinger.decrementBusy('jquery-ajax'); });
		});
	})();
})(jQuery);

