// --------------------------------------------------------------------
//
// ==UserScript==
// @name            TurkOp v0.4.1
// @description     "Post-Op Turkey" and other stories.
// @include         http://www.mturk.com/mturk/accept*
// @include         http://www.mturk.com/mturk/submit*
// @include         http://www.mturk.com/mturk/preview*
// @include         http://www.mturk.com/mturk/continue*
// @include         http://www.mturk.com:80/mturk/*
// ==/UserScript==


/*--------------------------------SETUP----------------------------------*/

// these enable/disable the available script modes
var MODEBLOCKVIEW 	= true;
var MODEARTISTNAME 	= true;


function init() {

	// General settings
	Settings.bAutoSkipAcceptError	= false;	// if true, will try to skip to next HIT if accept fails.
	Settings.bAutoReloadAcceptError	= true;		// if true, will refresh page if accept fails. Only set one of these to true.
	Settings.bAutoReloadSubmitError = true;		// if true, will refresh page if submit fails.
	Settings.bAutoAccept		= true;		// autoaccept HITs
	Settings.iHITCountAlert		= 0;		// alert me when I have submitted a total of [iHITCountAlert] HITs
	Settings.strFont		= "verdana";
	Settings.bFormGET		= true;		// change the submit form to "get" to stop the postdata alerts in FF

	// Settings for "A9 BlockView"
	Settings.iImageColumns		= 3;
	Settings.bAutoImageSize		= true;		// auto calc image sizes based on window width, will override values below
	Settings.iImageWidthSmall	= 0;		// manual image size
	Settings.iImageWidthBig		= 0;		
	Settings.iImageHeightSmall	= 0;		// manual image size, leave at 0 to autocalc based on fImageRatio
	Settings.iImageHeightBig	= 0;		
	Settings.fImageRatio		= (480 / 720)	// height / width ratio. Change if necessary in the FUTURE
	Settings.iZoomXOffset		= 4;		// offset position for zoom image
	Settings.iZoomYOffset		= 27;
	Settings.bAutoScroll		= true;		// auto scroll if selected image is outside the window
	Settings.iFontSize		= 19;		// size of name and address
	Settings.iHeaderHeight		= 27;		// height for name and address
	Settings.strBorderColor		= "blue";
	Settings.bCenterAddress		= true;	// center address+name 
	Settings.bAddressSingleLine	= true;		// address+name on single, set to false for 2 lines

	// Settings for "Confirm Artist Name"
	Settings.iArtistImageWidth	= 500;
	Settings.iArtistTitleSize	= 25;		// size of name and address
	Settings.iArtistTitleHeight	= 30;		// line height for name and address
	Settings.iArtistOffsetX		= 20;
	Settings.iArtistOffsetY		= 10;

	// Speech settings
	Settings.bSpeech		= true;	// enable Opera text-to-speech
	Settings.bSpeakOnLoad		= false;	// read name + address on page load (window must have focus for it to trigger).
	Settings.bSpeakOnFocus		= false;	// read name + address every time the page gets focus.
	Settings.strSpeechURL		= "http://www.nihilogic.dk/mturk/speech.xml";


	// key bindings

	// general keys
	bindKey (keyHelp		, "H");
	bindKey (keyStatus		, "F");
	bindKey (keySetup		, "N");
	bindKey (keyRefresh		, "R");
	bindKey (keyReturnHIT		, "P");
	bindKey (keyReturnToList	, "M");
	bindKey (keySwitchView		, "L");
	bindKey (keySpeakAddress	, "U");
	bindKey (keySpeakEarned		, "I");
	bindKey (keySpeakTotalHITs	, "Y");
	bindKey (keySpeakAvailHITs	, "O");
	bindKey (keySpeakReward		, "K");

	// BlockView keys
	//bindKey (keyToggleZoom		, "0");
	//bindKey (keySubmitSelectedImage	, "RETURN");
	bindKey (keyMoveImageLeft	, "LEFT");
	bindKey (keyMoveImageRight	, "RIGHT");
	bindKey (keyMoveImageDown	, "DOWN");
	bindKey (keyMoveImageUp		, "UP");
	bindKey (keyZoomMouseImage	, "0", "X");
	//bindKey (keySubmitMouseImage	, "2");

	// select individual images with number keys
	// bindKey (keySelectImage1	, "7");
	// bindKey (keySelectImage2	, "8");
	// bindKey (keySelectImage3	, "9");
	// bindKey (keySelectImage4	, "4");
	// bindKey (keySelectImage5	, "5");
	// bindKey (keySelectImage6	, "6");
	// bindKey (keySelectImage7	, "1");
	// bindKey (keySelectImage8	, "2");

	// select + submit individual images
	// bindKey (keySubmitImage1	, "7");
	// ....
	// bindKey (keySubmitImage8	, "2");



	// "Artist Name" keys
	bindKey (keyMoveArtistDown	, "DOWN", "TAB");
	bindKey (keyMoveArtistUp	, "UP", "8");
	bindKey (keySelectArtistInput	, "Q");
	bindKey (keySubmitArtist	, "RETURN");



	// keyDisable can be used to disable the default browser action for a key, ie. do nothing at all.
	// bindKey (keyDisable		, "1");
}


/*-----------------------------------------------------------------------*/


var dc = function(t, c) { var e = document.createElement(t); if (c) e.innerHTML = c; return e;};

var Settings = new Object();
var KeyMap = [];
var aImages = [];
var aRadios = [];
var oZoomImg = null;
var iZoomImgLeft = 0, iZoomImgTop = 0;
var iSelectedImg = 0;
var Keys = [];
var strReturnURL;
var oNewContent = null;
var oIFrame;
var oMouseImage = null;
var bKeyEnabled = false;
var iMode = 0;
var strHeader = "";
var aStatVals = [];
var oFocusElement;

var aArtistNames = new Array();
var aArtistOptions = new Array();
var oArtistInput = null;
var bArtistHasImage = false;
var iSelectedArtist = -1;

String.prototype.trim = function() {
	return this.replace(/^\s*|\s*$/g,"");
}

function getElementsByClass(node,searchClass,tag) {
	var classElements = new Array();
	var els = node.getElementsByTagName(tag);
	var elsLen = els.length;
	for (i = 0, j = 0; i < elsLen; i++) {
		if (els[i].className.trim() == searchClass) {
			classElements[j] = els[i];
			j++;
		}
	}
	return classElements;
}

function getSingleElementByClass(node,searchClass,tag) {
	var els = node.getElementsByTagName(tag);
	var elsLen = els.length;
	for (i = 0; i < elsLen; i++) {
		if (els[i].className.trim() == searchClass) {
			return els[i];
		}
	}
}

function checkError() {
	if (document.body) {
		var err = getElementsByClass(document.body, "error_title", "td");
		if (err.length > 0) {
			init();
			loadSettings();
			for (var e=0;e<err.length;e++) {
				var errtxt = err[e].childNodes[0];
				if (errtxt && errtxt.nodeValue.match("Your request was not completed successfully")) {
					if (window.location.href.match("/accept")) {
						if (Settings.bAutoSkipAcceptError) {
							gotoPage(window.location.href.replace("/accept", "/preview"));
						} else if (Settings.bAutoReloadAcceptError) {
							reloadPage();
						}
					} else if (window.location.href.match("/submit") && Settings.bAutoReloadSubmitError) {
						reloadPage();
					}
				}
			}
		}
	}
}	

function reloadPage() {
	window.location.reload();
	cleanUp();
}

function gotoPage(strURL) {
	cleanUp();
	window.location.href = strURL;
}


function saveSettings() {
	var aOldCookies = getOtherCookies();
	for (var s in Settings) {
		setCookie("turkop_" + s, escape(Settings[s]));
	}
	for (var i in aOldCookies) {
		setCookie(aOldCookies[i][0], aOldCookies[i][1], -1);
	}
}


function getCookie(strName) {
	strName += "=";
	var aCookies = document.cookie.split(";");
	for(var i in aCookies) {
		var strCookie = aCookies[i];
		while (strCookie.charAt(0) == " ") strCookie = strCookie.substring(1,strCookie.length);
			if (strCookie.indexOf(strName) == 0) return strCookie.substring(strName.length,strCookie.length);
	}
	return null;
}

function getOtherCookies() {
	var aRawCookies = document.cookie.split(";");
	var aCookies = new Array();
	for(var i in aRawCookies) {
		var strCookie = aRawCookies[i].trim();
		var aCookie = strCookie.split("=");
		if (aCookie[0].substring(0, 7) != "turkop_")
			aCookies.push([aCookie[0], aCookie[1]]);
	}
	return aCookies;
}

function setCookie(strName, strValue, iExpire) {
	if (!iExpire) iExpire = 365;
	var strExp = "";
	if (iExpire > 0) {
		var oDate = new Date();
		oDate.setTime(new Date().getTime()+(iExpire*24*60*60*1000));
		strExp = "; expires=" + oDate.toGMTString();
	}
	document.cookie = strName + "=" + strValue + strExp;
}

function loadSettings() {
	var aCookies = document.cookie.split(";");
	for (var c in aCookies) {
		aCookies[c] = aCookies[c].trim();
		if (aCookies[c].substring(0, 7) == "turkop_") {
			var aSetting = aCookies[c].substring(7).split("=");
			switch (typeof Settings[aSetting[0]]) {
				case "string" : 
					Settings[aSetting[0]] = unescape(aSetting[1]); break;
				case "number" :
					Settings[aSetting[0]] = aSetting[1].match(".") ? parseFloat(aSetting[1]) : parseInt(aSetting[1]); break;
				case "boolean" :
					Settings[aSetting[0]] = (aSetting[1] == "true");
			}
		}
	}
}


function load(e) {
	iMode = checkHITType();

	if (iMode == -1) return;

	switch (iMode) {
		case Modes.BLOCKVIEW :
			if (!MODEBLOCKVIEW) return;
			break;
		case Modes.ARTISTNAME :
			if (!MODEARTISTNAME) return;
			break;
	}

	init();
	loadSettings();

	if (checkAccept()) return;

	hideContent();

	if (Settings.bFormGET)
		document.forms[0].method = "get";

	oNewContent = document.body.appendChild(dc("span"));
	oNewContent.style.position = "absolute";
	oNewContent.style.top = 0;
	oNewContent.style.left = 0;
	oNewContent.style.width = "100%";
	oNewContent.style.height = "100%";

	generateHelp();
	generateStatus();
	generateSetup();

	var bLoad = false;
	switch (iMode) {
		case Modes.BLOCKVIEW :
			bLoad = loadBlockView();
			break;
		case Modes.ARTISTNAME :
			bLoad = loadArtistName();
			break;
	}
	if (!bLoad) return;


	registerEvent(document, 'keypress',
		function(e) {
			if (!bKeyEnabled) return;
			if (e.ctrlKey || e.altKey) return;
			var k = e.keyCode || e.which;
			if (KeyMap[k]) {
				KeyMap[k].fncAction();
				if (!KeyMap[k].bAllowDefault) e.preventDefault();
				e.stopPropagation();
			}
		}, false
	);
	scroll(0,0);

	if (Settings.bSpeech) {
		generateVoice();
		if (Settings.bSpeakOnLoad) setTimeout("saySomething(strHeader);", 100);
		if (Settings.bSpeakOnFocus) {
			registerEvent(window, 'focus',
				function() { saySomething(strHeader); }, false);
		}
	}

	if (Settings.iHITCountAlert != 0) {
		if (parseInt(aStatVals["HITs Submitted"],10) == Settings.iHITCountAlert) {
			alert("Congratulations!\r\nYou have submitted " + parseInt(aStatVals["HITs Submitted"],10) + " HITs!");
		}
	}
	bKeyEnabled = true;

	if (oFocusElement) oFocusElement.focus();
}

function loadBlockView() {
	checkImages();

	if (aImages.length == 0) return false;
	generateHeader();

	// auto image sizes
	if (!Settings.iImageWidthSmall) 
		Settings.iImageWidthSmall = Math.floor((document.body.offsetWidth) / Settings.iImageColumns) - (15 + (Settings.iImageColumns * 5));
	if (!Settings.iImageWidthBig) 
		Settings.iImageWidthBig = (document.body.offsetWidth - 20) - Settings.iZoomXOffset;

	if (!Settings.iImageHeightSmall)
		Settings.iImageHeightSmall = Math.floor(Settings.iImageWidthSmall * Settings.fImageRatio);
	if (!Settings.iImageHeightBig)
		Settings.iImageHeightBig = Math.floor(Settings.iImageWidthBig * Settings.fImageRatio);

	arrangeImages();
	selectImage(0);
	return true;
}


function loadArtistName() {
	buildArtistHeader();
	buildArtistImage();
	buildArtistNameList();
	selectArtistName(-1);
	oFocusElement = oArtistInput;
	return true;
}

function buildArtistHeader() {
	var cntHeader = dc("span");
	var aTitles = getElementsByClass(document.body, "question title", "p");
	if (aTitles.length > 0) {
		strHeader = getTextContent(aTitles[0]);
	}
	var oTxt = cntHeader.appendChild(dc("span", strHeader));
	oTxt.style.fontSize = Settings.iArtistTitleSize;
	oTxt.style.fontFamily = Settings.strFont;
	oTxt.style.fontWeight = "bolder";
	cntHeader.style.position = "absolute";
	cntHeader.style.top = Settings.iArtistOffsetY;
	cntHeader.style.left = Settings.iArtistOffsetX;
	oNewContent.appendChild(cntHeader);
}

function buildArtistImage() {
	var cntImage = dc("span");
	var aImages = getElementsByClass(document.body, "question binary image", "img");
	if (aImages.length > 0) {
		var oImg = cntImage.appendChild(dc("img"));
		oImg.src = aImages[0].src;
		oImg.style.width = Settings.iArtistImageWidth;
		cntImage.style.position = "absolute";
		cntImage.style.top = Settings.iArtistTitleHeight + Settings.iArtistOffsetY;
		cntImage.style.left = Settings.iArtistOffsetX;
		cntImage.style.border = "";
		cntImage.style.border = "1px solid black";
		oNewContent.appendChild(cntImage);
		bArtistHasImage = true
	}
}


function buildArtistNameList() {
	var oList = getSingleElementByClass(document.body, "question-list", "ul");
	if (oList) {
		var aItems = getElementsByClass(oList, "question-list-item", "li");
		for (var i in aItems) {
			aArtistNames[i] = getTextContent(aItems[i]);
		}
	}

	var aAddNames = new Array();
	for (var i in aArtistNames) {
		if (aArtistNames[i].match(",")) {
			aAddNames.push(
				aArtistNames[i].substring(aArtistNames[i].search(",")+1).trim()
				+ " " + aArtistNames[i].substring(0,aArtistNames[i].search(","))
			);
		}
	}
	for (var i in aAddNames) {
		var strName = truncWhite(aAddNames[i]);
		if (!searchArray(aArtistNames, strName))
			aArtistNames.push(strName);
	}
	Settings.iArtistNameHeight = 32;
	Settings.iArtistNameWidth = 400;
	Settings.strArtistFont = "verdana";
	Settings.iArtistFontSize = 21;

	var oOptions = oNewContent.appendChild(dc("span"));

	oArtistInput = oNewContent.appendChild(dc("input"));
	oArtistInput.setAttribute("autocomplete", "off"); // haha, Firefox blur() exception :downs:

	oArtistInput.style.fontFamily = Settings.strArtistFont;

	oArtistInput.style.fontSize = 15;
	oArtistInput.style.fontWeight = "bold";
	oArtistInput.style.border = "1px solid black";
	oArtistInput.style.width = Settings.iArtistNameWidth;
	oArtistInput.style.position = "absolute";
	oArtistInput.style.left = Settings.iArtistOffsetX + (bArtistHasImage ? (Settings.iArtistImageWidth + 15) : 0) ;
	oArtistInput.style.top = Settings.iArtistTitleHeight + Settings.iArtistOffsetY;


	registerEvent(oArtistInput, "keypress", 
		function(e) {
			if (iSelectedArtist != -1) return;
			if (e.ctrlKey || e.altKey) return;
			var k = e.keyCode || e.which;
			if (k == 9) {
				keyMoveArtistDown();
				e.preventDefault();
			}
			if (k == 13)
				keySubmitArtist();
			e.stopPropagation();
		}, false
	);
	registerEvent(oArtistInput, "mousedown",
		function(e) {
			selectArtistName(-1);
		}, false
	);

	for (var i in aArtistNames) {
		var oNameOption = oOptions.appendChild(dc("div", aArtistNames[i]));
		oNameOption.style.fontFamily = Settings.strArtistFont;
		oNameOption.style.fontSize = Settings.iArtistFontSize;
		oNameOption.style.fontWeight = "bold";
		oNameOption.style.whiteSpace = "nowrap";
		oNameOption.style.backgroundColor = ((i % 2) ? "#EEEEEE" : "white");
		oNameOption.style.top = i * Settings.iArtistNameHeight + 25;
		oNameOption.style.height = Settings.iArtistNameHeight;
		oNameOption.style.cursor = "default";
		oNameOption.style.left = 15;
		oNameOption.style.padding = "2px";
		oNameOption.style.border = "1px solid white";

		registerEvent(oNameOption, "click",
			function(e) {
				for (var i in aArtistOptions) {
					if (aArtistOptions[i] == this) {
						selectArtistName(i);
						keySubmitArtist();
						return;
					} 
				}
			}, false
		);

		aArtistOptions.push(oNameOption);
	}

	oOptions.style.position = "absolute";
	oOptions.style.top = Settings.iArtistTitleHeight + Settings.iArtistOffsetY + 30;
	oOptions.style.left = Settings.iArtistOffsetX + (bArtistHasImage ? (Settings.iArtistImageWidth + 15) : 0);
	oOptions.style.width = Settings.iArtistNameWidth;
}



function htmlDecode(strHTML) {
	return dc("span", strHTML).childNodes[0].nodeValue;
}

var Modes = {
	UNKNOWN 	: -1,
	BLOCKVIEW 	: 1,
	ARTISTNAME 	: 2
}

function checkHITType() {
	var aType = getElementsByClass(document.body, "overview title", "p");
	if (aType.length > 0) {
		if (getTextContent(aType[0]).match("Music - Confirm Artist Name")) 
			return Modes.ARTISTNAME;
	}
	aType = getElementsByClass(document.body, "capsulelink_bold", "td");
	for (var a in aType) {
		if (getTextContent(aType[0]).match("A9 BlockView"))
			return Modes.BLOCKVIEW;
	}
	return Modes.UNKNOWN;
}

function getTextContent(oElement) {
	return oElement.textContent || oElement.innerText;
}

function keyTest() {
}

function generateVoice() {
	oIFrame = dc("iframe")
	oIFrame.id = "ifrm";
	oIFrame.name = "ifrm";
	document.body.appendChild(oIFrame);
	oIFrame.style.display = "none";
}

function saySomething(strWhat) {
	if (!(oIFrame && Settings.bSpeech)) return;
	oIFrame.src = Settings.strSpeechURL + "?" + formatSpeechString(strWhat);
}

function formatSpeechString(strWhat) {
	strWhat = strWhat.replace(/\r/g, "");
	strWhat = strWhat.replace(/\n/g, "");
	strWhat = strWhat.replace(/\t/g, "");
	while (strWhat.search("  ") > -1) strWhat = strWhat.replace("  ", " ");
	return strWhat.trim();
}

function truncWhite(strWhat) {
	while (strWhat.search("  ") > -1) strWhat = strWhat.replace("  ", " ");
	return strWhat;
}

function keySpeakAddress() {
	saySomething(strHeader);
}
function keySpeakEarned() {
	saySomething("Total earned. " + aStatVals["Total earned"] 
		+ ((Math.random() < 0.2 && !aStatVals["Total earned"].match("Unavailable")) ? " ... Bling Bling" : "")
	);
}
function keySpeakAvailHITs() {
	saySomething("There are " + formatSpeechInt(aStatVals["HITs Available"]) + " HITs Available");
}
function keySpeakTotalHITs() {
	saySomething("You have Submitted " + formatSpeechInt(aStatVals["HITs Submitted"]) + " HITs");
}
function keySpeakReward() {
	saySomething("Reward is a whopping " + aStatVals["Reward"]);
}

function formatSpeechInt(strNumber) {
	var strFrm = "";
	strNumber = formatSpeechString(strNumber);
	if (strNumber == "Unavailable") return strNumber;
	while (strNumber.length > 0) {
		strFrm = strNumber.substring(strNumber.length-3) + (strFrm != "" ? "," : "") + strFrm;
		strNumber = strNumber.substring(0, strNumber.length-3);
	}
	return strFrm;
}

function keyCodeToString(iKey) {
	for (var k in Keys) {
		if (typeof Keys[k] == "object") {
			for (var kk in Keys[k]) {
				if (Keys[k][kk] == iKey) return k;
			}
		} else {
			if (Keys[k] == iKey) return k;
		}
	}
	return iKey;
}

function evImageClick(e) {
	if (e.which != 1) return;
	for (var i=0;i<aImages.length;i++) {
		if (aImages[i] == this) selectImage(i);
	}
	keySubmitMouseImage(this);
}

function evImageKeyPress(e) {
	if (!bKeyEnabled) return;
	var k = e.keyCode || e.which;
	if (KeyMap[k]) {
		if (e.ctrlKey || e.altKey) return;
		KeyMap[k].fncAction(this);
		if (!KeyMap[k].bAllowDefault) e.preventDefault();
		e.stopPropagation();
	}
}

function hideContent() {
	var mainContent = document.body.childNodes;
	for (var i=0;i<mainContent.length;i++) {
		if (mainContent[i].style) {
			mainContent[i].style.display = "none";
		}
	}
}

function generateHeader() {
	var cntHeader = dc("span");
	var queWrap = getElementsByClass(document.body, "question-content-wrapper", "div")[0];
	if (queWrap) {
		var queTitles = getElementsByClass(queWrap, "question title", "p");
		var strLoc = "";
		var strName = "";
		for (var i=0;i<queTitles.length;i++) {
			var qTitle = queTitles[i].childNodes[0];
			if (qTitle) {
			        var reName = new RegExp(" -- .*");
			        var reLoc = new RegExp("Location of business: ([^,]+),.*");
	
	        		if (qTitle.nodeValue.match(reLoc))
					strLoc = qTitle.nodeValue.replace(reLoc, "$1");
				if (qTitle.nodeValue.match(reName))
					strName = qTitle.nodeValue.replace(reName, "");
			}
		}

		strHeader = strName + (Settings.bAddressSingleLine ? " -- " : "<br>") + strLoc;
		var txt = dc("span", strHeader);
		cntHeader.appendChild(txt);
		txt.style.fontSize = Settings.iFontSize;
		txt.style.fontFamily = Settings.strFont;
		txt.style.fontWeight = "bolder";
	}
	cntHeader.style.position = "absolute";
	cntHeader.style.top = 2;
	cntHeader.style.left = 10;
	if (Settings.bCenterAddress) {
		cntHeader.style.width = document.body.offsetWidth - 20;
		cntHeader.style.textAlign = "center";
	}
	oNewContent.appendChild(cntHeader);
}

function checkImages() {
	var choices = getElementsByClass(document.body, "radiobutton-wrapper", "p");
	for (var i=0; i < choices.length; i++) {
		aRadios[i] = choices[i].getElementsByTagName('input')[0];
		var oImg = choices[i].getElementsByTagName('img')[0];
		if (oImg) {
			aImages[i] = dc("img");
			aImages[i].src = oImg.src;
		} else {
			aImages[i] = dc("img");
			aImages[i].src = "http://turking.com/downloads/nota.gif";
		}
	}
}

function arrangeImages() {
	var newContentImg = dc("span");
	var x = 8, y = Settings.iHeaderHeight;
	for (var i=0;i<aImages.length;i++) {
		var imgCnt = dc("span");
		imgCnt.style.position = "absolute";
		imgCnt.style.left = x;
		imgCnt.style.top = y;
		aImages[i].style.width = Settings.iImageWidthSmall;
		aImages[i].style.height = Settings.iImageHeightSmall;
		aImages[i].style.border = "2px solid white";

		registerEvent(aImages[i], "keypress", evImageKeyPress, false);
		registerEvent(aImages[i], "mousedown", evImageClick, false);
		registerEvent(aImages[i], "mouseover", 
			function() {
				oMouseImage = this;
			}, false);
		registerEvent(aImages[i], "mouseout", 
			function() {
				if (oMouseImage == this) oMouseImage = null;
			}, false);


		imgCnt.appendChild(aImages[i]);
		newContentImg.appendChild(imgCnt);
		x += Settings.iImageWidthSmall + 5;
    		if ((i+1) % Settings.iImageColumns == 0){
			x = 8;
			y += Math.floor(Settings.iImageWidthSmall * Settings.fImageRatio) + 5;
		}
	}
	oNewContent.appendChild(newContentImg);
}

function generateHelp() {
	var cntHelp = dc("span");
	cntHelp.id = "mthelp";
	cntHelp.style.position = "absolute";
	cntHelp.style.width = 400;
	cntHelp.style.backgroundColor = "#FFFFFF";
	cntHelp.style.border = "1px solid black";
	cntHelp.style.zIndex = 50;
	cntHelp.style.padding = 4;
	cntHelp.style.fontFamily = "verdana";
	cntHelp.style.display = "none";

	var strHTML = "<center><b>HELP</b><br>"
		+ "<table border=0>"
		+ "<tr><td width=100 style='font-size:9pt;'>Key</td><td width=250 style='font-size:9pt;'>Action</td></tr>";
	

	var i = 0;	
	var aKnownKeys = [];
	for (var k in KeyMap) {
		var strKey = keyCodeToString(k);
		if (!aKnownKeys[strKey]) {
			aKnownKeys[strKey] = true;
			i++;
			strHTML += "<tr "
				+ ((i % 2) ? "bgcolor='#EEEEEE'" : "") 
				+ "><td style='font-size:9pt;'>" + strKey + "</td><td style='font-size:9pt;'>" + functionName(KeyMap[k].fncAction) + "</td></tr>";
		}
	}
	strHTML += "</table></center>";
	cntHelp.innerHTML = strHTML;
	
	oNewContent.appendChild(cntHelp);
}


function generateSetup() {
	var cntSetup = dc("span");
	cntSetup.id = "mtsetup";
	cntSetup.style.position = "absolute";
	cntSetup.style.width = 450;
	cntSetup.style.backgroundColor = "#FFFFFF";
	cntSetup.style.border = "1px solid black";
	cntSetup.style.zIndex = 50;
	cntSetup.style.padding = 4;
	cntSetup.style.fontFamily = "verdana";
	cntSetup.style.display = "none";

	var strHTML = "<center><b>SETUP</b><br>"
		+ "<table border=0>";

	for (var s in Settings) {
		strHTML += "<tr "
			+ ((i % 2) ? "bgcolor='#EEEEEE'" : "") 
			+ "><td style='font-size:9pt;'>" + s + "</td><td style='font-size:9pt;'>";
		switch (typeof Settings[s]) {
			case "string" :
				strHTML += "<input id='setup_" + s + "'type='text' size='40' value='" + Settings[s] + "' style='border:1px solid black;' >";
				break;
			case "number" :
				strHTML += "<input id='setup_" + s + "' type='text' size='10' value='" + Settings[s] + "' style='border:1px solid black;' >";
				break;
			case "boolean" :
				strHTML += "<input id='setup_" + s + "' type='checkbox' " + (Settings[s] ? "checked" : "") + ">";
				break;
		} 
		strHTML += "</td></tr>";
	}
	strHTML += "<tr><td colspan=2 align=center><br>"
		+ "<input type='button' id='turkopSettingsLoadDefault' value='Load default' style='border:1px solid black'>&nbsp;&nbsp;"
		+ "<input type='button' id='turkopSettingsSave' value='Save settings' style='border:1px solid black'>&nbsp;&nbsp;"
		+ "<input type='button' id='turkopSettingsCancel' value='Cancel' style='border:1px solid black'><br><br>"

		+ "</td></tr>";
	strHTML += "</table></center>";
	cntSetup.innerHTML = strHTML;

	oNewContent.appendChild(cntSetup);

	registerEvent(document.getElementById("turkopSettingsLoadDefault"), "click", setupLoadDefault, false);
	registerEvent(document.getElementById("turkopSettingsSave"), "click", setupSaveSettings, false);
	registerEvent(document.getElementById("turkopSettingsCancel"), "click", keySetup, false);

}

function setupLoadDefault() {
	init();
	keySetup();
	keySetup();
}

function setupSaveSettings() {
	for (var s in Settings) {
		var oSet = document.getElementById("setup_" + s);
		if (oSet) {
			switch (typeof Settings[s]) {
				case "string" : 
					Settings[s] = oSet.value; break;
				case "number" : 
					Settings[s] = oSet.value.match(".") ? parseFloat(oSet.value) : parseInt(oSet.value); break;
				case "boolean" : 
					Settings[s] = oSet.checked; 
					break;
			}
		}
	}
	saveSettings();
	keySetup();
}

function generateStatus() {
	var cntStatus = dc("span");
	cntStatus.id = "mtstatus";
	cntStatus.style.position = "absolute";
	cntStatus.style.width = 400;
	cntStatus.style.backgroundColor = "#FFFFFF";
	cntStatus.style.border = "1px solid black";
	cntStatus.style.zIndex = 60;
	cntStatus.style.padding = 4;
	cntStatus.style.fontFamily = "verdana";
	cntStatus.style.display = "none";

	var strHTML = "<center><b>STATUS</b><br>"
		+ "<table border=0>";

	var aVals = new Array();
	var aTitles;

	aTitles = getElementsByClass(document.body, "title_orange_text", "td");
	for (var a=0;a<aTitles.length;a++) {
		var strTitle = getTextContent(aTitles[a]);
		if (strTitle && aTitles[a+1]) {
			var strVal = getTextContent(aTitles[a+1]);
			strVal = strVal.replace(/\r|\n/g,"");
			strVal = strVal.trim();

			if (strTitle.match("Total Earned:"))
				aVals["Total earned"] = strVal;
			if (strTitle.match("Total HITs Submitted:")) {
				aVals["HITs Submitted"] = strVal;
			}
		}
	}
	aTitles = getElementsByClass(document.body, "capsule_field_title", "td");
	for (var a=0;a<aTitles.length;a++) {
		var strTitle = getTextContent(aTitles[a]);
		var aCells = aTitles[a].parentNode.getElementsByTagName("td");
		var strVal = aCells.length > 1 ? aCells[1].innerHTML : "";

		if (strTitle.match("HITs Available"))
			aVals["HITs Available"] = strVal;

		if (strTitle.match("Duration:"))
			aVals["Duration"] = strVal;

		if (strTitle.match("Requester:"))
			aVals["Requester"] = strVal;

		if (strTitle.match("Qualifications:"))
			aVals["Qualifications"] = strVal;

		if (strTitle.match("Reward:"))
			aVals["Reward"] = strVal;

	}

	var i = 0;
	for (var a in aVals) {
		i++;
		strHTML += "<tr "
			+ ((i % 2) ? "bgcolor='#EEEEEE'" : "") 
			+ "><td style='font-size:9pt;' width=100>" + a + "</td><td style='font-size:9pt;' width=250>" + aVals[a] + "</td></tr>"
	}

	strHTML += "</table></center>";
	cntStatus.innerHTML = strHTML;
	aStatVals = aVals;
	oNewContent.appendChild(cntStatus);
}


function selectImage(idx)
{

	aImages[iSelectedImg].style.border = "2px solid white";
	aImages[idx].style.border = "2px solid " + Settings.strBorderColor;
	iSelectedImg = idx;

	adjustScroll();

	if (oZoomImg) {
		dezoomImage(oZoomImg);
		zoomImage(aImages[iSelectedImg]);
	}
}

function adjustScroll() {
	if (Settings.bAutoScroll && !oZoomImg) {
		var iBottom = getOffsetTop(aImages[iSelectedImg]) + aImages[iSelectedImg].offsetHeight;
		if (iBottom > oNewContent.offsetHeight) {
			scroll(0,iBottom - oNewContent.offsetHeight);
		} else {
			scroll(0,0);
		}
	}
}

function getOffsetTop(oEl) {
	var iTop = oEl.offsetTop;
	oParent = oEl.offsetParent;
	while (oParent) {
		iTop += oParent.offsetTop;
		oParent = oParent.offsetParent;
	}
	return iTop;
}

function submitImage(iIdx) {
	aRadios[iIdx].checked = true;
	cleanUp();
	document.forms[0].submit();
}


function checkAccept() {
	var pics = document.body.getElementsByTagName("img");
	for (var i=0; i < pics.length; i++) {
		var pic = pics[i];
		if (pic.src.match("accept_hit.gif")) {
			if (Settings.bAutoAccept) {
				gotoPage(pic.parentNode.href);
				return true;
			}
		}
		if (pic.src.match("return_hit.gif")) {
			strReturnURL = pic.parentNode.href;
		}
	}
}

// zoom functions
function zoomImage(oImg) {
	iZoomImgLeft = oImg.parentNode.style.left;
	iZoomImgTop = oImg.parentNode.style.top;

	oImg.parentNode.style.top = document.body.scrollTop + Settings.iZoomYOffset;
	oImg.parentNode.style.left = document.body.scrollLeft + Settings.iZoomXOffset;

	oImg.style.width = Settings.iImageWidthBig;
	oImg.style.height = Settings.iImageHeightBig;
	oImg.style.position = "absolute";

	oImg.style.zIndex = 10;

	var oOldImg = oZoomImg;
	oZoomImg = oImg;
	return oOldImg;
}
function dezoomImage(oImg) {
	oImg.style.position = "static";
	oImg.style.zIndex = 0;
	
	oImg.parentNode.style.left = iZoomImgLeft;
	oImg.parentNode.style.top = iZoomImgTop;
	oImg.style.width = Settings.iImageWidthSmall;
	oImg.style.height = Settings.iImageHeightSmall;
	if (oZoomImg == oImg) oZoomImg = null;
}

function functionName(obj) {
	if ((typeof obj).toLowerCase() == "function") {
		var str = obj.toString();
		if (str) return str.slice(str.indexOf(" ") + 1, str.indexOf("("));
	}
	return "";
}

function bindKey(fncAction) {
	if (iMode < 0) return false;
	if (!searchArray(aKeyAllow[0], fncAction)) {
		if (!searchArray(aKeyAllow[iMode], fncAction))
			return false;
	}

	var bAllowDefault = false;
	if (typeof fncAction != "function") return false;
	if (typeof arguments[arguments.length-1] == "boolean")
		bAllowDefault = arguments.pop();

	for (var a=1;a<arguments.length;a++) {
		if (typeof arguments[a] == "string") {
			var aCodes = Keys[arguments[a]];
			if (typeof aCodes != "object") aCodes = [aCodes];
			for (var k in aCodes) {
				KeyMap[aCodes[k]] = {fncAction:fncAction,bAllowDefault:bAllowDefault};
			}
		} else {
			KeyMap[arguments[a]] = {fncAction:fncAction,bAllowDefault:bAllowDefault};
		}
	}
	return true;
}

// event registering + cleanup
var aEventHandlers = new Array();
function cleanUp() {
	if (aEventHandlers) {
		for (var i=0;i<aEventHandlers.length;i++) {
			aEventHandlers[i][0].removeEventListener(
				aEventHandlers[i][1],
				aEventHandlers[i][2],
				aEventHandlers[i][3]
			);
		}
		aEventHandlers = new Array();
	}
	for (var a in aImages) aImages[a] = null;;
	aRadios = null;

}
function registerEvent(oTarget, strEvent, fncHandler, bCap) {
	aEventHandlers.push([oTarget,strEvent,fncHandler,bCap]);
	oTarget.addEventListener(strEvent, fncHandler, bCap);
}
registerEvent(window, 'unload', cleanUp, false);



function searchArray(a, n) {
	for (var i=0;i<a.length;i++)
		if (a[i] == n) return true;
	return false;
}


// keyboard functions
function keyToggleZoom() {
	if (oZoomImg) {
		dezoomImage(oZoomImg);
		adjustScroll();
	} else
		zoomImage(aImages[iSelectedImg]);
}

function keyMoveImageLeft() {
	var iNewImg = iSelectedImg;
	if (--iNewImg < 0) iNewImg = aImages.length-1;
	selectImage(iNewImg);
}

function keyMoveImageRight() {
	var iNewImg = iSelectedImg;
	if (++iNewImg > aImages.length-1) iNewImg = 0;
	selectImage(iNewImg);
}

function keyMoveImageDown() {
	var iNewImg = iSelectedImg + Settings.iImageColumns;
	if (iNewImg > aImages.length-1) iNewImg -= aImages.length;
	selectImage(iNewImg);
}

function keyMoveImageUp() {
	var iNewImg = iSelectedImg - Settings.iImageColumns;
	if (iNewImg < 0) iNewImg += aImages.length;
	selectImage(iNewImg);
}

function keySubmitSelectedImage() {
	submitImage(iSelectedImg);
}

function keyZoomMouseImage(oImg) {
	if (!oImg) {
		if (oMouseImage) oImg = oMouseImage;
	}
	if (!oImg) return;
	if (!oZoomImg) {
		for (var i=0;i<aImages.length;i++) {
			if (aImages[i] == oImg) {
				selectImage(i);
			}
		}
	}
	keyToggleZoom();
}

function keySubmitMouseImage(oImg) {
	for (var i=0;i<aImages.length;i++) {
		if (aImages[i] == oImg) {
			selectImage(i);
			submitImage(i);
			return;
		}
	}
}

function keyRefresh() {
	reloadPage();
}

function keySelectImage1() { selectImage(0); }
function keySelectImage2() { selectImage(1); }
function keySelectImage3() { selectImage(2); }
function keySelectImage4() { selectImage(3); }
function keySelectImage5() { selectImage(4); }
function keySelectImage6() { selectImage(5); }
function keySelectImage7() { selectImage(6); }
function keySelectImage8() { selectImage(7); }

function keySubmitImage1() { selectImage(0); submitImage(iSelectedImg); }
function keySubmitImage2() { selectImage(1); submitImage(iSelectedImg); }
function keySubmitImage3() { selectImage(2); submitImage(iSelectedImg); }
function keySubmitImage4() { selectImage(3); submitImage(iSelectedImg); }
function keySubmitImage5() { selectImage(4); submitImage(iSelectedImg); }
function keySubmitImage6() { selectImage(5); submitImage(iSelectedImg); }
function keySubmitImage7() { selectImage(6); submitImage(iSelectedImg); }
function keySubmitImage8() { selectImage(7); submitImage(iSelectedImg); }


function keyHelp() {
	var cntHelp = document.getElementById("mthelp");
	if (cntHelp.style.display == "none") {
		cntHelp.style.left = (oNewContent.offsetWidth / 2) - (parseInt(cntHelp.style.width) / 2);
		cntHelp.style.top = document.body.scrollTop + 20;
		cntHelp.style.display = "block";

	} else cntHelp.style.display = "none";
}

function keyStatus() {
	var cntStatus = document.getElementById("mtstatus");
	if (cntStatus.style.display == "none") {
		cntStatus.style.left = (oNewContent.offsetWidth / 2) - (parseInt(cntStatus.style.width) / 2);
		cntStatus.style.top = document.body.scrollTop + 20;
		cntStatus.style.display = "block";

	} else cntStatus.style.display = "none";
}

function keySetup() {
	var cntSetup = document.getElementById("mtsetup");
	if (cntSetup.style.display == "none") {
		bKeyEnabled = false;
		cntSetup.style.left = (oNewContent.offsetWidth / 2) - (parseInt(cntSetup.style.width) / 2);
		cntSetup.style.top = document.body.scrollTop + 20;
		cntSetup.style.display = "block";
		for (var s in Settings) {
			var oSet = document.getElementById("setup_" + s);
			if (oSet) {
				if (typeof Settings[s] == "boolean") {
					oSet.checked = Settings[s];
				} else {
					oSet.value = Settings[s];
				}
			}
		}
	} else {
		bKeyEnabled = true;
		cntSetup.style.display = "none";
	}
}


function keyReturnHIT() {
	if (!strReturnURL) {
		alert("No 'return HIT' URL :(");
		return;
	}
	var cnf = confirm("Return HIT?");
	if (cnf) gotoPage(strReturnURL);
}

function keyReturnToList() {
	var cnf = confirm("Return to HIT list?");
	if (cnf) gotoPage("/mturk/findhits");
}

function keySwitchView() {
	if (oNewContent.style.display == "none") {
		hideContent();
		oNewContent.style.display = "block";
	} else {
		var mainContent = document.body.childNodes;
		for (var i=0;i<mainContent.length;i++) {
			if (mainContent[i].style && mainContent[i].className != "tooltip") {
				mainContent[i].style.display = "inherit";
			}
		}
		oNewContent.style.display = "none";
	}
}

function keyDisable() {}


function keyMoveArtistDown() {
	var iNewArtist = iSelectedArtist + 1;
	if (iNewArtist > aArtistNames.length-1)
		iNewArtist = -1;
	selectArtistName(iNewArtist);
}

function keyMoveArtistUp() {
	var iNewArtist = iSelectedArtist - 1;
	if (iNewArtist < -1)
		iNewArtist = aArtistNames.length-1;
	selectArtistName(iNewArtist);
}

function selectArtistName(iIdx) {
	if (iSelectedArtist == -1) {
		oArtistInput.style.border = "";
		oArtistInput.style.border = "1px solid black";
		oArtistInput.blur();
	} else {
		aArtistOptions[iSelectedArtist].style.border = ""; // wtf
		aArtistOptions[iSelectedArtist].style.border = "1px solid white";
	}

	if (iIdx == -1) {
		oArtistInput.style.border = "";
		oArtistInput.style.border = "1px solid " + Settings.strBorderColor;
		setTimeout(function() {oArtistInput.focus();},10);
	} else {
		aArtistOptions[iIdx].style.border = "";
		aArtistOptions[iIdx].style.border = "1px solid " + Settings.strBorderColor;
		aArtistOptions[iIdx].focus();
		oArtistInput.value = htmlDecode(getTextContent(aArtistOptions[iIdx]));
	}
	iSelectedArtist = iIdx;
}

function keySelectArtistInput() {
	selectArtistName(-1);
}

function keySubmitArtist() {
	var oFormInput = getSingleElementByClass(document.body, "question free-text", "input");
	if (oFormInput) {
		oFormInput.value = oArtistInput.value;
	}
	//alert(oFormInput.value);
	document.forms[0].submit();
}


// allowed keys
var aKeyAllow = new Array();

// general keys
aKeyAllow[0] = [
	keyHelp,
	keyStatus,
	keySetup,
	keyReturnHIT,
	keySwitchView,
	keySpeakAddress,
	keySpeakEarned,
	keySpeakTotalHITs,
	keySpeakAvailHITs,
	keySpeakReward,
	keyReturnToList,
	keyRefresh,
	keyDisable
];

// A9 BlockView keys
aKeyAllow[Modes.BLOCKVIEW] = [
	keyToggleZoom,
	keySubmitSelectedImage,
	keyMoveImageLeft,
	keyMoveImageRight,
	keyMoveImageDown,
	keyMoveImageUp,
	keyZoomMouseImage,
	keySubmitMouseImage,
	keySelectImage1,
	keySelectImage2,
	keySelectImage3,
	keySelectImage4,
	keySelectImage5,
	keySelectImage6,
	keySelectImage7,
	keySelectImage8
];

aKeyAllow[Modes.ARTISTNAME] = [
	keyMoveArtistDown,
	keyMoveArtistUp,
	keySelectArtistInput,
	keySubmitArtist
];

// keys
Keys["BACK"]		= 0x08;
Keys["TAB"]		= 0x09;
Keys["RETURN"]		= 0x0D;
Keys["SHIFT"]		= 0x10;
Keys["CTRL"]		= 0x11;
Keys["ALT"]		= 0x12;
Keys["CAPSLOCK"]	= 0x14;
Keys["ESCAPE"]		= 0x1B;
Keys["SPACE"]		= 0x20;
Keys["PGUP"]		= 0x21;
Keys["PGDN"]		= 0x22;
Keys["LEFT"]	 	= 0x25;  
Keys["UP"]	   	= 0x26;  
Keys["RIGHT"]		= 0x27;  
Keys["DOWN"]	 	= 0x28;

Keys["ASTERISK"]	= 0x2A;
Keys["PLUS"]		= 0x2B;
Keys["MINUS"]		= 0x2D;  
Keys["SLASH"]		= 0x2F;

Keys["COMMA"]		= 0x2C;
Keys["PERIOD"]		= 0x2E;

Keys["0"]		= 0x30;
Keys["1"]		= 0x31;
Keys["2"]		= 0x32;
Keys["3"]		= 0x33;
Keys["4"]		= 0x34;
Keys["5"]		= 0x35;
Keys["6"]		= 0x36;
Keys["7"]		= 0x37;
Keys["8"]		= 0x38;
Keys["9"]		= 0x39;

Keys["A"]		= [0x61,0X41];
Keys["B"]		= [0x62,0X42];
Keys["C"]		= [0x63,0X43];
Keys["D"]		= [0x64,0X44];
Keys["E"]		= [0x65,0X45];
Keys["F"]		= [0x66,0X46];
Keys["G"]		= [0x67,0X47];
Keys["H"]		= [0x68,0X48];
Keys["I"]		= [0x69,0X49];
Keys["J"]		= [0x6A,0X4A];
Keys["K"]		= [0x6B,0X4B];
Keys["L"]		= [0x6C,0X4C];
Keys["M"]		= [0x6D,0X4D];
Keys["N"]		= [0x6E,0X4E];
Keys["O"]		= [0x6F,0X4F];
Keys["P"]		= [0x70,0X50];
Keys["Q"]		= [0x71,0X51];
Keys["R"]		= [0x72,0X52];
Keys["S"]		= [0x73,0X53];
Keys["T"]		= [0x74,0X54];
Keys["U"]		= [0x75,0X55];
Keys["V"]		= [0x76,0X56];
Keys["W"]		= [0x77,0X57];
Keys["X"]		= [0x78,0X58];
Keys["Y"]		= [0x79,0X59];
Keys["Z"]		= [0x7A,0X5A];


checkError();
load();

//registerEvent(document.body, "load", load, false);


