	function poundFirst(strId) { return ((strId && !/^#/.test(strId)) ? "#" : "") + strId; }
	function stripFirstPound(strId) { return (strId || "").replace(/^#/, ""); }
	function slashLast(strUrl) { return strUrl + ((/\/$/.test(strUrl)) ? "" : "/"); }

	function testNullString(value, returnIfNull)
	{
		value = String(((value || value == 0) ? value : ""));
		returnIfNull = ((typeof(returnIfNull) != "undefined") ? returnIfNull : null);
		return ((value && value != "undefined") ? value : returnIfNull);
	}

	/**
	 * parseKeywords() takes in a 'parse' string and a kwReplacements object.
	 * All occurrences of {*keyword_key*} in the parse string are replaced by
	 * the corresponding keyword replacement attribute in kwReplacements.
	 *
	 * e.g.
	 * parseKeywords("hello {*username*}, it's been {*num_days*} days since you last logged in.", {username: "Preston", num_days: 3}) -->
	 * --> "hello Preston, it's been 3 days since you last logged in."
	 * */
	function parseKeywords(strParse, kwReplacements, replaceEmpty)
	{
		strParse = strParse || "";
		kwReplacements = kwReplacements || {};
		replaceEmpty = replaceEmpty !== false;

		for (var key in kwReplacements)
		{
			var rx = new RegExp("{\\s?\\*\\s?"+ key +"\\s?\\*\\s?}", "ig");
			strParse = strParse.replace(rx, kwReplacements[key]);
		}
		if (replaceEmpty && strParse.indexOf("{*") > -1)
			strParse = strParse.replace(/\{\*[\w\s\d]+\*\}/ig, ""); // Shouldn't really get here, but if any kw replacements haven't been found, remove them...
		
		return strParse;
	}

	/**
	 * gettext is a simple function for translations.
	 **/
	function gettext(key, kwReplacements) { return ((kwReplacements) ? parseKeywords(trans, kwReplacements) : trans); }
	function _(key, kwReplacements){ return gettext(key, kwReplacements); }

	function capFirst(value, capEveryWord)
	{
		var arrCap = ((capEveryWord) ? value.split(/\s/g) : [value]);
		for (var i = 0; i < arrCap.length; i ++)
			arrCap[i] = arrCap[i].substring(0, 1).toUpperCase() + arrCap[i].substring(1);
		return arrCap.join(" ");
	}

	function randStr(length, kwargs)
	{
		kwargs = kwargs || {};
		var isIncludeCaps = kwargs.isIncludeCaps || true;
		var isIncludeNumbers = kwargs.isIncludeNumbers || true;
		var isIncludeSpecial = kwargs.isIncludeSpecial || false;

		var alphabet = "abcdefghijklmnopqrstuvwxyz" +
			((isIncludeCaps) ? "ABCDEFGHIJKLMNOPQRSTUVWXYZ" : "") +
			((isIncludeNumbers) ? "0123456789" : "") +
			((isIncludeSpecial) ? "`~!@#$%^&*()-_=+" : "");
		alphabet = alphabet.split("");
		
		var randomized = [];
		for (var i = 0; i < length; i ++)
			randomized.push(alphabet[Math.floor(Math.random() * alphabet.length)]);
		return randomized.join("");
	}

	function softTruncate(input, maxLength)
	{
		var output = input || "";
		maxLength = maxLength || 200;

		if (output.length > maxLength)
		{
			output = input.substring(0, maxLength);
			var tmp = input.substring(maxLength);
			var is = tmp.search(/\s/);
			if (is > -1)
				output += tmp.substring(0, is).replace(/\W+$/, "") + "&#133;";
		}

		return output;
	}

	function hardTruncate(input, maxLength)
	{
		maxLength = maxLength || 200;
		return ((input.length > maxLength) ? input.substring(0, maxLength) : input);
	}

	function unpluralize(plural){ return plural.replace(/ies$/, "y").replace(/ses$/, "s__").replace(/s$/, "").replace(/__$/, ""); }

	function padString(value, length, padChar, padBehind)
	{
		value = String(value);
		padChar = padChar || '0';

		if (value.length >= length)
			return value;

		var padding = "";
		var numPadChars = length - value.length;
		for (var i = 0; i < numPadChars; i ++)
			padding += padChar;

		return ((padBehind) ? (value + padding) : (padding + value));
	}

	function parseKeywords(sIn, kwReplacements, removeExtras)
	{
		var removeExtras = removeExtras !== false; // default to removing non-matched items

		for (var key in kwReplacements)
		{
			var rx = new RegExp("{\\s?\\*\\s?"+ key +"\\s?\\*\\s?}", "ig");
			sIn = sIn.replace(rx, kwReplacements[key]);
		}

		if (removeExtras)
			if (sIn.indexOf("{*") > -1)
				sIn = sIn.replace(/\{\s?\*\s?[\w\s_]+\s?\*\s?\}/ig, "");

		return sIn;
	}

	function formatNumber(value, kwargs)
	{
		value = value || 0;
		kwargs = kwargs || {};
		var decimalPlaces = ((typeof(kwargs.decimalPlaces) != "undefined") ? kwargs.decimalPlaces : 4);
		var addCommas = kwargs.addCommas || false;
		var zeroString = "00000000000000000000000000000000000000000000000000";
		
		if (decimalPlaces == 0)
			value = (Math.round(Number(value)));

		value = String(value);
		
		var intPart = value; // default the values -- will be changed if 'value' has decimals
		var decPart = ((decimalPlaces == 0) ? "" : "."+ zeroString.substring(0, decimalPlaces));

		if (/\./.test(value))
		{
			intPart = value.replace(/\.\d+$/, "")
			decPart = "."+ (value.replace(/^.*\./, "") + zeroString).substring(0, decimalPlaces);
		}
		return ((addCommas) ? intPart.split("").reverse().join("").replace(/(\d{3})/g, "$1,").split("").reverse().join("").replace(/^,/, "") : intPart) + decPart;
	}
	function formatMoney(value){ return formatNumber(value, {'decimalPlaces': 2, 'addCommas': true}); }


