/* jslint verified 2009.06.29 */
/*jslint browser: true, onevar: true, undef: true, white: false, eqeqeq: true, immed: true, cap: false  */
/*globals $, jQuery, _normalizeArguments, window, Exception, Element, Form, Input, Table, ActiveXObject */

var MMM = {};
window.MMM = MMM;

/*jslint eqeqeq: false */
MMM.ie = "\v"=="v"; // http://ajaxian.com/archives/ievv
/*jslint eqeqeq: true */
MMM.ie6 = MMM.ie && navigator.userAgent.toLowerCase().indexOf("msie 6") >= 0;
/*jslint immed: false, eqeqeq: false */
MMM.B = (function x(){})[-5]=='x'?'FF3':(function x(){})[-6]=='x'?'FF2':/a/[-1]=='a'?'FF':'\v'=='v'?'IE':/a/.__proto__=='//'?'Saf':/s/.test(/a/.toString)?'Chr':/^function \(/.test([].sort)?'Op':'Unknown'; // http://www.thespanner.co.uk/2009/01/29/detecting-browsers-javascript-hacks/
/*jslint immed: true, eqeqeq: true */
MMM.OS = {
	iPhone: false,
	win: navigator.userAgent.indexOf('Win') >= 0,
	mac: navigator.userAgent.indexOf('Mac') >= 0
};

$(document).ready(function () {
	var i, img,
		appMenuExpanded = false,
		toggleMenu = function () {
			appMenuExpanded = !appMenuExpanded;
			$("#appsList").toggle();
			if (appMenuExpanded) {
				$("#appsMenu").addClass('expanded');
				if (MMM.ie) { $("#appsMenu").addClass('mmm-button-hover-expanded'); }
				window.setTimeout(function () { $(document.body).click(toggleMenu); }, 100);
			} else {
				$("#appsMenu").removeClass('expanded').removeClass('mmm-button-hover-expanded');
				$(document.body).unbind('click', toggleMenu);
			}
		};

	$("#appsMenu").click(toggleMenu);
	if (MMM.ie) {
		$('#appsMenu')
			.mouseover(function (e) {
				$(this).addClass('mmm-button-hover');
				if ($(this).hasClass('expanded')) { $(this).addClass('mmm-button-hover-expanded'); }
			})
			.mouseout(function (e) { $(this).removeClass('mmm-button-hover').removeClass('mmm-button-hover-expanded'); });
	}

	$('a.app_create').click(function (e) {
		if (!$("#appsList").is(":visible")) { toggleMenu(); }
		var link = this,
			check = function (val) {
				var yes = false;
				if (val.length > 0 && val.length <= 13) {
					$('#new_app_error').removeClass('error');
					yes = true;
				} else {
					$('#new_app_error').addClass('error');
					$('#new_app_name').select();
				}
				return yes;
			},
			div = $('<div class="modal-msg"><br /><br /><div id="new_app_error">Names must be 13 characters or less.</div></div>'),
			submit = function (val) {
				$(div).dialog('close');
				window.location.href = $(link).attr('href') + '?n=' + encodeURIComponent(val);
			},
			input = $('<input maxlength="13" id="new_app_name" />')
				.keydown(function (e) {
					var val = $('#new_app_name').val();
					if (MMM.keys.enter(e) && check(val)) { submit(val); }
				})
				.change(function (e) { check($(this).val()); });
		div.prepend(input)
			.dialog({
				bgiframe: true,
				modal: true,
				draggable: false,
				resizable: false,
				title: 'Name your app!',
				close: toggleMenu,
				buttons: {
					OK: function (e) {
						var val = $('#new_app_name').val();
						if (check(val)) { submit(val); }
					},
					Cancel: function (e) { $(div).dialog('close'); }
				}
			});
		return false;
	});
	
	$("#closeUrlMessage").click(function() { $("#urlMessage").hide(); });
});

function noop() {}

// usage: isset(typeof(variable)) returns true if variable is defined.
function isset(ref) { return ref.toString ? ref.toString().toLowerCase() !== "undefined" : false; }

// Object.extend alias
function $$(el, forceID) {
	if (el && el !== document && el.nodeType !== 3 && typeof(el)!=="string") {
		Object.extend(el, Element);
		if (!!forceID && (!isset(typeof(el.id)) || el.id === '')) {
			el.forceID();
		}
		if (el.isNode('OPTION') || (el.isNode('INPUT') && ['RADIO','CHECKBOX'].in_array(el.type.toUpperCase()))) {
			Object.extend(el, Input);
		} else if (el.isNode('TABLE') || el.isNode('TBODY')) {
			Object.extend(el, Table);
		} else if (el.isNode('FORM')) {
			Object.extend(el, Form);
		}
		if (el.hasClassName('hide')) {
			el.hide().removeClassName('hide');
		}
	}
	return el;
}

/// Call Object.extend(obj,obj2) to add obj2's functionality to obj
Object.extend = function (d, s) {
	if (typeof(s)==='function' && s.prototype) {
		return Object.extend(d, s.prototype);
	} else if (d) {
		var p, p2;
		for (p in s) {
			if (p.toString() !== 'prototype') {
				if (d.prototype && typeof(d.prototype)==='object') { d.prototype[p] = s[p]; }
				else if (!d.nodeType || d.nodeType !== 3) { d[p] = s[p]; } // textNode checking added for IE 6
			}
			/*jslint forin: true */
			else { for (p2 in p) { d.prototype[p2] = s[p][p2]; } }
			/*jslint forin: false */
		}
	}
	return d;
};
// TODO: add argument handling for functions
Function.prototype.bind = function (who) {
	var func = this, args = [], i;
	for (i=1; i<arguments.length; ++i) { args.push(arguments[i]); }
	return function () { return func.apply(who,args); };
};
// Function.curry allows you to pre-prepare some of a function's arguments
// crockford, pg 44, js: the good parts
Function.prototype.curry = function () {
	var slice = Array.prototype.slice,
		args = slice.apply(arguments),
		that = this;
	return function () { return that.apply(null, args.concat(slice.apply(arguments))); };
};
// from http://www.crockford.com/javascript/inheritance.html
Function.prototype.inherits = function (Parent) {
	var d = {}, p = (this.prototype = new Parent());
	this.prototype.uber = function uber(name) {
		if (!(name in d)) { d[name] = 0; }
		var f, r, t = d[name], v = Parent.prototype;
		if (t) {
			while (t) {
				v = v.constructor.prototype;
				t -= 1;
			}
			f = v[name];
		} else {
			f = p[name];
			if (f === this[name]) {
				f = v[name];
			}
		}
		d[name] += 1;
		r = f.apply(this, Array.prototype.slice.apply(arguments, [1]));
		d[name] -= 1;
		return r;
	};
	return this;
};

if (!Object.watch || !Object.unwatch) {
	//	Cross Browser Watch Function
	//	based on http://forwarddevelopment.blogspot.com/2006/12/wactch-one-of-many-missing-js-function.html
	MMM.IEwatch = function (obj, prop, func) {
		prop = String(prop);
		var _oldvalue = obj[prop], _interval,
			check = function () {
				if (isset(typeof(obj[prop])) && obj[prop] !== null && obj[prop] !== _oldvalue) {
					obj[prop] = _oldvalue = func(prop, _oldvalue, obj[prop]);
				}
			};
		if (typeof(func)==="function") { _interval = window.setInterval(check, 50); }
		return {
			stopwatch: function () { window.clearInterval(_interval); }
		};
	};
}
Object.xwatch = function (obj, prop, func) {
	if (!Object.watch || !Object.unwatch) {
		if (!isset(typeof(obj.watchpoint))) {
			obj.watchpoint = {};
			delete(obj.watchpoint.xwatch);
			delete(obj.watchpoint.xunwatch);
		}
		obj.watchpoint[prop] = new MMM.IEwatch(obj, prop, func);
	} else { obj.watch(prop, func); }
};
Object.xunwatch = function (obj, prop) {
	if (!Object.watch || !Object.unwatch) {
		if (obj.watchpoint[prop]) {
			obj.watchpoint[prop].stopwatch();
			delete(obj.watchpoint[prop]);
		}
	} else { obj.unwatch(prop); }
};

//================= array functions ==================\\
Object.extend(Array, {
	/// find the index of an element in an array. -1 is its not in the array
	indexOf: function (toFind) {
		var retval = false, i;
		for (i = 0; retval === false && i < this.length; ++i) { if (this[i] === toFind) { retval = i; } }
		return retval === false ? -1 : retval;
	},
	in_array: function (obj) {
		obj = String(obj).replace('(', '\\(', 'g').replace(')', '\\)', 'g');
		return new RegExp('(^|,)'+obj+'(,|$)', 'gi').test(this);
	},
	// Array.reduce calls function f on each element of the array, starting at value
	reduce: function (f, value) {
		var i;
		for (i = 0; i < this.length; ++i) { value = f(this[i], value); }
	}
});
if (!Array.prototype.filter) {
	// from https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference:Objects:Array:filter
	Array.prototype.filter = function(fun /*, thisp*/) {
		var len = this.length,
			res = [],
			thisp = arguments[1],
			i, val;
		if (typeof(fun) !== "function") { throw new TypeError(); }
		for (i = 0; i < len; i++) {
			if (i in this) {
				val = this[i]; // in case fun mutates this
				if (fun.call(thisp, val, i, this)) { res.push(val); }
			}
		}
		return res;
	};
	Array.filter = function (arr) { return Array.prototype.filter.apply(arr, arguments); };
}

//	Can be called on any value to determine if it is an array (note: Function.arguments is not an array)
// doug from crockford, pg 61, js: the good parts.
// kangax from http://thinkweb2.com/projects/prototype/instanceof-considered-harmful-or-how-to-write-a-robust-isarray/
Array.is_a = function (val) {
		var doug = isset(typeof(val)) && !!val &&
				typeof(val) === "object" &&
				typeof(val.length) === "number" &&
				typeof(val.splice) === "function" &&
				!val.propertyIsEnumerable('length'),
			kangax = Object.prototype.toString.call(val) === "[object Array]"; // Object.prototype.toString.call([]);
		if (doug !== kangax) { throw new Exception("Array.is_a: detection method results differ"); }
		return doug && kangax;
	};
// Will produce an array dimmed at dimension containing initial, or the output of initial(i)
// crockford, pg. 63, js: the good parts.
Array.dim = function (dimension, initial) {
		var a = [], i, isfunc = typeof(initial) === "function";
		for (i = 0; i < dimension; ++i) { a[i] = isfunc ? initial(i) : initial; }
		return a;
	};
// will produce a 2-dimensional array with m rows and n columns, with initial value "initial" or initial(row, col)
Array.matrix = function (m, n, initial) {
		var i, mat = [], isfunc = typeof(initial) === "function";
		for (i = 0; i < m; ++i) { mat[i] = Array.dim(n, isfunc ? initial.curry(i) : initial); }
		return mat;
	};
// will produce an identity matrix with n rows and columns
Array.identity = function (n) {
		var func = function (max, row, col) { return row === col ? max : 0; };
		return Array.matrix(n, n, func.curry(n));
	};

//================= string functions =================\\
Object.extend(String,{
	/// replace HTML characters with their equivalent (e.g. for use in a textbox)
	editFormat: function () {
		var txt = this;
		return txt.replace(/&#39;/g, "'").replace(/&quot;/g, '"').replace(/<br \/>/g, "\n");
	},
	/// trim off whitespace as well as special HTML characters (&XXX;)
	trim: function () {
		var str = this, retval;
		if (str === "") { retval = ""; }
		else { retval = str.replace(/^((&[a-zA-Z]*;)|\s)*/g,"").replace(/(((&[a-zA-Z]*;)|\s)*)$/g,""); }
		return retval;
	},
	// fast pure trim function
	trim2: function () {
		var str = this.replace(/^\s+/, ''), i;
		for (i = str.length - 1; i >= 0; --i) {
			if (/\S/.test(str.charAt(i))) {
				str = str.substring(0, i + 1);
				break;
			}
		}
		return str;
	},
	/// change a camelcase string to hyphenated (backgroundColor -> background-color)
	toHyphenated: function () {
		var str = this, tmp;
		tmp = str.substr(1).substr(0,str.length-2);
		tmp = tmp.replace(/([A-Z])/g, '-$1');
		return (str.substr(0,1) + tmp + str.substr(-1)).toLowerCase();
	},

	//+ Jonas Raoni Soares Silva
	//@ http://jsfromhell.com/string/wordwrap [v1.1]
	wordWrap: function (m, b, c){
    	var i, j, l, s, r;
		if (m < 1) { return this; }
		else {
			for (i = -1, l = (r = this.split("\n")).length; ++i < l; r[i] += s) {
/*jslint eqeqeq: false, laxbreak: true */
				for (s = r[i], r[i] = ""; s.length > m; r[i] += s.slice(0, j) + ((s = s.slice(j)).length ? b : "")) {
					j = c == 2 || (j = s.slice(0, m + 1).match(/\S*(\s)?$/))[1] ? m : j.input.length - j[0].length
					|| c == 1 && m || j.input.length + (j = s.slice(m).match(/^\S*/)).input.length;
				}
/*jslint eqeqeq: true, laxbreak: false */
			}
		}
    	return r.join("\n");
	},
	find: function (what) { return this.indexOf(what)>=0 ? true : false; }, // from niftyCube
	reverse: function () { return this.split("").reverse().join(""); },
	repeat: function (num) {
		num = parseInt(num, 10);
		return !num ? '' : Array.dim(num, this).join('');
	},
	leftPad: function (l, c) {
		// from a comment on http://snipplr.com/view/8423/left-pad-string/
		return Array.dim(l - this.length, c || '0').join('') + this;
	},
	rightPad: function (l, c) {
		// based on leftPad
		return this + Array.dim(l - this.length, c || '0').join('');
	},
	count: function (match) {
		var res = this.match(match);
		return res === null ? 0 : res.length;
	}
});
String.prototype.substring = String.prototype.substr = String.prototype.slice; // replaces String.substring with the better String.slice. Implementation is identical.

/*********************************************************/
//================= number functions =================\\
// adds .intval() to any number.
Number.prototype.intval = function () { return this < 0 ? Math.ceil(this) : Math.floor(this); };
Number.prototype.addCommas = function () {
// http://www.mredkj.com/javascript/nfbasic.html
	var nStr = this + '',
		x = nStr.split('.'),
		x1 = x[0],
		x2 = x.length > 1 ? '.' + x[1] : '',
		rgx = /(\d+)(\d{3})/;
	while (rgx.test(x1)) {
		x1 = x1.replace(rgx, '$1' + ',' + '$2');
	}
	return x1 + x2;
};
Number.prototype.number_format = function ( decimals, dec_point, thousands_sep ) {
    // http://kevin.vanzonneveld.net
    // +   original by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)
    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +     bugfix by: Michael White (http://getsprink.com)
    // +     bugfix by: Benjamin Lupton
    // +     bugfix by: Allan Jensen (http://www.winternet.no)
    // +    revised by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)
    // +     bugfix by: Howard Yeend
    // + 	revised by: Jordan Harband (http://www.mixmatchmusic.com/)
    // *     example 1: (1234.5678).number_format(2, '.', '');
    // *     returns 1: 1234.57
	decimals = Math.abs(decimals);
	var n = this,
		c = isNaN(decimals) ? 2 : decimals,
		d = !isset(typeof(dec_point)) ? "." : dec_point,
		t = !isset(typeof(thousands_sep)) ? "," : thousands_sep,
		s = this < 0 ? "-" : "",
		i = parseInt((n = Math.abs(+n || 0).toFixed(c)), 10) + "", j = (j = i.length) > 3 ? j % 3 : 0;
    return s + (j ? i.substr(0, j) + t : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) + (c ? d + Math.abs(n - i).toFixed(c).slice(2) : "");
};
Number.prototype.seconds_to_minutes = function () {
	return Math.floor(this / 60) + ":" + String(this % 60).leftPad(2);
};

function cancelBubble(e) {
	// from http://www.quirksmode.org/js/events_order.html
	if (!e) { e = window.event; }
	if (e) {
		e.cancelBubble = true;
		if (e.stopPropagation) { e.stopPropagation(); }
	}
}

//	Can be called on any value to determine if it is an array (note: Function.arguments is not an array)
// doug from crockford, pg 61, js: the good parts.
// kangax from http://thinkweb2.com/projects/prototype/instanceof-considered-harmful-or-how-to-write-a-robust-isarray/
Array.is_a = function (val) {
	var doug = isset(typeof(val)) && !!val &&
			typeof(val) === "object" &&
			typeof(val.length) === "number" &&
			typeof(val.splice) === "function" &&
			!val.propertyIsEnumerable('length'),
		kangax = Object.prototype.toString.call(val) === "[object Array]"; // Object.prototype.toString.call([]);
	if (doug !== kangax) { throw new Exception("Array.is_a: detection method results differ"); }
	return doug && kangax;
};

MMM.keys = {};
MMM.keys.modifier = function (mask, e) {
	// http://24ways.org/2007/capturing-caps-lock
	// http://www.quirksmode.org/dom/w3c_events.html
	if (!e) { e = window.event; }
	if (!!e) {
		var targ = e.target ? e.target : e.srcElement, // get key pressed
			which = e.which || e.keyCode || -1, yes,
			masks = {
				'0001': 'altKey', 	'0010': 'ctrlKey',	'0100': 'shiftKey',
				'0002': 'altLeft',	'0020': 'ctrlLeft',	'0200': 'shiftLeft',
				'1000': 'metaKey'
			};
		// get modifier status
		if (isset(typeof(e[masks[mask]]))) { yes = !!e[masks[mask]]; }
		else if (e.modifiers) {
			mask = parseInt(mask, 2);
			yes = (e.modifiers & mask) > 0; // netscape 4
		}
	}
	return !!e && !!yes;
};
MMM.keys.shift 		= MMM.keys.modifier.curry('0100');	// on Mac, capslock down + shift = not detectable
MMM.keys.ctrl		= MMM.keys.modifier.curry('0010');	// on Mac, requires element.oncontextmenu to catch
MMM.keys.alt 		= MMM.keys.modifier.curry('0001');
MMM.keys.meta 		= MMM.keys.modifier.curry('1000');	// Mac FF + Safari only
MMM.keys.altLeft 	= MMM.keys.modifier.curry('0002');	// Windows IE only
MMM.keys.ctrlLeft 	= MMM.keys.modifier.curry('0020');	// Windows IE only
MMM.keys.shiftLeft 	= MMM.keys.modifier.curry('0200');	// Windows IE only
MMM.keys.enter = function (e) {
	if (!e) { e = window.event; }
	return (e.which || e.keyCode || -1) === 13;
};
MMM.keys.esc = function (e) {
	if (!e) { e = window.event; }
	return (e.which || e.keyCode || -1) === 27;
};
MMM.getMouseCoords = function (e) {
	// gets the mouse coordinates relative to the document
	// http://www.quirksmode.org/js/events_properties.html
	if (!e) { e = window.event; }
	var posX = 0, posY = 0;
	if (!!e) {
		if (e.pageX || e.pageY) {
			posX = e.pageX;
			posY = e.pageY;
		}
		else if (e.clientX || e.clientY) {
			posX = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
			posY = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
		}
	}
	return {x: parseInt(posX, 10), y: parseInt(posY, 10)};
};

MMM.callJSONP = function (src) {
	if (MMM.B !== "Saf" && MMM.B !== "Chr" && !MMM.ie) {
		var el = document.createElement('div');
		el.innerHTML = '<script type="text/javascript" src="'+src+'"></script>';
		document.body.appendChild(el);
		document.body.removeChild(el);
	} else {
		$('<div><script type="text/javascript" src="'+src+'"></script></div>').appendTo($(document.body)).remove();
	}
};

if (MMM.ie) {
	$(document).ready(function () {
		$('input.submit')
			.mouseover(function (e) { $(this).addClass('submitHover'); })
			.mouseout(function (e) { $(this).removeClass('submitHover'); });
	});
}

MMM.getAppID = function () {
	var idA = window.location.pathname.match(/^\/(customize|stats|preview|check|launch)\/([0-9]*)\/.*\/?$/);
	return idA === null ? null : parseInt(idA[2], 10);
};

MMM.errorCodes = {
	ERROR_CODE_REQUIRES_LOGON: 1,
	ERROR_CODE_NOT_YOUR_APP: 2,
	ERROR_CODE_NOT_YOUR_ACCT: 3,
	ERROR_CODE_BAD_PASSWORD: 4,
	ERROR_CODE_PASSWORD_SHORT: 5,
	ERROR_CODE_URL_INVALID: 6
};

/**
 * Wrapper around jQuery.getJSON that handles some of the standard error
 * codes that our AJAX calls can return.
 */
MMM.getJSON = function (url, data, callback) {
	var newCallback = function (data, textStatus) {
		var status, errorCode, msg,
			oldCallback = function () {
				if (typeof(callback) === "function") { callback(data, textStatus); }
			};
		if (textStatus === 'success') {
			if (data) {
				status = data.y;
				if (!status) {
					errorCode = data.ec;
					if (errorCode === MMM.errorCodes.ERROR_CODE_REQUIRES_LOGON) {
						msg = data.e + '<br />' + 'You will be redirected to the login page';
						MMM.error('Error', msg, MMM.goToLogin, true);
						MMM.goToLogin(3);
						return;
					} else if (errorCode === MMM.errorCodes.ERROR_CODE_NOT_YOUR_APP) {
						MMM.error('Error', data.e, oldCallback);
						return;
					} else if (errorCode === MMM.errorCodes.ERROR_CODE_NOT_YOUR_ACCT) {
						MMM.error('Error', data.e, oldCallback);
						return;
					} else if (errorCode === MMM.errorCodes.ERROR_CODE_BAD_PASSWORD) {
						MMM.error('Error', data.e, oldCallback);
						return;
					} else if (errorCode === MMM.errorCodes.ERROR_CODE_URL_INVALID) {
						MMM.error('Error', data.e, oldCallback);
						return;
					} else if (errorCode === MMM.errorCodes.ERROR_CODE_PASSWORD_SHORT) {
						MMM.error('Error', data.e, oldCallback);
						return;
					}
				}
			}
		} else {
			MMM.error('Error', 'An unknown error occurred. Sorry!<br />Please try again.', oldCallback(), true);
			return;
		}
		oldCallback();
	};

	jQuery.getJSON(url, data, newCallback);
};

MMM.goToLogin = function(delayInSeconds) {
	delayInSeconds = delayInSeconds || 0;
	var redirectToLogin = function () {
		window.location.href = '/login';
	};

	window.setTimeout(redirectToLogin, delayInSeconds * 1000);
};

MMM.prompt = function (title, msg, labels, inputs, callback) {
	msg = msg || '';
	var gatherInputs = function () {
			var i, vals = [];
			for (i = 0; i < inputs.length; ++i) {
				vals.push(inputs[i].val());
			}
			return vals;
		}, i,
		div = $('<div class="modal-msg"></div>')
		.html(msg)
		.dialog({
			bgiframe: true,
			modal: true,
			draggable: false,
			resizable: false,
			title: title
		}),
		closeFunc = function () {
			if (typeof(callback) === "function") { callback(gatherInputs()); }
			$(div).dialog('close');
		},
		closeOnEnter = function (e) { if (MMM.keys.enter(e)) { closeFunc(); } };
	div.dialog('option', 'buttons', { OK: closeFunc });
	for (i = 0; i < labels.length && i < inputs.length; ++i) {
		if ((inputs[i].attr('id') || '').length === 0) { inputs[i].attr('id', 'MMM_dialog_prompt_'+i); }
		div.append(
			$('<div class="ui-MMM-dialog-prompt"><label for="'+inputs[i].attr('id')+'">'+labels[i]+'</label></div>')
				.append(inputs[i])
		);
		if (i + 1 >= inputs.length || i + 1 >= labels.length) {
			inputs[i].keydown(closeOnEnter);
		}
	}
	if (inputs[0]) { inputs[0].focus(); }
};
MMM.error = function (title, msg, callback, noButtons) { MMM.dialog(title, msg, callback, noButtons, true); };
MMM.dialog = function (title, msg, callback, noButtons, isError) {
	msg = msg || '';
	if (isError === true) { msg = '<div class="error">'+msg+'</div>'; }
	var div = $('<div class="modal-msg"></div>')
		.html(msg)
		.dialog({
			bgiframe: true,
			modal: true,
			draggable: false,
			resizable: false,
			title: title,
			close: function () {
				if (typeof(callback) === "function") { callback(); }
			}
		});
	if (isError === true) { div.css('textAlign', 'center').dialog('option', 'width', '400px'); }
	if (!noButtons) {
		div.dialog('option', 'buttons', {
			OK: function () { $(this).dialog('close'); }
		});
	}
};

MMM.disableInput = function (input) {
	$(input).attr('disabled', 'disabled').addClass('disabled');
};

MMM.enableInput = function (input) {
	$(input).removeAttr('disabled').removeClass('disabled');
};

MMM.paintItBlack = function (pretty) {
	if (pretty === null) {
		$('#header img').attr('src', !$(document.body).hasClass('black') ? '/img/logo_black.png' : '/img/logo.png');
		$(document.body).toggleClass('black');
	} else if (pretty) {
		$('#header img').attr('src', '/img/logo_black.png');
		$(document.body).addClass('black');
	} else {
		$('#header img').attr('src', '/img/logo.png');
		$(document.body).removeClass('black');
	}
};
MMM.konami = {c: "38,38,40,40,37,39,37,39,66,65", k: [],
	f: function (e) {
		MMM.konami.k.push(e.keyCode);
		if (MMM.konami.k.length >= 11 && MMM.konami.k.toString().indexOf(MMM.konami.c) >= 0) {
			MMM.konami.k = [];
			if (window.confirm('Ready to leave the page?')) { window.location.href = 'http://konamicodesites.com/'; }
		} else if (MMM.konami.k.length > 11) {
			// MMM.konami.k.splice(-11);
		}
	}
};
$(document.body)
	.dblclick(function (e) { if (MMM.keys.shift(e) && MMM.keys.alt(e)) { MMM.paintItBlack(null); } } )
	.keydown(MMM.konami.f);

MMM.Hash = (function () {
	var self = {
			scroll: function (anchor) {
				var hash = window.location.hash;
				window.location.hash = '#' + anchor;
				window.location.hash = hash.length > 0 ? hash : '#';
			},
			clear: function () {
				if (window.location.hash.length > 0) { window.location.hash = "#"; }
			}
		};
	return self;
}());

// returns a GET variable. Null for no match.
// http://www.netlobo.com/url_query_string_javascript.html
function $_GET(name) {
	name = name.replace(/[\[]/,"\\[").replace(/[\]]/,"\\]");
	var regexS = "[\\?&]"+name+"=([^&#]*)",
		results = (new RegExp(regexS)).exec(window.location.search);
	if (results !== null) {
		results = +results[1]+"" === results[1] ? +results[1] :
			results[1] === "true" ? true :
			results[1] === "false" ? false :
			results[1];
	}
	return results;
}

MMM.cancelDrag = function (img) {
	/* begin thumbnail drag cancel */
	if (MMM.B.substr(0,2)==="FF") {
		img.mousedown(
			function (e) {
				if (!e) { e = window.event; }
				if (e.button === 1 && window.event !== null || e.button === 0) { e.preventDefault(); }
			}
		);
	} else if (MMM.ie) { img.get(0).ondragstart = function () { return false; }; }
	/* end thumbnail drag cancel */
	return img;
};
MMM.getFileInfo = function (fileEl) {
	var file = fileEl;
	if (!isset(typeof(file.files))) { file = {fileName: file.value.substr(file.value.lastIndexOf('\\')+1), fileSize: -1}; }
	else if (typeof(file.files.item) === "function") { file = file.files.item(0); }
	else { file = Array.is_a(file.files) ? file.files[0] : null; }
	return file;
};

//============= Cookie functions ==============\\
// adapted from quirksmode.org/js/cookies.html
MMM.Cookie = {
	// get set and check cookies
	createCookie: function (name,value,days,expires) {
		var date;
		if (!isset(typeof days) && !isset(typeof expires)) {
			expires = '';
		} else {
			if (!days) { days = expires / (24 * 60 * 60 * 1000); }
			if (days) {
				date = new Date();
				date.setTime(date.getTime()+(days*24*60*60*1000));
				expires = "; expires="+date.toGMTString();
			}
		}
		document.cookie = name+"="+value+expires+"; path=/";
	},
	readCookie: function (name) {
		var nameEQ = name + "=", i,
			ca = document.cookie.split(';'),
			data = null, c;
		for (i=0; i < ca.length; ++i) {
			c = ca[i];
			while (c.charAt(0)===' ') { c = c.substring(1); }
			if (c.indexOf(nameEQ) === 0) { data = c.substring(nameEQ.length); break; }
		}
		return data;
	},
	eraseCookie: function (name) { MMM.Cookie.createCookie(name, 0, -1); }
};

function utf8_encode( str_data ) {
	// http://kevin.vanzonneveld.net
	// +   original by: Webtoolkit.info (http://www.webtoolkit.info/)
	// * 	example 1: utf8_encode('Kevin van Zonneveld');
	// * 	returns 1: 'Kevin van Zonneveld'
	// adapted by jordan
	str_data = str_data.toString().replace(/\r\n/g,"\n");
	var utftext = "", n, c;
	for (n = 0; n < str_data.length; ++n) {
		c = str_data.charCodeAt(n);
		if (c < 128) { utftext += String.fromCharCode(c); }
		else if ((c > 127) && (c < 2048)) {
			utftext += String.fromCharCode((c >> 6) | 192);
			utftext += String.fromCharCode((c & 63) | 128);
		} else {
			utftext += String.fromCharCode((c >> 12) | 224);
			utftext += String.fromCharCode(((c >> 6) & 63) | 128);
			utftext += String.fromCharCode((c & 63) | 128);
		}
	}
	return utftext;
}
function utf8_decode( utftext ) {
// http://www.webtoolkit.info/javascript-url-decode-encode.html
// adapted by jordan
	var string = "", i, c, c1, c2, c3;
	i = c = c1 = c2 = c3 = 0;
	while ( i < utftext.length ) {
		c = utftext.charCodeAt(i);
		if (c < 128) {
			string += String.fromCharCode(c);
			++i;
		} else if ((c > 191) && (c < 224)) {
			c2 = utftext.charCodeAt(i+1);
			string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
			i += 2;
		} else {
			c2 = utftext.charCodeAt(i+1);
			c3 = utftext.charCodeAt(i+2);
			string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
			i += 3;
		}
	}
	return string;
}

function sha1 ( str ) {
	// http://kevin.vanzonneveld.net
	// +   original by: Webtoolkit.info (http://www.webtoolkit.info/)
	// + namespaced by: Michael White (http://crestidg.com)
	// -	depends on: utf8_encode
	// *	 example 1: sha1('Kevin van Zonneveld');
	// *	 returns 1: '54916d2e62f65b3afa6e192e6a601cdbe5cb5897'
	// adapted by Jordan
	str = utf8_encode(str);
	var rotate_left = function (n,s) { return ( n<<s ) | (n>>>(32-s)); },
		lsb_hex = function (val) {
			var lstr="", i, vh, vl;
			for (i=0; i<=6; i+=2) {
				vh = (val>>>(i*4+4))&0x0f;
				vl = (val>>>(i*4))&0x0f;
				lstr += vh.toString(16) + vl.toString(16);
			}
			return lstr;
		},
		cvt_hex = function (val) {
			var cstr="", i, v;
			for (i=7; i>=0; --i) {
				v = (val>>>(i*4))&0x0f;
				cstr += v.toString(16);
			}
			return cstr;
		},
		blockstart, i, j,
		W = [], H0 = 0x67452301,
		H1 = 0xEFCDAB89, H2 = 0x98BADCFE,
		H3 = 0x10325476, H4 = 0xC3D2E1F0,
		A, B, C, D, E, temp, str_len = str.length,
		word_array = [];

	for (i=0; i<str_len-3; i+=4) {
		j = str.charCodeAt(i)<<24 | str.charCodeAt(i+1)<<16 |
		str.charCodeAt(i+2)<<8 | str.charCodeAt(i+3);
		word_array.push( j );
	}
	switch( str_len % 4 ) {
		case 0: 	i = 0x080000000;
			break;
		case 1: 	i = str.charCodeAt(str_len-1)<<24 | 0x0800000;
			break;
		case 2:		i = str.charCodeAt(str_len-2)<<24 | str.charCodeAt(str_len-1)<<16 | 0x08000;
			break;
		case 3:		i = str.charCodeAt(str_len-3)<<24 | str.charCodeAt(str_len-2)<<16 | str.charCodeAt(str_len-1)<<8 | 0x80;
	}
	word_array.push( i );
	while ((word_array.length % 16) !== 14 ) { word_array.push( 0 ); }
	word_array.push( str_len>>>29 );
	word_array.push( (str_len<<3)&0x0ffffffff );

	for ( blockstart=0; blockstart<word_array.length; blockstart+=16 ) {
		for (i=0; i<16; ++i) { W[i] = word_array[blockstart+i]; }
		for (i=16; i<=79; ++i) { W[i] = rotate_left(W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16], 1); }

		A = H0; B = H1; C = H2; D = H3; E = H4;
		for (i= 0; i<=19; ++i) {
			temp = (rotate_left(A,5) + ((B&C) | (~B&D)) + E + W[i] + 0x5A827999) & 0x0ffffffff;
			E = D; D = C; C = rotate_left(B,30); B = A; A = temp;
		}
		for (i=20; i<=39; ++i) {
			temp = (rotate_left(A,5) + (B ^ C ^ D) + E + W[i] + 0x6ED9EBA1) & 0x0ffffffff;
			E = D; D = C; C = rotate_left(B,30); B = A; A = temp;
		}
		for (i=40; i<=59; ++i) {
			temp = (rotate_left(A,5) + ((B&C) | (B&D) | (C&D)) + E + W[i] + 0x8F1BBCDC) & 0x0ffffffff;
			E = D; D = C; C = rotate_left(B,30); B = A; A = temp;
		}
		for (i=60; i<=79; ++i) {
			temp = (rotate_left(A,5) + (B ^ C ^ D) + E + W[i] + 0xCA62C1D6) & 0x0ffffffff;
			E = D; D = C; C = rotate_left(B,30); B = A; A = temp;
		}
		H0 = (H0 + A) & 0x0ffffffff;
		H1 = (H1 + B) & 0x0ffffffff;
		H2 = (H2 + C) & 0x0ffffffff;
		H3 = (H3 + D) & 0x0ffffffff;
		H4 = (H4 + E) & 0x0ffffffff;
	}
	temp = cvt_hex(H0) + cvt_hex(H1) + cvt_hex(H2) + cvt_hex(H3) + cvt_hex(H4);
	return temp.toLowerCase();
}

