/* jquery inexplicably has a getJSON shortcut, but not a postJSON one */
$.postJSON = function(url, data, callback) {
	$.post(url, data, callback, "json");
};

/* we don't want arrays serialized with [] appended to the key names */
jQuery.ajaxSettings.traditional = true;

var STORM       = STORM || {};
STORM._handlerTemplates = {
	hasUniqId: function (type, url, overrides) {
		return {
			_url: url,
			_retrieve: function (uniqId) {
				STORM.event.fireWhenReady(type + '.retrieve', uniqId);
			},
			_parse: function (uniqId, response) {
				if (parseInt(response.errnum) == 52) {
					STORM.instanceList.remove(uniqId);
					return;
				}
				if (response) {
					STORM[type]._findCache[uniqId] = response;
					STORM.event.fireWhenReady(type + '.load', uniqId);
				}
			},
			_cacheTimeout: 60,
			_findCache: {},
			find: function (uniqId) {
				if (STORM[type]._findCache[uniqId]) {
					return STORM[type]._findCache[uniqId];
				}
				return null;
			},
			clear: function (uniqId) {
				STORM.JSON.cache.expire(STORM[type]._url + uniqId);
			},
			load: function (uniqId) {
				STORM.JSON.load(STORM[type]._url + uniqId, function (response) { STORM[type]._parse(uniqId, response) }, function () { STORM[type]._retrieve(uniqId) }, STORM[type]._cacheTimeout);
			},
			preLoad: function (uniqId, data) {
				STORM.JSON.preLoad(STORM[type]._url + uniqId, data, function (response) { STORM[type]._parse(uniqId, response) }, function () { STORM[type]._retrieve(uniqId) }, STORM[type]._cacheTimeout);
			}
		};
	},
	noUniqId: function (type, url, overrides) {
		return {
			_url: url,
			_lookupField: 'name',
			_retrieve: function () {
				STORM.event.fireWhenReady(type + '.retrieve');
			},
			_parse: function (response) {
				STORM[type]._findCache = {};
				for (var key in response) {
					if (response[key] && typeof response[key] != 'string') {
						STORM[type].list = response[key];
						STORM.event.fireWhenReady(type + '.load');
					}
				}
			},
			list: [],
			_cacheTimeout: 60,
			_findCache: {},
			find: function (value) {
				if (STORM[type]._findCache[value]) {
					return STORM[type]._findCache[value];
				}
				var field = STORM[type]._lookupField;
				var l = STORM[type].list.length;
				for (var i=0; i<l; i++) {
					var entry = STORM[type].list[i];
					if (entry[field] == value) {
						STORM[type]._findCache[value] = entry;
						return entry;
					}
				}
				return null;
			},
			each: function(callback) {
				for (var i in STORM[type].list) {
					callback(STORM[type].list[i]);
				}
			},
			clear: function () {
				STORM.JSON.cache.expire(STORM[type]._url);
			},
			load: function () {
				STORM.JSON.load(STORM[type]._url, STORM[type]._parse, STORM[type]._retrieve, STORM[type]._cacheTimeout);
			},
			length: function () {
				return STORM[type].list.length
			},
			preLoad: function (data) {
				STORM.JSON.preLoad(STORM[type]._url, data, STORM[type]._parse, STORM[type]._retrieve, STORM[type]._cacheTimeout);
			}
		}
	}
};

STORM.JSON = {
	_redo: [],
	preLoad: function (url, data, callback, before, timeout) {
		STORM.JSON.cache.set(url, data, timeout);
		if (before) {
			before();
		}
		if (! STORM.JSON.handleError(data)) {
			callback(data);
		}
	},
	load: function (url, callback, before, timeout) {
		var data = STORM.JSON.cache.get(url);
		if (data) {
			callback(data);
			return;
		}

		if (STORM.JSON.cache.isReserved(url)) {
			return;
		}

		var method = arguments.callee.caller;
		var arg    = method.arguments[0];
		STORM.JSON._redo.push([ url, callback, before, timeout ]);

		STORM.JSON.cache.reserve(url);
		if (before) {
			before();
		}
		$.getJSON(url, function (response) {
			STORM.JSON.cache.cancelReservation(url);
			if (! STORM.JSON.handleError(response)) {
				STORM.JSON.cache.set(url, response);
				callback(response);
			}
			if (! STORM.vars.loggedOut) {
				STORM.JSON._redo = [];
			}
		});
	},
	cache: {
		_cache: {},
		_reserved: {},
		_timeout: {},
		reserve: function (url) {
			STORM.JSON.cache._reserved[url] = true;
		},
		isReserved: function (url) {
			return STORM.JSON.cache._reserved[url];
		},
		cancelReservation: function (url) {
			STORM.JSON.cache._reserved[url] = false;
		},
		set: function (url, data, timeout) {
			/* default the timeout to 2 minutes */
			timeout = timeout || 120;

			STORM.JSON.cache.cancelReservation(url);
			STORM.JSON.cache._timeout[url] = setTimeout(function () {
				STORM.JSON.cache.expire(url);
			}, timeout * 1000);

			STORM.JSON.cache._cache[url] = data;
		},
		get: function (url) {
			return STORM.JSON.cache._cache[url];
		},
		expire: function (url) {
			STORM.JSON.cache._cache[url] = null;
			STORM.JSON.cache.cancelReservation(url);
			if (STORM.JSON.cache._timeout[url]) {
				clearTimeout(STORM.JSON.cache._timeout[url]);
			}
		}
	},
	handleError: function (response) {
		if (! response.errnum) {
			return false;
		}
		if (parseInt(response.errnum) == 4) {
			/* their login has timed out, show the login prompt */
			if (! STORM.vars.loggedOut) {
				STORM.DHTML.popupLogin();
			}
		}
		else if (parseInt(response.errnum) == 52) {
			/* the instance no longer exists, but we don't know the uniq_id at this point */
			return false;
		}
		else {
			var error = response.error;
			if (STORM.errors) {
				error = STORM.errors[response.errnum] || STORM.errors['default'];
				if (error == '[[original]]') {
					error = response.error;
				}
			}
			STORM.DHTML.popupError(error);
		}
		return true;
	},
	createHandler: function (template, type, url, overrides) {
		var lookatme = STORM;
		/* many different pieces of data are loaded in exactly the same manner, so I created some templates for creating the js 'objects' */
		if (! STORM[type]) {
			STORM[type] = STORM._handlerTemplates[template](type, url, overrides);
			if (overrides) {
				for (var method in overrides) {
					STORM[type][method] = overrides[method];
				}
			}
		}
	}
};

STORM.event = {
	_callbacks: {},
	_fired: {},
	_repeated: {},
	_rateLimited: {},
	_delayed: {},
	watch: function (eventName, func) {
		STORM.event._callbacks[eventName] = STORM.event._callbacks[eventName] || [];
		STORM.event._callbacks[eventName].push(func);
	},
	fire: function (eventName, params) {
		var key = (params) ? eventName + ':' + params : eventName;
		if (STORM.event._rateLimited[key]) {
			return;
		}

		/* rate-limit to prevent excessive calls to the same event */
		STORM.event._rateLimited[key] = setTimeout(function () {
			STORM.event._rateLimited[key] = false;
		}, 100);

		STORM.event._fired[key] = true;

		var list = STORM.event._callbacks[eventName];
		if (list) {
			var l = list.length;
			for (var i=0; i<l; i++) {
				list[i](params);
			}
		}
	},
	fireWhenReady: function (eventName, params) {
		if (STORM.event.hasFired('user.loggedIn')) {
			STORM.event.fire(eventName, params);
		}
		else {
			var key = (params) ? eventName + ':' + params : eventName;
			if (! STORM.event._delayed[key]) {
				STORM.event._delayed[key] = true;
				STORM.event.watch('user.loggedIn', function () {
					STORM.event.fire(eventName, params);
				});
			}
		}
	},
	hasFired: function (eventName, params) {
		var key = (params) ? eventName + ':' + params : eventName;
		return STORM.event._fired[key];
	},
	fireRepeated: function (eventName, params, interval) {
		var key = (params) ? eventName + ':' + params : eventName;
		if (!STORM.event._repeated[key]) {
			STORM.event.fire(eventName, params);
			STORM.event.repeat(eventName, params, interval);
		}
	},
	repeat: function (eventName, params, interval) {
		var key = (params) ? eventName + ':' + params : eventName;

		if (interval == null) {
			alert("STORM.event.repeat() called without an interval");
			return;
		}

		if (STORM.event._repeated[key]) {
			clearTimeout(STORM.event._repeated[key]);
		}
		STORM.event._repeated[key] = setTimeout(function () {
			STORM.event._repeated[key] = null;
			STORM.event.fire(eventName, params);
			STORM.event.repeat(eventName, params, interval);
		}, interval * 1000);
	},
	stopRepeating: function (eventName) {
		if (STORM.event._repeated[eventName]) {
			clearTimeout(STORM.event._repeated[eventName]);
			STORM.event._repeated[eventName] = null;
		}
	}
};

STORM.DHTML = {
	_greyCount: 0,
	buildRow: function (input, columns) {
		var tr = $('<tr>');
		var td = $('<td>').css('width', '20px');
		input.id = input.id || input.name + '_' + input.value.replace(/\./g, '_');
		/* you can't dynamically change the 'name' attribute on an input in IE 6/7 */
		td.append($('<input type="radio" name="' + input.name + '" value="">').attr({ value: input.value, id: input.id }));
		tr.append(td);

		var th = $('<th>');
		if (input.label && input.label.indexOf('<img') != -1 && $.browser.msie && parseInt($.browser.version) <= 7) {
			// IE + images + labels == fail
			th.html(input.label);
		}
		else {
			th.append($('<label>').attr('for', input.name + '_' + input.value).html(input.label));
		}
		tr.append(th);

		for (var i=0; i<columns.length;i++) {
			tr.append($('<td>').html(columns[i]));
		}
		return tr;
	},
	buildPricedRow: function (input, price, columns) {
		var tr = STORM.DHTML.buildRow(input, columns);
		var td = $('<td>');
		STORM.DHTML.TOOLTIP.price.insert(td, price);
		tr.append(td);
		return tr;
	},
	tabs: {
		_current: {},
		_callbacks: {},
		createGroup: function (groupName, callbacks) {
			STORM.DHTML.tabs._callbacks[groupName] = callbacks || {};
			$('div.tab_group[group=' + groupName + '] > div.tabs img.link, '
			 +'a.change_tab_link[group=' + groupName +']').click(function (event) {
				event.preventDefault();
				var tab = $(this);
				STORM.DHTML.tabs.changeTab(tab.attr('group'), tab.attr('tab'));
				return false;
			});
		},
		_getTabImage: function (groupName, tabName) {
			var img = $('img.link[group=' + groupName + '][tab=' + tabName + ']');
			return img;
		},
		_getTab: function (groupName, tabName) {
			var div = $('div.tab[group=' + groupName + '][tab=' + tabName + ']');
			return div;
		},
		changeTab: function (groupName, tabName, skipHash) {
			var current = STORM.DHTML.tabs._current[groupName];
			if (current && tabName != current) {
				var img = STORM.DHTML.tabs._getTabImage(groupName, current);
				img.attr('src', img.attr('src').replace(/-up/, '-dn'));
			}

			var img = STORM.DHTML.tabs._getTabImage(groupName, tabName);
			img.attr('src', img.attr('src').replace(/-dn/, '-up'));

			var tabs = $('div.tab_group[group=' + groupName + '] > div.tab');
			tabs.hide();
			var tab = STORM.DHTML.tabs._getTab(groupName, tabName)
			tab.show();
			var hash = tab.attr('hash');
			if (! skipHash && hash) {
				location.hash = hash;
			}

			STORM.DHTML.tabs._current[groupName] = tabName;
			if (STORM.DHTML.tabs._callbacks[groupName][tabName]) {
				STORM.DHTML.tabs._callbacks[groupName][tabName]();
			}
			STORM.event.fireWhenReady('tab.load', groupName, tabName);
		},
		loadFirst: function (groupName) {
			var first = $('div.tab_group[group=' + groupName + '] > div.tab').attr('tab');
			STORM.DHTML.tabs.changeTab(groupName, first, true);
		},
		loadByLocation: function (groupName) {
			var hash   = location.hash.replace(/#/, '');
			if (hash) {
				if (hash.indexOf('/') != -1) {
					var parts = hash.split('/');
					var tab  = $('div[hash=' + parts[0] + ']');
					STORM.DHTML.tabs.changeTab(tab.attr('group'), tab.attr('tab'));
				}
				var tab  = $('div[hash=' + hash + ']');
				STORM.DHTML.tabs.changeTab(tab.attr('group'), tab.attr('tab'));
			}
			else {
				STORM.DHTML.tabs.loadFirst(groupName);
			}
		},
		selected: function (groupName) {
			return STORM.DHTML.tabs._current[groupName];
		}
	},
	greyOut: function (speed) {
		if (STORM.DHTML._greyCount == 0) {
			var height = $(document).height();
			$('#translucent').css({ opacity: 0.0, height: height + 'px' }).show().fadeTo(speed, 0.6);
		}
		STORM.DHTML._greyCount++;
	},
	unGreyOut: function (speed) {
		STORM.DHTML._greyCount--;
		if (STORM.DHTML._greyCount == 0) {
			$('#translucent').fadeOut(speed);
		}
	},
	showPopup: function () {
		STORM.DHTML.greyOut('slow');
		var l = arguments.length;
		for (var i=0; i<l; i++) {
			var selector  = arguments[i];
			var absPos    = STORM.vars.popupY;
			var relPos    = absPos - $(window).scrollTop();
			var winHeight = $(window).height();

			if ((relPos + 200) > winHeight) {
				absPos -= 240;
			}

			// stop() is to stop any current animation and reset the object
			$(selector).stop(true, true);
			$(selector).css({ top: absPos + 'px' }).slideDown('slow');
		}
	},
	hidePopup: function () {
		STORM.DHTML._greyCount--;
		if (STORM.DHTML._greyCount == 0) {
			$('#translucent').fadeOut('slow');
		}
		var l = arguments.length;
		for (var i=0; i<l; i++) {
			var selector = arguments[i];
			$(selector).slideUp('slow');
		}
	},
	rollover: function () {
		var l = arguments.length;
		for (var i=0; i<l; i++) {
			var selector = arguments[i];
			$(selector).mouseover(function (event) {
				if (!$(this).hasClass('grayedout')) {
					this.src = this.src.replace(/\.png/, '-up.png');
				}
			}).mouseout(function (event) {
				if (!$(this).hasClass('grayedout')) {
					this.src = this.src.replace(/-up\.png/, '.png');
				}
			});
		}
	},
	trackMouse: function () {
		STORM.vars.popupY = 180;
		STORM.vars.mouseX = 0;
		STORM.vars.mouseY = 0;
		var l = arguments.length;
		for (var i=0; i<l; i++) {
			var selector = arguments[i];
			$(selector).mousemove(function (event) {

				STORM.vars.popupY = (event.pageY > 280) ? event.pageY - 100 : 180;
				STORM.vars.mouseX = event.pageX;
				STORM.vars.mouseY = event.pageY;
			});
		}
	},
	popupLogin: function () {
		if (! STORM.vars.ajaxLoginEnabled) {
			STORM.vars.ajaxLoginEnabled = true;
			$('#login_form legend').html('Your session has expired.  Please log in again.');
			$('#login_form').submit(function (event) {
				$('#login_form input[type=submit]').attr({
					value: 'Logging In...',
					disabled: 'disabled'
				});
				var form = this;
				$.postJSON(STORM.vars.managePath + '/json/login.html', {
					username: form.storm_username.value,
					password: form.storm_password.value
				}, function (response) {
					$('#login_form input[type=submit]').attr({
						value: 'Log In',
						disabled: ''
					});
					if (response.error) {
						STORM.DHTML.popupError(response.error);
					}
					else {
						$('#login_form').get(0).reset();
						STORM.event.fire('user.login');
						STORM.DHTML.hidePopup('#login_box');
						STORM.vars.loggedOut = false;

						var l = STORM.JSON._redo.length;
						for (var i=0; i<l; i++) {
							var args = STORM.JSON._redo[i];
							STORM.JSON.load(args[0], args[1], args[2], args[3]);
						}
					}
				});
				event.preventDefault();
				return false;
			});
		}
		STORM.vars.loggedOut = true;
		STORM.DHTML.showPopup('#login_box');
	},
	_errors: [],
	_buildErrorMessage: function () {
		var ul = $('#error ul').empty().get(0);
		var l = STORM.DHTML._errors.length;
		for (var i=0; i<l; i++) {
			var li = document.createElement('LI');
			li.innerHTML = STORM.DHTML._errors[i];
			ul.appendChild(li);
		}
	},
	popupError: function () {
		if (STORM.DHTML._errors.length == 0) {
			/* only pop up on the first error */
			STORM.DHTML.greyOut('fast');
			var offset = STORM.vars.popupY || ($(window).scrollTop() + (($(window).height() / 2) - 100));
			$('#error').css({ top: offset + 'px' }).show();
		}
		for (var i=0; i<arguments.length; i++) {
			var arg = arguments[i];
			if ($.isArray(arg)) {
				for (var j=0; j<arg.length; j++) {
					STORM.DHTML._errors.push(arg[j]);
				}
			}
			else {
				STORM.DHTML._errors.push(arg);
			}
		}
		STORM.DHTML._buildErrorMessage();
	},
	hideError: function () {
		STORM.DHTML._errors = [];
		STORM.DHTML.hidePopup('#error');
	},
	popupMessage: function (message) {
		$('#notification_message').html(message);
		STORM.DHTML.showPopup('#notification');
		setTimeout(function () {
			STORM.DHTML.hidePopup('#notification');
		}, 3000);
	},
	popupConfirm: function (settings) {
		if (typeof(settings) != 'object') {
			var message = settings;
			settings = {
				message: message
			};
		}

		if (!settings.message)
			settings.message = '';
		if (!settings.cancel)
			settings.cancel = 'Cancel';
		if (!settings.confirm)
			settings.confirm = 'Confirm';
		if (!settings.title)
			settings.title = 'Request Verification';

		$('#verification h3').html(settings.title);
		$('#verification_message').html(settings.message);
		$('#verification_cancel span').html(settings.cancel);
		$('#verification_confirm span').html(settings.confirm);

		STORM.DHTML.showPopup('#verification');
	},
	getPrompt: function (key, domainStr) {
		var prompt = STORM.vars.prompts[key];
		prompt = prompt.replace(/\[\[instance\]\]/g, domainStr);
		return prompt;
	},
	getPromptX: function (name, vars) {
		var prompt = STORM.vars.prompts[name];
		for (var key in vars) {
			prompt = prompt.replace(new RegExp('\\[\\[' + key + '\\]\\]', 'g'), vars[key]);
		}
		return prompt;
	},
	_toggleLoadingId: 1,
	toggleLoading: function (selection) {
		$(selection).each(function () {
			if ($(this).attr('_storm_loading_id')) {
				var loading_id = $(this).attr('_storm_loading_id');
				$('#_storm_loading_' + loading_id).hide();
				$(this).show();
			} else {
				var height   = $(this).height();
				var width    = $(this).width();
				var type     = ($(this).css('display') == 'inline')
				                    ?
								'span'
									:
								'div';

				var loading  = document.createElement(type);

				$(loading).attr("id", "_storm_loading_" + STORM.DHTML._toggleLoadingId);
				$(this).attr("_storm_loading_id", STORM.DHTML._toggleLoadingId++);

				var zindex = $(this).css('z-index');

				if (zindex == 'auto') {
					zindex = 10;
				} else {
					zindex++;
				}

				var floatx = $(this).css('float');

				$(loading).css({
					'z-index':  zindex,
					'width':    '100%',
					'float':    floatx,
					'cursor':   'wait'
				});

				if (type == 'div') {
					$(loading).css({
						'text-align': 'center'
					});
				}

				if (width >= 200) {
					$(loading).html('<img src="' + STORM.vars.icons.loadingLarge + '">');
				} else {
					$(loading).html('<img src="' + STORM.vars.icons.loading + '">');
				}



				$(this).hide();
				$(this).after(loading);
				$(loading).show();

			}
		});
	},
	zoomIntoNotifications: function (selector) {
		var obj = $(selector);

		var orig    = obj.position();
		orig.width  = obj.width();
		orig.height = obj.height();

		var destination   = $('#leftstatus').position();
		destination.width = $('#leftstatus').width();

		var final_state = {
			top: destination.top + 70,
			left: destination.left,
			width: destination.width,
			height: '20px',
			opacity: 0
		};

		STORM.DHTML._greyCount--;
		if (STORM.DHTML._greyCount == 0) {
			$('#translucent').fadeOut(1000);
		}

		obj.animate(final_state, 1400, 'swing', function () {
			obj.css({
				width: 		orig.width,
				height: 	'auto',
				left:		orig.left,
				opacity: 	1
			});
			obj.hide();
		});
		STORM.activity.addPendingAction();
	},
	showLoading: function () {
		var loading = '<img src="' + STORM.vars.icons.loading + '">';
		var l = arguments.length;
		for (var i=0; i<l; i++) {
			if ($.isArray(arguments[i])) {
				for (var j=0; j<arguments[i].length; j++) {
					$(arguments[i][j]).html(loading);
				}
			}
			else {
				$(arguments[i]).html(loading);
			}
		}
	},
	newButton: function(text, type) {
		var div = $('<div>').addClass('button').addClass(type).text(text);
		STORM.DHTML.buildButton(div);
		STORM.DHTML.setButtonHover(div);
		return div;
	},
	buildButton: function (selector) {
		$(selector).each(function () {
			$(this).wrapInner('<span></span>');
			$(this).prepend('<div class="button-left"></div>');
			$(this).append('<div class="button-right"></div>');
		});
	},
	setButtonHover: function (selector) {
		if (!($.browser.msie && parseInt($.browser.version) < 7)) {
			$(selector).hover(
				function () {
					if (!$(this).hasClass('grayedout')) {
						$(this).toggleClass('over');
					}
				},
				function () {
					if (!$(this).hasClass('grayedout')) {
						$(this).toggleClass('over');
					}
				}
			);
		}
	},
	truncateTextToWidth: function (selector, goal, options) {
		if (typeof options != 'object') {
			options = {}
		}

		var obj = $(selector);
		obj.wrapInner('<span></span>');
		obj = obj.find('span');

		obj.css({ display: 'inline-block'});

		var width = obj.width();

		while (width > goal) {
			var text = obj.text();

			if (options.tooltip) {
				STORM.DHTML.TOOLTIP.add(selector, text);
				$(selector).removeClass('has_tooltip');
				options.tooltip = false;
			}

			obj.text(text.substr(0, text.length - 6) + '...');
			var newwidth = obj.width();

			if (newwidth == width) {
				// we aren't making any difference. bail to avoid infinte loop
				return;
			}
			width = newwidth;
		}
	},
	PRICING: {
		format: function (price, digits, pos) {
			if (price === null)
				return null;

			if (price.toString().indexOf('$') != -1) {
				/* already formatted */
				return price;
			}
			price  = parseFloat(price);
			pos    = pos || '';
			var sign = (price < 0) ? '-' : pos;
			return sign + '$' + Math.abs(price).toFixed(digits);
		},
		adjusted: function (option, handler, key) {
			var price = (option && option.price) ? {
				monthly: option.price.monthly,
				hourly:  option.price.hourly
			} : { monthly: 0, hourly: 0 };

			var currInstance = STORM.instanceList.current();
			var pos = (currInstance) ? '+' : '';

			if (handler && key) {
				var current      = (currInstance && currInstance[key]) ? STORM[handler].find(currInstance[key]) : null;
				if (current && current.price) {
					price.monthly -= current.price.monthly;
					price.hourly  -= current.price.hourly;
				}
			}
			price.monthly  = STORM.DHTML.PRICING.format(price.monthly, 2, pos);
			price.hourly   = STORM.DHTML.PRICING.format(price.hourly, 5, pos);
			return price;
		}
	},
	TOOLTIP: {
		price: {
			_build: function (hourly) {
				return '<b>' + STORM.DHTML.PRICING.format(hourly, 5) + ' / hr</b>';
			},
			dropdown: function (selector, list) {
				var tooltips = [];
				var l = list.length;
				for (var i=0; i<l; i++) {
					tooltips[i] = STORM.DHTML.TOOLTIP.price._build(list[i].hourly);
				}
				$(selector).tooltip({
					delay: 100,
					bodyHandler: function () {
						return tooltips[this.selectedIndex];
					}
				});
				/* don't propagate mouseover events up to the select so it doesn't trigger the tooltip until they select something */
				$(selector + ' option').mouseover(function (e) {
					e.preventDefault();
					return false;
				});
			},
			insert: function (selector, price) {
				$(selector).addClass('has_tooltip').html(STORM.DHTML.PRICING.format(price.monthly, 2) + ' / mo').tooltip({
					delay: 100,
					bodyHandler: function () {
						return STORM.DHTML.TOOLTIP.price._build(price.hourly);
					}
				});
			}
		},
		add: function (selector, content) {
			var obj = $(selector);

			if (obj.hasClass('has_tooltip')) {
				return;
			}

			obj.addClass('has_tooltip');
			obj.tooltip({
				delay: 100,
				showURL: false,
				bodyHandler: function () {
					return content;
				}
			});
		}
	}
};

STORM.verification = {
	prompt: function (message, callback) {
		STORM.verification._actionPrompt = false;
		STORM.verification._callback = callback;
		STORM.DHTML.popupConfirm(message);
	},
	actionPrompt: function (message, callback) {
		STORM.verification._actionPrompt = true;
		STORM.verification._callback = callback;
		STORM.DHTML.popupConfirm(message);
	},
	confirm: function () {
		var callback = STORM.verification._callback;
		/* clear it out so multiple confirms don't repeat the action */
		STORM.verification._callback = null;
		if (callback) {
			if (callback()) {
				if (STORM.verification._actionPrompt) {
					STORM.DHTML.zoomIntoNotifications('#verification');
				} else {
					STORM.DHTML.hidePopup('#verification');
				}
			}
			else {
				STORM.verification._callback = callback;
			}
		}
	},
	cancel: function () {
		STORM.DHTML.hidePopup('#verification');
	},
	extra: {
		reboot: function () {
			var force = ($('#reboot_force').attr('checked')) ? '1' : '0';
			STORM.vars.urlTodo += '&force=' + force;
			return true;
		},
		destroy: function () {
			var input = $('#confirm_destroy');
			STORM.validation.removeHighlight(input);
			var confirm = input.attr('value').toLowerCase();
			if (confirm != 'destroy') {
				STORM.validation.highlight(input, "You must enter 'destroy' here");
				return false;
			}
			return true;
		}
	}
};
