(function($) {
	//This prototype is provided by the Mozilla foundation and
	//is distributed under the MIT license.
	//http://www.ibiblio.org/pub/Linux/LICENSES/mit.license
	if(!Array.prototype.map)
	{
		Array.prototype.map = function(fun /*, thisp*/)
		{
			var len = this.length;
			if (typeof fun != "function")
				throw new TypeError();
	
			var res = new Array(len);
			var thisp = arguments[1];
			for (var i = 0; i < len; i++)
			{
				if (i in this)
					res[i] = fun.call(thisp, this[i], i, this);
			}
	
			return res;
		};
	}

	String.prototype.trim = function() {
		return this.replace(/^\s+|\s+$/g,"");
	}
	String.prototype.ltrim = function() {
		return this.replace(/^\s+/,"");
	}
	String.prototype.rtrim = function() {
		return this.replace(/\s+$/,"");
	}

	String.prototype.stripScripts = function() {
		return this.replace(new RegExp('<script[^>]*>([\\S\\s]*?)<\/script>', 'img'), '');
	};
	
	String.prototype.extractScripts = function() {
		var matchAll = new RegExp('<script[^>]*>([\\S\\s]*?)<\/script>', 'img');
		var matchOne = new RegExp('<script[^>]*>([\\S\\s]*?)<\/script>', 'im');
		return (this.match(matchAll) || []).map(function(scriptTag) {
			return (scriptTag.match(matchOne) || ['', ''])[1];
		});
	};
	
	String.prototype.evalScripts = function() {
		return this.extractScripts().map(function(script) { return eval(script) });
	};

	var isOpera     = window.opera && opera.buildNumber ? true : false;
	var isWebKit    = /WebKit/.test(navigator.userAgent);
	var isOldWebKit = isWebKit && !window.getSelection().getRangeAt;
	var isIE        = !isWebKit && !isOpera && (/MSIE/gi).test(navigator.userAgent) && (/Explorer/gi).test(navigator.appName);
	var isIE6       = isIE && /MSIE [56]/.test(navigator.userAgent);
	var isGecko     = !isWebKit && /Gecko/.test(navigator.userAgent);
	var isMac       = navigator.userAgent.indexOf('Mac') != -1;

	var keycodes = {
		'backspace'     : 8,
		'tab'           : 9,
		'enter'         : 13,
		'shift'         : 16,
		'ctrl'          : 17,
		'alt'           : 18,
		'pause'         : 19,
		'break'         : 19,
		'caps lock'     : 20,
		'escape'        : 27,
		'page up'       : 33,
		'page down'     : 34,
		'end'           : 35,
		'home'          : 36,
		'left arrow'    : 37,
		'up arrow'      : 38,
		'right arrow'   : 39,
		'down arrow'    : 40,
		'insert'        : 45,
		'delete'        : 46,
		'0'             : 48,
		'1'             : 49,
		'2'             : 50,
		'3'             : 51,
		'4'             : 52,
		'5'             : 53,
		'6'             : 54,
		'7'             : 55,
		'8'             : 56,
		'9'             : 57,
		'a'             : 65,
		'b'             : 66,
		'c'             : 67,
		'd'             : 68,
		'e'             : 69,
		'f'             : 70,
		'g'             : 71,
		'h'             : 72,
		'i'             : 73,
		'j'             : 74,
		'k'             : 75,
		'l'             : 76,
		'm'             : 77,
		'n'             : 78,
		'o'             : 79,
		'p'             : 80,
		'q'             : 81,
		'r'             : 82,
		's'             : 83,
		't'             : 84,
		'u'             : 85,
		'v'             : 86,
		'w'             : 87,
		'x'             : 88,
		'y'             : 89,
		'z'             : 90,
		'f1'            : 112,
		'f2'            : 113,
		'f3'            : 114,
		'f4'            : 115,
		'f5'            : 116,
		'f6'            : 117,
		'f7'            : 118,
		'f8'            : 119,
		'f9'            : 120,
		'f10'           : 121,
		'f11'           : 122,
		'f12'           : 123,
		'num lock'      : 144,
		'scroll lock'   : 145,
		'semi-colon'    : 186,
		'comma'         : 188,
		'dash'          : 189,
		'period'        : 190,
		'forward slash' : 191,
		'grave accent'  : 192,
		'open bracket'  : 219,
		'back slash'    : 220,
		'close bracket' : 221,
		'single quote'  : 222
	};

	$.keycode_equals = function(e, key) {
		keycode = isIE ? event.keyCode : e.keyCode;

		if(keycodes[key] == keycode)
		{
			return true;
		}
		return false;
	};

	$.disable_event = function(e) {
		// mozilla
		if(e.preventDefault)
		{
			e.preventDefault();
		}
		// ie
		else
		{
			e.returnValue = false;
			// fix some stuborn events, like F5 and ctrl-f
			e.keyCode = 0;
		}
	};
	
	$.isIE = function() {
		return isIE;
	};

	$.get_width = function(element) {
		return $(element).width() + $.parse_int($(element).css('paddingLeft')) + $.parse_int($(element).css('paddingRight')) + $.parse_int($(element).css('marginLeft')) + $.parse_int($(element).css('marginRight')) + $.parse_int($(element).css('borderLeftWidth')) + $.parse_int($(element).css('borderRightWidth'));
	},

	$.get_height = function(element) {
		return $(element).height() + $.parse_int($(element).css('paddingTop')) + $.parse_int($(element).css('paddingBottom')) + $.parse_int($(element).css('marginTop')) + $.parse_int($(element).css('marginBottom')) + $.parse_int($(element).css('borderTopWidth')) + $.parse_int($(element).css('borderBottomWidth'));
	},

	// Core code from - quirksmode.com
	// Edit for Firefox by pHaez
	$.get_page_size = function() {
		var width, height;
		// Mozilla
		if(window.innerHeight && window.scrollMaxY)
		{
			width  = window.innerWidth  + window.scrollMaxX;
			height = window.innerHeight + window.scrollMaxY;
		}
		// ??
		else if(document.body.scrollHeight > document.body.offsetHeight)
		{
			width  = document.body.scrollWidth;
			height = document.body.scrollHeight;
		}
		// Chrome + IE7
		else
		{
			width  = document.body.offsetWidth;
			height = document.body.offsetHeight;
		}

		var window_width, window_height;
		// Mozilla + Chrome + most others
		if(self.innerHeight)
		{
			if(document.documentElement.clientWidth)
			{
				window_width = document.documentElement.clientWidth; 
			}
			else
			{
				window_width = self.innerWidth;
			}
			window_height = self.innerHeight;
		}
		// IE7
		else if(document.documentElement && document.documentElement.clientHeight)
		{
			window_width  = document.documentElement.clientWidth;
			window_height = document.documentElement.clientHeight;
		}
		// ??
		else if(document.body)
		{
			window_width  = document.body.clientWidth;
			window_height = document.body.clientHeight;
		}

		// for small pages with total height less than height of the viewport
		if(height < window_height)
		{
			height = window_height;
		}

		// for small pages with total width less than width of the viewport
		if(width < window_width)
		{
			width = window_width;
		}

		return {width: width, height: height, window_width: window_width, window_height: window_height};
	};
	
	// Core code from - quirksmode.com
	$.get_page_scroll = function() {
		var left, top;

		// ??
		if(self.pageYOffset)
		{
			left = self.pageXOffset;
			top  = self.pageYOffset;
		}
		// ??
		else if(document.documentElement && document.documentElement.scrollTop)
		{
			left = document.documentElement.scrollLeft;
			top  = document.documentElement.scrollTop;
		}
		// IE7 + Mozilla + Chrome
		else if(document.body)
		{
			left = document.body.scrollLeft;	
			top  = document.body.scrollTop;
		}

		return {left: left, top: top};
	};

	$.parse_int = function(val) {
		// get rid of preceeding 0's
		while(val.substr(0,1) == '0')
		{
			val = val.substr(1);
		}
		var num = parseInt(val);
		if(isNaN(num))
			num = 0;
		return num;
	};

	/* set cookie */
	$.set_cookie = function(name, value, expires, path, domain, secure)
	{
		if(value == '')
		{
			$.delete_cookie(name, path, domain);
			return;
		}

		// set domain
		if(typeof domain == 'undefined')
		{
			var parts = document.domain.split('.');
			domain = parts.pop();
			domain = '.'+parts.pop()+'.'+domain;
		}

		// set path
		if(typeof path == 'undefined')
		{
			path = '/';
		}

		// set time, it's in milliseconds
		var today = new Date();
		today.setTime(today.getTime());
		
		// if no expires is giving, assume 1 year
		if(typeof expires == 'undefined')
		{
			expires = 365*60*60*24;
		}
		// expires is number of days, so we convert
		// to days in seconds
		else
		{
			expires = expires*60*60*24;
		}
		// convert to milliseconds
		expires *= 1000;
		var expires_date = new Date(today.getTime()+(expires));

		document.cookie = name+'='+escape(value) +
			((expires) ? ';expires='+expires_date.toGMTString() : '') + 
			((path)    ? ';path='+path : '') + 
			((domain)  ? ';domain='+domain : '') +
			((secure)  ? ';secure' : '');
	};
	
	/* delete cookie */
	$.delete_cookie = function(name, path, domain)
	{
		// set domain
		if(typeof domain == 'undefined')
		{
			var parts = document.domain.split('.');
			domain = parts.pop();
			domain = parts.pop()+'.'+domain;
		}

		// set path
		if(typeof path == 'undefined')
		{
			path = '/';
		}

		document.cookie = name+"="+((path)?";path="+path:"")+((domain)?";domain="+domain:"")+";expires=Thu, 01-Jan-1970 00:00:01 GMT";
	};
	
	/* get cookie */
	$.get_cookie = function(name)
	{
		var start = document.cookie.indexOf(name+'=');
		var len = start + name.length + 1;
		if((!start) && (name != document.cookie.substring(0, name.length)))
		{
			return '';
		}
		if(start == -1)
			return '';
		var end = document.cookie.indexOf(";", len);
		if(end == -1)
			end = document.cookie.length;
		return unescape(document.cookie.substring(len, end));
	};
	
	// searches for an element with matching id or class (within a list of classes)
	// if found, returns the element
	// if not found, returns false
	$.find_event_target = function(e, settings) {
		var defaults = {
			id      : null,
			classes : null
		};
		settings = $.extend(defaults, settings);

		var target = e.target;
		// bad click
		if(target == null) return false;

		var found = false;
		// search until we hit the body or it is found or invalid
		while(target != null && target.tagName.toLowerCase() != 'body' && !found)
		{
			if(settings.id != null && settings.id == target.id)
			{
				found = true;
			}

			if(settings.classes != null && typeof settings.classes == 'object')
			{
				for(var i=0; i<settings.classes.length && !found; i++)
				{
					if($(target).hasClass(settings.classes[i]))
					{
						found = true;
					}
				}
			}
			
			if(!found)
			{
				target = target.parentNode;
			}
		}
		
		if(!found)
		{
			return false;
		}
		
		return target;
	};
	
	// ajax
	$.ajax_request = function(settings) {
		var defaults = {
			url      : 'index.php',
			type     : 'post',
			dataType : 'json',
			data     : {}
		};

		settings = $.extend(defaults, settings);

		$.ajax({
			url      : settings.url,
			data     : settings.data,
			type     : settings.type,
			dataType : settings.dataType,
			success  : function(response, textStatus) {
				var error   = response.error || null;
				var errors  = response.errors || null;
				var content = response.content || null;
				var scripts = '';
				
				if(error == 1)
				{
					moxyfy.prompt_show({title: response.title, content: response.content});
					return;
				}
				
				// were there errors?
				if(errors != null)
				{
					// execute error function
					if(typeof settings.error == 'function')
					{
						settings.error.apply(this, [response]);
					}
					else
					{
						var error_text    = '';
						var errors_length = errors.length;
						for(var i=0;i<errors_length;i++)
						{
							error_text += errors[i]+'<br />';
						}
						moxyfy.prompt_show({title: 'Oops!', content: error_text});
					}
					return;
				}
				
				if(content != null)
				{
					scripts = content.extractScripts();
					content = content.stripScripts();
				}
				
				// update
				if(typeof settings.update == 'string')
				{
					$(settings.update).html(content);
				}

				// execute success function
				if(typeof settings.success == 'function')
				{
					settings.success.apply(this, [response]);
				}

				// execute scripts
				if(scripts != '')
				{
					for(i in scripts)
					{
						eval(scripts[i]);
					}
				}
			}
		});
	};
	
	$.get_pos = function(element) {
		if(typeof element == "undefined" || element == null)
			return {"x":0,"y":0};

		if(typeof element == 'string')
			element = $(element);

		var offset = $(element).offset();
		return {'x':(offset.left),'y':offset.top};

/*
		var curleft = curtop = 0;
		if(element.offsetParent)
		{
			curleft = element.offsetLeft
			curtop = element.offsetTop
			while(element = element.offsetParent)
			{
				curleft += element.offsetLeft
				curtop += element.offsetTop
			}
		}
		return {"x":curleft,"y":curtop};
*/
	};
	
	$.fn.childOf = function(parent) {
		var found = false;
		this.each(function() {
			var target = this;
			while(!found && target != null && typeof target != 'undefined' && target.tagName != 'BODY')
			{
				if(target === parent)
				{
					found = true;
				}
				target = target.parentNode;
			}
		});

		return found;
	};
	
	$.fn.disable_selection = function() {
		return this.each(function() {
			$(this).bind('selectstart', function(e) { e.preventDefault(); return false; });
			if(isGecko)
				$(this).css({MozUserSelect : 'none'});
		});
	};
	
	$.cancel_selection = function() {
		if($.isIE())
		{
			var range = document.body.createTextRange();
			range.collapse(true);
		}
		else
		{
			var sel = window.getSelection ? window.getSelection() : document.selection;
			sel.removeAllRanges();
		}
	};
	
	$.gen_url = function(settings) {
		var defaults = {
			app    : 'www',
			mod    : '',
			act    : '',
			params : {}
		};
		
		settings = $.extend(defaults, settings);

		// get domain
		var parts = window.location.host.split('.');
		domain = parts.pop();
		domain = '.'+parts.pop()+'.'+domain;

		var url = window.location.protocol+'//'+settings.app+domain;
		if(settings.mod != '')
		{
			url += '/'+settings.mod;
			if(settings.act != '')
			{
				url += '/'+settings.act;
				
				for(var key in settings.params)
				{
					url += '/'+key+'/'+settings.params[key];
				}
			}
		}

		return url;
	};
	
	$.sprintf = function(str, i) {
		return str.replace(/%d/, i).replace(/%s/, i);
	};
	
	$.nohtml = function(str) {
		return str.replace(/&/g, "&amp;").replace(/</g,"&lt;").replace(/>/g, "&gt;").replace(/'/g, "&#039;").replace(/"/g, "&quot;");
	};
	
	$.html_wrap = function(str, chars) {
		return str.replace(RegExp('([^\\s]{'+chars+'})','ig'), "$1<wbr>");
	};
	
	$.preload_images = function(images) {
		var images_length = images.length;
		for(var i=0; i<images_length; i++)
		{
			$('<img>').attr('src', images[i]);
		}
	};
	
	$.in_array = function(needle, haystack) {
		var length = haystack.length;
		var i = 0;
		var found = false;
		while(i<length && !found)
		{
			if(haystack[i] === needle)
			{
				found = true;
			}
			i++;
		}
		return found;
	};
	
	$.array_key_exists = function(needle, haystack) {
		for(key in haystack)
		{
			if(key == needle)
			{
				return true;
			}
		}
		return false;
	};
	
	$.get_click_type = function(e) {
		if(e.which)
		{
			if(e.which == 1) { click_type = 'left'; }
			else if(e.which == 3) { click_type = 'right'; }
		}
		else
		{
			if(e.button == 1) { click_type = 'left'; }
			else if(e.button == 2) { click_type = 'right'; }
		}
		
		return click_type;
	};
	
	$.get_mouse_pos = function(e) {
		var mX, mY;
		if(isIE)
		{
			if(document.documentElement)
			{
				mX = event.clientX + document.documentElement.scrollLeft;
				mY = event.clientY + document.documentElement.scrollTop;
			}
			else if(document.body)
			{
				mX = event.clientX + document.body.scrollLeft;
				mY = event.clientY + document.body.scrollTop;
			}
		}
		else
		{
			mX = e.pageX;
			mY = e.pageY;
		}

		if(mX < 0){mX = 0}
		if(mY < 0){mY = 0}
		
		return {x:mX,y:mY};
	};

	$.fn.input_instruct = function(text) {
		if($(this).attr('value') == '')
		{
			$(this).attr('value', text);
		}
		else
		{
			$(this).addClass('active');
		}

		$(this).addClass('hint_input');
		
		$(this).bind('focus', function() {
			if($(this).attr('value') == text)
			{
				$(this).attr('value','');
				$(this).addClass('active');
			}
		});

		$(this).bind('blur', function() {
			if($(this).attr('value') == '')
			{
				$(this).attr('value',text);
				$(this).removeClass('active');
			}
		});
	};

	$.fn.input_area = function(settings) {
		var defaults = {
			auto_expand    : false,
			default_text   : '',
			blur_safe_area : null
		};
		settings = $.extend(defaults, settings);

		return this.each(function() {
			var self = this;

			// make content editable
			this.contentEditable = true;

			// default text
			if(settings.default_text != '')
			{
				$(this).html(settings.default_text);
			}

			// focus
			$(this).bind('focus', function() {
				if(settings.default_text == '' || $(this).html() == settings.default_text)
				{
					$(this).html('<br />');
					$(this).addClass('active');
					if(settings.auto_expand !== false)
					{
						$(this).height(settings.auto_expand.active_height);
					}
				}
			});

			// blur safe area
			if(settings.blur_safe_area != null)
			{
				$(document.body).bind('click', function(e) {
					if(!$(e.target).childOf($(settings.blur_safe_area)[0]))
					{
						var tmp = $(self).html();
						tmp = tmp.replace(/\s*/g,'');
						tmp = tmp.replace(/\&nbsp;/g,'');
						tmp = tmp.replace(/\n/g,'');
						tmp = tmp.replace(/\r/g,'');
						tmp = tmp.replace(/<br>/gi,'');
						tmp = tmp.replace(/<br \/>/gi,'');
						if(tmp == '')
						{
							$(self).html(settings.default_text);
							$(self).removeClass('active');
							if(settings.auto_expand !== false)
							{
								$(self).height(settings.auto_expand.height+'px');
							}
						}
					}
				});
			}

			// auto expand
			if(settings.auto_expand !== false)
			{
				$(self).height(settings.auto_expand.height+'px');
				this.style.overflow = 'hidden';

				// watch for need to expand vertically
				$(this).bind('keyup keydown blur', function(e) {
					this.style.height = 0;
					var height = this.scrollHeight;
					if(height < settings.auto_expand.active_height)
					{
						height = settings.auto_expand.active_height;
					}
					if(height > settings.auto_expand.max_height)
					{
						height = settings.auto_expand.max_height;
						this.style.overflowY = 'auto';
					}
					else
					{
						this.style.overflowY = 'hidden';
					}
					this.style.height = height+'px';
				});
			}
		});
	};
	
	$.set_chars_left = function(name, max_len) {
		var len = $('#'+name+'_input').val().length;
		var chars_left = 0;
		if(len >= max_len)
		{
			$('#'+name+'_input').val($('#'+name+'_input').val().substring(0, max_len));
		}
		else
		{
			chars_left = max_len-len;
		}
		$('#'+name+'_char_lim').html(chars_left.toString());
	};
})(jQuery);

/* UBBC Filter */
(function($){
	var filters = {
		'b' : {
			'open'  : 'strong',
			'close' : 'strong',
			'attr'  : {},
			'allow' : 'all',
			'parent' : []
		},
		'i' : {
			'open'  : 'em',
			'close' : 'em',
			'attr'  : {},
			'allow' : 'all',
			'parent' : []
		},
		'u' : {
			'open'  : 'span style="text-decoration: underline;"',
			'close' : 'span',
			'attr'  : {},
			'allow' : 'all',
			'parent' : []
		},
		's' : {
			'open'  : 'del',
			'close' : 'del',
			'attr'  : {},
			'allow' : 'all',
			'parent' : []
		},
		'sup' : {
			'open'  : 'sup',
			'close' : 'sup',
			'attr'  : {},
			'allow' : 'all',
			'parent' : []
		},
		'sub' : {
			'open'  : 'sub',
			'close' : 'sub',
			'attr'  : {},
			'allow' : 'all',
			'parent' : []
		},
		'marquee' : {
			'open'  : 'marquee',
			'close' : 'marquee',
			'attr'  : {},
			'allow' : 'all',
			'parent' : []
		},
		'pre' : {
			'open'  : 'pre',
			'close' : 'pre',
			'attr'  : {},
			'allow' : 'all',
			'parent' : []
		},
		'left' : {
			'open'  : 'div style="text-align: left;"',
			'close' : 'div',
			'attr'  : {},
			'allow' : 'all',
			'parent' : []
		},
		'center' : {
			'open'  : 'div style="text-align: center;"',
			'close' : 'div',
			'attr'  : {},
			'allow' : 'all',
			'parent' : []
		},
		'right' : {
			'open'  : 'div style="text-align: right;"',
			'close' : 'div',
			'attr'  : {},
			'allow' : 'all',
			'parent' : []
		},
		'hr' : {
			'open'  : 'hr',
			'close' : '',
			'attr'  : {},
			'allow' : 'none',
			'parent' : []
		},
		'size' : {
			'open'  : 'font',
			'close' : 'font',
			'attr'  : {
				'size' : 'size="%d"'
			},
			'allow' : 'all',
			'parent' : []
		},
		'face' : {
			'open'  : 'span',
			'close' : 'span',
			'attr'  : {
				'face' : 'style="font-family: %s;"'
			},
			'allow' : 'all',
			'parent' : []
		},
		'color' : {
			'open'  : 'span',
			'close' : 'span',
			'attr'  : {
				'color' : 'style="color: %s;"'
			},
			'allow' : 'all',
			'parent' : []
		},
		'url' : {
			'open'  : 'a target="_blank"',
			'close' : 'a',
			'attr'  : {
				'url'    : 'href="%s"'
			},
			'allow' : 'all',
			'parent' : []
		},
		'ftp' : {
			'open'  : 'a target="_blank"',
			'close' : 'a',
			'attr'  : {
				'url'    : 'href="%s"'
			},
			'allow' : 'all',
			'parent' : []
		},
		'img' : {
			'open'  : 'img',
			'close' : '',
			'attr'  : {
				'img'    : 'src="%s"',
				'width'  : 'width="%d"',
				'height' : 'height="%d"',
				'alt'    : 'alt="%s"'
			},
			'allow' : 'none',
			'parent' : []
		},
		'email' : {
			'open'  : 'a',
			'close' : 'a',
			'attr'  : {
				'email' : 'href="mailto:%s"'
			},
			'allow' : 'img',
			'parent' : []
		},
		'table' : {
			'open'  : 'table',
			'close' : 'table',
			'attr'  : {
				'width'       : 'width="%d"',
				'cellspacing' : 'cellspacing="%d"',
				'cellpadding' : 'cellpadding="%d"'
			},
			'allow' : 'tr',
			'parent' : []
		},
		'tr' : {
			'open'  : 'tr',
			'close' : 'tr',
			'attr'  : {},
			'allow' : 'td',
			'parent' : []
		},
		'td' : {
			'open'  : 'td',
			'close' : 'td',
			'attr'  : {},
			'allow' : 'all',
			'parent' : []
		},
		'tt' : {
			'open'  : 'tt',
			'close' : 'tt',
			'attr'  : {},
			'allow' : 'all',
			'parent' : []
		},
		'code' : {
			'open'  : 'code',
			'close' : 'code',
			'attr'  : {},
			'allow' : 'all',
			'parent' : []
		},
		'quote' : {
			'open'  : 'q',
			'close' : 'q',
			'attr'  : {},
			'allow' : 'all',
			'parent' : []
		},
		'list' : {
			'open'  : 'ol',
			'close' : 'ol',
			'attr'  : {},
			'allow' : 'li',
			'parent' : []
		},
		'ulist' : {
			'open'   : 'ul',
			'close'  : 'ul',
			'attr'   : {},
			'allow'  : 'li',
			'parent' : []
		},
		'li' : {
			'open'   : 'li',
			'close'  : 'li',
			'attr'   : {},
			'allow'  : 'all',
			'parent' : ['list','ulist']
		}
	};
	
	$.get_readable_date = function(settings) {
		var defaults = {
			today       : false,
			day_of_week : false,
			no_year     : false
		};
		settings = $.extend(defaults, settings);

		var date = new Date();
		var mon;
		switch(date.getUTCMonth()) {
			case 0  : mon = 'January'; break;
			case 1  : mon = 'February'; break;
			case 2  : mon = 'March'; break;
			case 3  : mon = 'April'; break;
			case 4  : mon = 'May'; break;
			case 5  : mon = 'June'; break;
			case 6  : mon = 'July'; break;
			case 7  : mon = 'August'; break;
			case 8  : mon = 'September'; break;
			case 9  : mon = 'October'; break;
			case 10 : mon = 'November'; break;
			case 11 : mon = 'December'; break;
		}
		
		var date_str = '';
		if(settings.today)
		{
			date_str += '<b>Today</b>, ';
		}
		if(settings.day_of_week)
		{
			switch(date.getDay()) {
				case 0 : date_str += 'Monday, ';    break;
				case 1 : date_str += 'Tuesday, ';   break;
				case 2 : date_str += 'Wednesday, '; break;
				case 3 : date_str += 'Thursday, ';  break;
				case 4 : date_str += 'Friday, ';    break;
				case 5 : date_str += 'Saturday, ';  break;
				case 6 : date_str += 'Sunday, ';    break;
			}
		}
		date_str += mon+' '+date.getUTCDate();
		switch(date.getUTCDate()) {
			case 1  :
			case 21 :
			case 31 : date_str += 'st'; break;
			case 2  :
			case 22 : date_str += 'nd'; break;
			case 3  :
			case 23 : date_str += 'rd'; break;
			default : date_str += 'th'; break;
		}
		if(!settings.no_year)
		{
			date_str += ', '+date.getUTCFullYear();
		}
		return date_str;
	};

	$.ubbc_filter = function(str, settings) {
		var defaults = {
			tag_list   : [],
			wrap_chars : 80
		};
		settings = $.extend(defaults, settings);

		// get ubbc tag array
		var tags = $.get_tag_array(str, settings.tag_list);
		// if false, then no ubbc tags exist
		if(tags === false)
		{
			return $.html_wrap($.nohtml(str), settings.wrap_chars);
		}

		var new_str = '';
		var open_tags = [];
		var prev_tag = null;
		var tags_length = tags.length;
		for(var i=0;i<tags_length;i++)
		{
			var tag = tags[i];
			if(typeof tag['name'] == 'string')
			{
				if(tag['type'] == 'close')
				{
					// if this is a close tag without an open tag, text it
					if(prev_tag == null || prev_tag['name'] != tag['name'])
					{
						new_str += '[/'+tag['close']+']';
					}
					else
					{
						new_str += '</'+tag['close']+'>';
						open_tags.pop();
						if(open_tags.length > 0)
						{
							prev_tag = open_tags[(open_tags.length-1)];
						}
						else
						{
							prev_tag = null;
						}
					}
				}
				else
				{
					// make sure this tag is allowed in the parent
					var allowed = true;
					if(prev_tag != null)
					{
						var allow = prev_tag['allow'];
						if(allow.match(/^all\-/))
						{
							allow = allow.replace(/^all\-/,'');
							not_allowed_list = allow.split(',');
							if($.in_array(tag['name'], not_allowed_list))
							{
								allowed = false;
							}
						}
						else
						{
							if(allow != 'all')
							{
								allowed_list = allow.split(',');
								if(!$.in_array(tag['name'], allowed_list))
								{
									allowed = false;
								}
							}
						}
					}
					
					// make sure this tag's parent is the proper parent
					if(tag['parent'].length > 0 && !$.in_array(prev_tag['name'], tag['parent']))
					{
						allowed = false;
					}

					// if the tag isn't allowed in the parent, text it
					if(allowed)
					{
						// add word break so html entities can be broken if chained
						new_str += '<wbr><'+tag['open'];
						if(tag['attr'].length > 0)
						{
							new_str += ' '+tag['attr'].join(' ');
						}
						new_str += '>';
						// if this tag can be closed
						if(tag['close'] != '')
						{
							open_tags.push(tag);
							prev_tag = tag;
						}
					}
					else
					{
						new_str += $.nohtml(tag['orig']);
					}
				}
			}
			else
			{
				new_str += $.html_wrap($.nohtml(tag['text']), settings.wrap_chars);
			}
		}
		
		// close any tags left open
		open_tags.reverse().map(function(tag) {
			new_str += '</'+tag['close']+'>';
		});
	
		return new_str;
	};
	
	$.get_tag_array = function(str, tag_list) {
		tag_list = $.extend([], tag_list);
		var tags       = [];
		var last_pos   = str.length;
		var cur_pos    = 0;
		var open_pos   = 0;
		var open2_pos  = 0;
		var close_pos  = 0;
		var ubbc_found = 0;
		
		while(cur_pos < last_pos)
		{
			tag = {};
	
			// find the first open bracket
			open_pos = str.indexOf('[', cur_pos);
			// if there are no open brackets, then we are done
			if(open_pos == -1)
			{
				tag['text'] = str.substr(cur_pos, last_pos-cur_pos+1);
				cur_pos = last_pos;
			}
			else
			{
				// if the open bracket isn't first, text everything before it
				if(cur_pos != open_pos)
				{
					// don't eat the bracket
					tag['text'] = str.substr(cur_pos, open_pos-cur_pos);
					cur_pos = open_pos;
				}
				else
				{
					// find the second open bracket
					open2_pos = str.indexOf('[', open_pos+1);
					// if no open is found, set open2_pos to the last_pos
					if(open2_pos == -1)
					{
						open2_pos = last_pos;
					}
					
					// find close position
					close_pos = str.indexOf(']', cur_pos);
					// if there are no close brackets, then we are done
					if(close_pos == -1)
					{
						tag['text'] = str.substr(cur_pos, last_pos-cur_pos+1);
						cur_pos = last_pos;
					}
					else
					{
						// if the second open bracket is before the close bracket, text everything up to the second open bracket
						if(open2_pos < close_pos)
						{
							tag['text'] = str.substr(cur_pos, open2_pos-cur_pos);
							cur_pos = open2_pos;
						}
						// get tag contents
						else
						{
							tag_str = str.substr(cur_pos, close_pos-open_pos+1);
							tag = $.build_tag(tag_str);
							if(tag === false)
							{
								tag['text'] = tag_str;
							}
							else
							{
								ubbc_found++;
							}
							cur_pos = close_pos+1;
						}
					}
				}
			}
			tags.push(tag);
		}
	
		if(ubbc_found == 0)
		{
			return false;
		}
		
		return tags;
	};
	
	$.build_tag = function(str, tag_list) {
		tag_list = $.extend([], tag_list);

		// get rid of beginning and ending brackets
		// separate tag name and its attributes
		attr_arr = str.replace(/^\[(.*)\]/, "$1").split('=');
		tag_name = attr_arr[0];

		// determine tag type
		if(tag_name.match(/^\//))
		{
			tag_name = tag_name.replace(/^\//, '');
			tag_type = 'close';
		}
		else
		{
			tag_type = 'open';
		}

		// if the tag is not in the list, return false
		if(tag_list.length > 0 && !$.in_array(tag_name, tag_list))
		{
			return false;
		}
		
		// if the tag doesn't exist in the filters, return false
		if(!$.array_key_exists(tag_name, filters))
		{
			return false;
		}
		
		// copy filter
		var filter = {};
		for(key in filters[tag_name])
		{
			filter[key] = filters[tag_name][key];
		}
		filter['orig'] = str;
		filter['name'] = tag_name;
		filter['type'] = tag_type;

		// update attributes
		s = attr_arr.length;
		i = 0;
		attr = [];
		if(s > 1)
		{
			while(i < s)
			{
				key = attr_arr[i].trim();
				val = (attr_arr[i+1]);
				if($.array_key_exists(key, filter['attr']))
				{
					attr[key] = $.sprintf(filter['attr'][key], $.nohtml(val));
				}
				i+=2;
			}
		}

		return filter;
	};
	
	/* validation functions */
	$.is_valid_email = function(value) {
		if(typeof value == 'undefined' || value == null || value == '')
		{
			return true;
		}

		var filter = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;
		if(filter.test(value))
		{
			return true;
		}
		return false
	};
	
	$.is_valid_emails = function(value) {
		if(typeof value == 'undefined' || value == null)
		{
			return true;
		}

		var emails = value.split(',');
		var filter = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;
		for(i in emails)
		{
			var email = emails[i].trim();
			if(!filter.test(email))
			{
				return false;
			}
		}
		return true;
	};
})(jQuery);