jQuery.debug = function(o) {
	alert(toJSON(o));
};

jQuery.isDefined = function(o) {
	return o !== undefined;
};

jQuery.toString = function() {
	var o = arguments[0];
	if (jQuery.isFunction(o)) {
		var args = [];
		for (var i=1; i<arguments.length; i++) {
			args.push(arguments[i]);
		}
		return o.apply(this, args);
	}
	return o;
};

jQuery.getJSON = function(url, data) {
	var json;
	$.ajax({
		url: url,
		data: data,
		dataType: 'json',
		async: false,
		success: function(data) { json = data; }
	});
	return json;
};

jQuery.preferences = function(name) {
	if (!(this instanceof jQuery.preferences)) {
		return new jQuery.preferences(name);
	}
	var cookieName = name;
	var prefs = {};
	var val = Base64.decode($.cookie(name));
	if (val) prefs = parseJSON(Base64.decode($.cookie(name)));
	
	this.set = function(name, value) {
		prefs[name] = value;
		return this;
	}
	
	this.get = function(name) {
		return prefs[name];
	}
	
	this.save = function() {
		$.cookie(cookieName, Base64.encode(toJSON(prefs)));
		return this;
	}
}

jQuery.rectangle = function(l, t, w, h) {
	if (!(this instanceof jQuery.rectangle)) {
		return new jQuery.rectangle(l, t, w, h);
	}
	this.left = l;
	this.top = t;
	this.width = w;
	this.height = h;
	
	this.translate = function(x, y) {
		this.left += x;
		this.top += y;
		return this;
	}
	
	this.resize = function(w, h) {
		this.width += w;
		this.height += h;
	}
	
	this.intersects = function(r) {
	    if ( this.width <= 0 || this.height <= 0 || r.width <= 0 || r.height <= 0 )
	        return false;

	    return !((r.left + r.width <= this.left) ||
	             (r.top + r.height <= this.top) ||
	             (r.left >= this.left + this.width) ||
	             (r.top >= this.top + this.height));
	}
	
	this.intersection = function(r) {
        var lx = this.left, ly = this.top, lcx = lx+this.width, lcy = ly+this.height;
        var rlx = r.left, rly = r.top, rlcx = rlx+r.width, rlcy = rly+r.height;

        if (rlx > lx) lx = rlx;
        if (rly > ly) ly = rly;
        if (rlcx < lcx) lcx = rlcx;
        if (rlcy < lcy) lcy = rlcy;

        if (lcx - lx < 0 || lcy - ly < 0) return jQuery.rectangle(0,0,0,0);

        return jQuery.rectangle(lx,ly,lcx-lx,lcy-ly);
	}
	
	this.union = function(r) {
		var x1 = Math.min(this.left, r.left);
		var x2 = Math.max(this.left + this.width, r.left + r.width);
		var y1 = Math.min(this.top, r.top);
		var y2 = Math.max(this.top + this.height, r.top + r.height);
		return jQuery.rectangle(x1, y1, x2 - x1, y2 - y1);
	}
	
	this.grow = function(h, v) {
		this.left -= h;
		this.top -= v;
		this.width += h * 2;
		this.height += v * 2;
		return this;
	}
}

jQuery.URL = function(url, options) {
	if (!(this instanceof jQuery.URL)) {
		return new jQuery.URL(url, options);
	}

	url = url || document.location.href;
	this.settings = jQuery.extend({}, options);
	this.params = {};
	this.host = url.substringBefore('?');
	this.hash = url.substringAfterLast('#');
	
	var parts = url.substringAfter('?').substringBefore('#').split('&');
	for (var i=0; i<parts.length; i++) {
		var entry = parts[i].split('=');
		this.addParameter(entry[0], entry[1]);
	}
}

jQuery.URL.prototype = {
	getParameter: function(name) {
		var p = this.params[name];
		return p ? p[0] : null;
	},
	getParameters: function(name) {
		if (name) return this.params[name];
		var arr = {};
		for (var p in this.params) {
			var e = this.params[p];
			arr[p] = (e.length > 1) ? e : e[0];
		}
		return arr;
	},
	setParameter: function(name, value) {
		if (value && !jQuery.isFunction(value)) {
			this.params[name] = jQuery.makeArray(value);
		} else {
			delete this.params[name];
		}
		return this;
	},
	addParameter: function(name, value) {
		if (value && !jQuery.isFunction(value)) {
			if (!this.params[name]) this.params[name] = [];
			this.params[name] = this.params[name].concat(value);
		} else {
			delete this.params[name];
		}
		return this;
	},
	addParameters: function(map) {
		for (var a in map) {
			this.addParameter(a, map[a]);
		}
		return this;
	},
	toString: function() {
		var url = [];
		if (this.host) url.push(this.host);
		
		var query = [];
		for (var p in this.params) {
			for (var i=0; i<this.params[p].length; i++) {
				query.push(p + '=' + this.params[p][i]);
			}
		}
		if (query.length > 0) {
			if (url.length > 0) url.push('?');
			url.push(query.join('&'));
		}
		if (this.hash) {
			url.push('#');
			url.push(this.hash);
		}
		return url.join('');
	}
};

jQuery.fn.rectangle = function() {
	var off = this.offset();
	return jQuery.rectangle(off.left, off.top, this.outerWidth(), this.outerHeight());
};

jQuery.fn.overlap = function(e) {
	var r = this.rectangle();
	return $(e).filter(function() {
		return r.intersects($(this).rectangle());
	});
}

jQuery.fn.setVisible = function(b) {
	this.css('visibility', b ? 'visible' : 'hidden');
};

jQuery.fn.value = function(val) {
	var self = this.data('this');
	return self !== undefined && self.value ? self.value(val) : this.val(val);
}

jQuery.fn.values = function() {
	var vals = [];
	this.each(function() {
		var val = jQuery(this).value();
		if (val) vals = vals.concat(jQuery.makeArray(val));
	});
	return vals;
};

jQuery.fn.textValues = function() {
	var vals = [];
	this.each(function() {
		vals.push($(this).text());
	});
	return vals;
};

jQuery.fn.template = function(source, data) {
	this.html($(source).html().format(data));
	return this;
};

jQuery.fn.scrollTo = function(target, duration) {
	var pos = jQuery(target).position();
	var top = pos ? pos.top + this.scrollTop() : -this.scrollTop();
	this.animate({scrollTop: top}, duration || 200);
	return this;
};

jQuery.fn.textNodes = function() {
	var ret = [];
	if (this.length > 0) {
	    (function(el){
	        if ((el.nodeType == 3)||(el.nodeName =="BR"))
	            ret.push(el);
	        else
	            for (var i=0; i < el.childNodes.length; ++i)
	                arguments.callee(el.childNodes[i]);
	    })(this[0]);
	}
    return jQuery(ret);
}

jQuery.fn.comments = function() {
	var ret = [];
	if (this.length > 0) {
	    (function(el){
	        if (el.nodeType == 8)
	            ret.push(el.nodeValue);
	        else
	            for (var i=0; i < el.childNodes.length; ++i)
	                arguments.callee(el.childNodes[i]);
	    })(this[0]);
	}
    return ret;
}

jQuery.fn.nearest = function(selector) {
	var prev = $(this).prevAll(selector);
	var parent = $(this).parent();
	if (prev.length > 0 || parent.length == 0) return prev;
	return $(this).parent().nearest(selector);
}

jQuery.fn.radioGroup = function(options) {
	settings = jQuery.extend({
		
	}, options);
	var names = [];
	var self = this;
	$('.rad input:radio', self)
		.each(function() {
			names.push($(this).val());
		})
		.click(function() {
			for (var i=0; i<names.length; i++) {
				$('#' + names[i]).hide();
			}
			$('#' + $(this).val()).show();
		});
	$('.radA a', self).click(function() {
		$(this).nearest('input:radio').click();
		return false;
	});
	
	this.select = function(name) {
		$('.rad input:radio[value={0}]'.format(name), self).click();
	};
	return this;
}

jQuery.fn.attributeEditor = function(options) {
	var settings = jQuery.extend({
		attribute: 'input:radio'
	}, options);
	var self = this;
	$(settings.attribute, self)
		.each(function() {
			var editor = $('#' + $(this).attr('id') + '-edit').addClass('attribute-editor');
			if ($(this).is(':checked')) {
				editor.show();
			} else {
				editor.hide();
			}
		})
		.bind('click change', function() {
			$('.selected', self).removeClass('selected');
			$(this).addClass('selected');
			$('.attribute-editor', $(settings.editor || self)).hide();
			$('#' + $(this).attr('id') + '-edit').show();
			if (settings.onChange) settings.onChange.call(this);
		});
	return this;
};

jQuery.fn.menuSelect = function(menus, options) {
	return this.drilldown(jQuery.extend({
		data: menus,
		header: function(level) {
			if (level == 0)
				return '<option selected>- Select Top Nav Menu -</option>';
			return '<option selected>- Select Left Nav Menu -</option>';
		}
	}, options));
};

jQuery.fn.languagedData = function(options) {
	settings = jQuery.extend({
		data: [],
		editor: function(lang, defaultLang) {
			return '<div class="label">{0.name} Title{1}</div><input name="name{0.id}" class="textInput" type="text" size="30" maxLength="240">'.format(lang, defaultLang ? '<em class="sys">*</em>' : '');
		}
	}, options);
	
	var self = this;
	$(self).html(createEditor());
	$('select[name=languageSelector]', self).change(function() {
		$('.otherLanguages .languageEdit', self).hide();
		$('.otherLanguages #lang{0}'.format($(this).val()), self).show();
	});
	
	function createEditor() {
		var data = settings.data;
		var html = [];
		var edit = [];
		html.push('<table id="languages" width="100%" border="0" cellSpacing="2" cellPadding="2">');
		html.push('<tr>');
		edit.push('<tr>');
		for (var i=0; i<data.length; i++) {
			if (data[i]['default'] == 'Y') {
				html.push('<th class=areaHeader width="50%">{0.name}</th>'.format(data[i]));
				edit.push('<td class="coContainer">');
				edit.push('<div class="languageEdit" id="lang{0.id}">'.format(data[i]), settings.editor(data[i], true), '</div>');
				edit.push('</td>');
			}
		}
		html.push('<th class=areaHeader>');
		html.push('<select name="languageSelector">');
		html.push('<option>- Select a language to translate -</option>');
		edit.push('<td class="otherLanguages coContainer">');
		for (var i=0; i<data.length; i++) {
			if (data[i]['default'] != 'Y') {
				html.push(('<option value="{0.id}"' + (data[i].active == 'Y' ? '' : ' class=dis') + '>{0.name}</option>').format(data[i]));
				edit.push('<div class="languageEdit" id="lang{0.id}">'.format(data[i]), settings.editor(data[i]), '</div>');
			}
		}
		edit.push('</td>');
		html.push('</select>');
		html.push('</th>');

		edit.push('</tr>');
		html.push('</tr>');
		html.push(edit.join(''));
		html.push('</table>');
		return html.join('');
	}
	
	this.serialize = function() {
		var data = {};
		$('.languageEdit input, .languageEdit textarea', self).each(function() {
			var $this = $(this);
			var name = $this.attr('name');
			var value = $this.val();
			var cat = name.substringBefore('-');
			var lang = name.substringAfter('-');
			
			if (value) {
				if (!data[lang]) data[lang] = {};
				data[lang][cat] = value;
			}
		});
		return data;
	}
	return this;
};

jQuery.fn.grid = function(options) {
	settings = jQuery.extend({
		max: 50,
		formatCell: function(row, header) { return row[header.toLowerCase()]; }
	}, options);
	
	var self = this;
	var table = createTable();
	
	$(self).empty().append(table);
	gotoPage(0);
	
	function createTable() {
		var table = $('<table class=stats border=0 cellpadding=0 cellspacing=0><thead></thead><tbody></tbody></table>');
		if (settings.headers) {
			var tr = $('<tr class=headers></tr>')
			if (settings.selectable) {
				tr.append($('<th class=header width=1><input type=checkbox onclick="$(\'input[name=ids]\').attr(\'checked\', this.checked);"></th>'));
			}
			for (var i=0; i<settings.headers.length; i++) {
				tr.append($('<th class=header>' + settings.headers[i] + '</th>'));
			}
			$('thead', table).append(tr);
		}
		return table;
	}
	
	function gotoPage(page) {
		var html = [];
		var data = settings.data;
		var offset = page * settings.max;
		for (var i=offset; i<data.length && i<(offset + settings.max); i++) {
			var row = data[i];
			
			html.push('<tr class="', (i - offset) % 2 == 0 ? 'evenRow' : 'oddRow','">');
			if (settings.selectable) {
				html.push('<td><input type=checkbox name=ids value="', row.id, '"></td>');
			}
			for (var j=0; j<settings.headers.length; j++) {
				html.push('<td>', settings.formatCell(row, settings.headers[j]) , '</td>');
			}
			html.push('</tr>');
		}
		$('tbody', table).html(html.join(''));
	}
};

jQuery.fn.highlight = function(options) {
	timeout = null;
	
	settings = jQuery.extend({
		action: 'keyup change',
		container: 'body',
		delay: 200,
		highlightClass: 'highlight',
		value: function (e) { return jQuery(e).val(); }
	}, options);
	
	this.bind(settings.action, function() {
		var value = settings.value(this);

	    if (settings.onBeforeHighlight) {
	    	settings.onBeforeHighlight.call(this);
	    }
		if (value.length > 0) {
			clearTimeout(timeout);
			timeout = setTimeout('doHighlight("' + value + '")', settings.delay);
		} else {
			var container = $(settings.container);
			container.each(doClear);
		    if (settings.onAfterHighlight) {
		    	settings.onAfterHighlight.call(container);
		    }
		}
	});

	doClear = function() {
		var container = $(this);
		container.find('span.' + settings.highlightClass).each(function() {
			var parent = this.parentNode;
			var text = this.firstChild;
			parent.replaceChild(text, this);
			var nodes = [text.previousSibling, text, text.nextSibling];

			var node;
			for (var i=0; i<nodes.length; i++) {
				if (nodes[i] && nodes[i].nodeType == 3) {
					if (!node) {
						node = nodes[i];
					} else {
						node.nodeValue += nodes[i].nodeValue;
						parent.removeChild(nodes[i]);
					}
				}
			}
		});
	}
	
	doHighlight = function(value) {
		var re = new RegExp(value, 'i');
		var container = jQuery(settings.container);
		container.each(doClear).each(function() {
			var container = $(this);
			container.textNodes().each(function(index) {
			    var node = this;
			    var pos = node.nodeValue.search(re);
			    if (pos > -1) {
				    var middlebit = node.splitText(pos);
				    var endbit = middlebit.splitText(value.length);
				    var middleclone = middlebit.cloneNode(true);
				    var spannode = document.createElement('span');
				    spannode.className = settings.highlightClass;
				    spannode.appendChild(middleclone);
				    middlebit.parentNode.replaceChild(spannode, middlebit);
				    if (settings.onHighlight) {
				    	settings.onHighlight.call(this);
				    }
			    }
			});
		});
	    if (settings.onAfterHighlight) {
	    	settings.onAfterHighlight.call(container);
	    }
	}
};

jQuery.fn.tree = function(options) {
	var settings = jQuery.extend({
		data: [],
		childProperty : 'children',
		checkbox: false,
		label: '{0}'
	}, options);

	this.addClass('ui-tree').each(function() {
		$(this).html(createTree(settings.data));
		$('.parent > .toggle', this).click(function() {
			$(this).parent().toggleClass('expanded').children('ul').toggleClass('expanded');
		});
	});

	this.expandAll = function() {
		$('.parent', this).addClass('expanded').children('ul').addClass('expanded');
	}
	
	this.collapseAll = function() {
		$('.expanded', this).removeClass('expanded');
	}

	this.selectAll = function(checked) {
		$(':checkbox[name=ids]', this).attr('checked', checked);
	}
	
	function createTree(nodes) {
		var html = new Array();
		if (nodes && nodes.length > 0) {
			html.push('<ul>');
			for (var i=0; i<nodes.length; i++) {
				var cls = ['leaf'];
				if (nodes[i].sub) cls.push('parent');
				if (i == nodes.length - 1) cls.push('last');
				
				html.push('<li class="' + cls.join(' ') + '">');
				html.push('<span class=toggle></span>');
				if (settings.checkbox) html.push('<input type=checkbox name=ids value={0.id}>'.format(nodes[i]));
				if (jQuery.isFunction(settings.label)) {
					html.push(settings.label(nodes[i]));
				} else {
					html.push(settings.label.format(nodes[i]));
				}
				html.push(createTree(nodes[i][settings.childProperty]));
				html.push('</li>');
			}
			html.push('</ul>');
		}
		return html.join('');
	}
	return this;
};

jQuery.widget('ui.taxonomy', {
	_init: function() {
		var self = this, o = this.options;
		self.element.data('this', self);
		var taxonomy = this._getData('taxonomy');
		var select = $('<select></select>').change(function() {
			var sel = $(this);
			var val = sel.val();
			var tax = taxonomy[val];
			var tf = self['_create' + tax.field];
			if (tf && jQuery.isFunction(o.loadTaxonomy)) {
				sel.next().empty().append(tf.call(tax, o.loadTaxonomy(val)));
			}
		});
		$.each(this.options.data, function(index, value) {
			taxonomy[value.id] = value;
			select.append('<option value="{0.id}">{0.name}</option>'.format(value));
		});
		this.element
			.append(select)
			.append('<div></div>');
		
		select.change();
	},
	_createText: function(data) {
		return '<div><input name="{0.name}" class="textInput" type=text maxLength=240>&nbsp;<a href="#" onclick="doLookup(\'dav\', \'{0.name}\');">Look&nbsp;Up</a></div>'.format(this);
	},
	_createSingle: function(data) {
		var html = [];
		html.push('<div><select multiple size=10>');
		$.each(data, function(index, value) {
			html.push('<option>{0.name}</option>'.format(value));
		});
		html.push('</select></div>');
		return html.join('');
	},
	_createMultilevel: function(data) {
		return jQuery('<div class=drilldown></div>').drilldown({
			data: data,
			header: function(index) {
				return '<option>--Level {0}--</option>'.format((index + 1));
			}
		});
	},
	value: function() {
		var values = [];
		jQuery('select:has(option[value!=-1]:selected),input,textarea', this.element).each(function() {
			var input = $(this);
			if (input.is('select')) {
				var nv = [];
				jQuery('option:selected', input).each(function() {
					var val = $(this).text();
					if (values.length == 0) {
						nv.push([val]);
					} else {
						for (var i=0; i<values.length; i++) {
							nv.push(values[i].concat(val));
						}
					}
				});
				values = nv;
			} else {
				var val = input.val();
				if (values.length == 0) {
					values.push([val]);
				} else {
					for (var i=0; i<values.length; i++) {
						values[i].push(val);
					}
				}
			}
		});
		return values;
	}
});

jQuery.extend(jQuery.ui.taxonomy, {
	taxonomy: {},
	defaults: {
		data: [],
		taxonomy: {}
	}
});
jQuery.widget('ui.drilldown', {
	_init: function() {
		var self = this, o = this.options;
		self.element.data('this', self);
		var items = self._getData('items');
		self._initItems(items, o.data);

		self.element.append(self._createSelect(o.data, 0));
	},
	_initItems: function(items, data) {
		if (data !== undefined) {
			for (var i=0; i<data.length;i++) {
				items[data[i].id] = data[i];
				this._initItems(items, data[i].sub);
			}
		}
	},
	_createSelect: function(data, level) {
		if (!data || data.length == 0) {
			return $();
		}
		
		var self = this, o = this.options;
		
		var select = $('<select></select>').change(function() {
			var sel = jQuery(this)
			self._change(sel);
			if (o.onChange) o.onChange.call(self, sel);		
		});

		if (o.className) select.addClass(o.className);
		
		var html = [];
		if (o.header) html.push(jQuery.toString(o.header, level));
		var curr = o.current ? $(o.current).val() : null;
		$.each(data, function(index, value) {
			if (value.id == curr) {
				html.push('<option value="{0.id}" disabled>{0.name}</option>'.format(value));
			} else {
				html.push('<option value="{0.id}" class="{0.class}">{0.name}</option>'.format(value));
			}
		});
		select.html(html.join(''));
		return select;
	},
	_change: function(sel) {
		var self = this, o = this.options;
		var items = self._getData('items');
		var curr = items[sel.val()];
		sel.nextAll('select').remove();
		if (curr && curr.sub) {
			self.element.append(self._createSelect(curr.sub, sel.prevAll('select').length + 1));
		}
	},
	getItem: function(id) {
		return this._getData('items')[id];
	},
	value: function(val) {
		var self = this, o = this.options;
		if (val !== undefined) {
			var items = self._getData('items');
			var item = items[val];
			if (item !== undefined) {
				if (item.parent !== undefined) {
					self.value(item.parent);
				}
				var sel = jQuery('select:last', self.element).val(val);
				self._change(sel);
				if (o.onChange) o.onChange.call(self, sel);		
			}
			return self;
		}
		return jQuery('select[value]:last', self.element).val();
	}
});

jQuery.extend(jQuery.ui.drilldown, {
	defaults: {
		data: [],
		items: {},
		header: '<option disabled selected>- Select Item -</option>'
	}
});