Travian Croptool Updater

By Risi Last update Oct 30, 2010 — Installed 4,827 times.

There are 4 previous versions of this script.

// ==UserScript==
// @author      Richard Laffers
// @email	rlaffers@gmail.com
// @namespace	http://userscripts.org/
// @name		Travian Croptool Updater
// @description	Update your crop tool (wheat manager) with a one click from the market.
// @include     http://s*.travian.*/*
// @include     http://s*.travian3.*/*
// @include     http://welt*.travian.*/*
// @exclude     http://forum.travian.*
// @exclude     http://www.travian.*
// @version     0.3
// ==/UserScript==



var LOG_LEVEL = 0; // 0 - errors, 1 - warnings & errors, 2 - info, warnings & errors, 3 - all
var sLang = "en";
var sGetParameters = "";
var sCurrentServer = "";    // Set this to the server's url to override automatic server detection
                            // (i.e. s1.travian.net)
                            // Dont set it if you're playing on multiple servers simultaneously!

var sAuthKey;

var init = detectLanguage() && initialize();


if(init) {
	// Images
	var sCloseBtn = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVCAIAAAAmdTLBAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH1gYKECMhBqiEGQAAADJ0RVh0Q29tbWVudABFcnN0ZWxsdCB2b24gRmxvcmlhbiBTY2hyZWllciBtaXQgVGhlIEdJTVCOHcWrAAADLUlEQVR42pWUPYhdRRiG32/OuTdZXTcas0n2ZiVVYBttLFLFQuNPZTQgiIIphGBAtNTC0lZBRRQVEYWoBAyk8ActRFCRLa3SpNH9icm699695+6Z+X5ei7Obtc1UH+/MM+/M9w4jJD0wnNi9czVuZ3iwSlKbQ5ohP3rzn+FGTBvmlqbUgpRgBQDdAbC0AOAepbV2ezI3v/Txd+nuQ0Jy4+3XqsMDqeqYjHxryJJRCiPgRneAMKU7rbBktlNrtoaj0WrvrpMXlxMAH29Kf5/0+6hqqXtS16h7SImpkqqSVCHVkipIQqq6NanuTdZXDs7VNQA2WzEZS12znTJvM2eWQi1UpSnDqQUgtcCVpnRHSqmqACQA1BKToY83oxnPPvz0/BvvR9OwaaKZeDOO8TC2NmP077FPfjzw/CvM23STlKTu3eKV04bbE+a8/8FTAI6+dTG00Aq00DK1LF78HcDs6bNwAwPcSaHjc5TCXKh6/fVz3cTggytwQzjc7vt6uRP/OrOEcGEAAXKHB8DcRm5pCvf1V5/pxGOf/kT64pd/7MBPnoArXEEXUrDLRztFyVCFOSMoXH35TMcsfvFrV6ycXUqJIiFwoUmYROz6mzK30LJ7Nwq5+tITt57a6rP3S6IIk0TqeBrC9/oXVhjOcGGIAAmDD7+/xQ+++jMl7PmHwlXC9vrHkqkFYWSQXHjvSkeun3+oK458tizdFjAJEytpz98KNdMK3Ri+8M43O/CFRwBef/EkALlj7sila4IQBlzFC013+bLjT9WFdy938NqFx0CCBHjj3AOdePjyqoSmDv7f+Qtzy9JS8/SXbwGsnT8NN3b5hzP8xnMnALQ/fI5wmkELzQAIyWun5qsD98jMbNq3X6oaAjAEAYRQJYpYFi+wgtKyFMtlPPWrW3zqatQAVEu020kqiZCqliQIh6CLOoXCi1iBFahRzdTVQh0A6pujaA4OuHkDyqqfUdWAgCFCYQi9gsNVwukqZjQ35UghC8cByDSTG3///MKjo7WVfq+mSEhCRJVEAKFXdAlPDPdAeMUozjh6/PFLv/UPDYRkVuzr3dbfh5sjv3OmmunjP4EhhHJu9NM9AAAAAElFTkSuQmCC";

	//Styles
	var cssStyle = "";
	cssStyle += ".ctu_draghandle {font-size: 120%; font-weight:bold;}";
	cssStyle += ".ctu_close_btn {float:right; padding:2px 4px; color:white; margin:-5px -15px 0 0;}";
	cssStyle +=	"#ctu_message {position:absolute; z-index:100; border:1px solid black; padding:10px 20px; color:black; width:335px}";
	cssStyle += ".handle {cursor: move;}";


	GM_addStyle(cssStyle);
	window.addEventListener('load', onLoad, false);
} else {
        _log("Initialization failed", 0);
    alert("Crop tool updater initialization failed.");
}

switch(sLang) {
	case "sk":
		aLangStrings = ["Prebehol pokus o aktualizáciu Crop Tool-u. Výsledok neznámy.", "Aktualizácia Crop Tool-u úspešná", "Aktualizácia Crop Tool-u zlyhala!", "Nastaviť Crop Tool Updater", "Aktualizovať Crop Tool", "Crop tool Updater:", "Skopíruj sem autentifikačný kľúč zo svojho profilu v Crop Tool-e"];
		break;
	case "en":
	case "com":
	case "uk":
	default:
        aLangStrings = ["Update attempted with unknown result.", "Your crop tool was successfully updated.", "Update failed!", "Crop Tool Updater setup", "Update Crop Tool", "Crop tool Updater:", "Enter the authentication key from your Crop tool profile"];
		break;
}

// Do not change the array below!
var aLangStringsMaster = ["Update attempted with unknown result.", "update_successful", "update_failed", "Crop Tool Updater setup", "Update Crop Tool", "Crop tool Updater:", "Enter the authentication key from your Crop tool profile"];

/**
* Custom log function .
* @param:{int} msg Message to log.
* @param {int} level
*/
function _log(msg, level) {
    if(level == undefined) {
        var level = 3;
    }
	if (level <= LOG_LEVEL && navigator.userAgent.indexOf("Opera") == -1) {
        if(typeof(console) == 'undefined' && console == null) {
            // Since Firebug 1.0, extensions.firebug.showChromeMessages must be set 
            // to true for GM_log messages to show up in the Firebug console.
            GM_log(msg);
            return true;
        }
        switch(level) {
            case 0:
                console.error(msg);
                break;
            case 1:
                console.warn(msg);
                break;
            case 2:
                console.info(msg);
                break;
            case 3:
            default:
                console.log(msg);
                break;
        }

		//GM_log(msg);
    } else {
    }
}

/**
 * Performs some initial checkings on conditions that have to be met to run the script
 *
 * @return true if initialization was successful, false otherwise
 */
function initialize() {

    if (typeof GM_getValue == "undefined") {
        alert('This script requires Greasemonkey v0.3 or newer, please upgrade to latest version!');
        _log("The current version of Greasemonkey is too old", 0);
        return false;
    }

    if (sCurrentServer != "") {
        return true;
    }

    // check what Travian server we're using
    var re = new RegExp("^https?://(.+\.travian3?\.[a-zA-Z.]+)(\/.*)?$", "i");
	var server = re.exec(window.location.href);
    if ((server) && (server[1])) {
        sCurrentServer = server[1];
        _log("using settings for server '" + server[1] + "'", 3);
        return true;
    }
    else {
         _log("ERROR, unknown Travian server!", 0);
        return false;
    }
}

/**
 * Detects the language used based on the server's url
 *
 * @return true if the language is successfully detected, false otherwise
 */
function detectLanguage() {
	//if(sLang != "") {return true;}
	var re = null; re = new RegExp("([a-zA-Z]{2,3})(\/.*)?$", "i");
	var lang = re.exec(window.location.href);

    if(!lang) {
            _log("failed to detect language automatically!", 0);
		if(sLang == "") sLang = "en";
        return true;
	} else {
        sLang = lang[1];
            _log("detected language '" + sLang + "'", 3);
        return true;
    }
}


function GM_post(url, data, callback) {
	GM_xmlhttpRequest({
		method: 'POST',
		url: url,
        data: data,
        headers: {
            "Content-Type": "application/x-www-form-urlencoded"
        },
		onload: callback,
	});
}


function handleUpdateCrop(response)
{
    _log('-> handleUpdateCrop()', 3);



	if (response.readyState == 4) {
		if (response.status == 200) { // ok
            var sResponse = response.responseText;
            _log(sResponse, 3);

			if(!sResponse) {  // error retrieving the response
				printMsg( t("Update attempted with unknown result."), true );
				return;
			}


			var re = new RegExp('<title>(.*)<\/title>\n<log>(.*)<\/log>', 'i');
			var aMatchResults = sResponse.match(re);

            if( aMatchResults != null && aMatchResults[1] == "Success") {
                var message = t('update_successful');
                //if(LOG_LEVEL > 2) message += '<br/>' + aMatchResults[2];
                message += '<br/>' + aMatchResults[2];
                printMsg(message);
			} else if( aMatchResults != null ) {
                var message = t('update_failed');
                //if(LOG_LEVEL > 2) message += '<br/>' + aMatchResults[2];
                message += '<br/>' + aMatchResults[2];
                printMsg(message, 1);
			} else {
                var message = t('update_failed');
                message += '<br/>Your Crop Tool authentication key is probably invalid.';
                printMsg(message, 1);
            }


        } else {
				_log("HTTP request status: " + response.status, 3);
        }
    }

    _log('<- handleUpdateCrop()', 3);
}



function addMsg(sMsg, obj) {
	obj.innerHTML += "<br/>" + sMsg ;
}

function printMsg(sMsg,bError) {
		_log("-> printMsg()", 3);
	var oDate = new Date();
	var sWhen = oDate.toLocaleString() + "\n";
	_log(sWhen + sMsg, 3);
	//alert(sMsg);

	// delete old message
	var oOldMessage = $("ctu_message");
	if(oOldMessage) {
			_log("Removing the old message." +oOldMessage, 3);
		oOldMessage.parentNode.removeChild(oOldMessage);
	}

	// here we generate a link which closes the message
	var sLinkClose = "<a href='#' onclick='document.getElementById(\"ctu_message\").parentNode.removeChild(document.getElementById(\"ctu_message\"));' class='ctu_close_btn'><img src='" +sCloseBtn+ "' alt='X' /></a>";

	var sBgColor = (bError) ? "#FFB89F" : "#90FF8F";
	var oMsgBox = document.createElement("div");
	//oMsgBox.innerHTML = sLinkClose + "<div id='ttq_draghandle_msg' class='handle ttq_draghandle' style='background-color:white; -moz-opacity:0.2; border:1px dashed white;' >&nbsp;</div>" + sMsg;
	oMsgBox.innerHTML = "<div id='ctu_draghandle_msg' class='handle'>" + sLinkClose + sMsg + "</div>";
	oMsgBox.style.backgroundColor = sBgColor;
	var msgCoords = getOption("MSG_POSITION", "215px_215px");
	msgCoords = msgCoords.split("_");
	oMsgBox.style.top = msgCoords[0];
	oMsgBox.style.left = msgCoords[1];
	oMsgBox.id = "ctu_message";
	document.body.appendChild(oMsgBox);
	makeDraggable($('ctu_draghandle_msg'));
		_log("<- printMsg()", 3);
}

/**
 * Retrieves the value corresponding do the given variable name and the current Travian server
 *
 * @param name          The name of the variable
 * @param defaultValue  default value if name is not found
 */
function getVariable(name, defaultValue) 
{
		_log("-> getVariable()", 3);

    if(!defaultValue) { var defaultValue = ''; }

    name = sCurrentServer + '_' + name;
    var data = GM_getValue(name, defaultValue);

        _log( "<- getVariable()", 3);
    return data;
}

/**
 * Sets the value for the given variable name and the current Travian server
 * Use greasemonkey's built-in system instead of cookies to permantenly store and read settings
 *
 * @param name  The name of the variable
 * @param value The value to be assigned
 */
function setVariable(name, value) 
{
		_log( "-> setVariable()", 3);

    name = sCurrentServer + '_' + name;
    GM_setValue(name, value);

        _log( "<- setVariable()", 3);
    return true;
}

/**
* @param key: name of the parameter in the TTQ_OPTIONS variable
* @param defaultValue: this is returned if the parameter is not found
* @param type: if set, type conversion occurs. Values {string, integer, boolean} The conversion occurs only if it is not the defaultValue being returned.
*/
function getOption(key, defaultValue, type) 
{
        _log( "-> getOption()", 3);

    var options = getVariable('CTU_OPTIONS', '');
	options = options.split(",");
	var myOption = options.indexOf(key);
	if(myOption < 0) {return defaultValue;}
	switch(type) {
		case "boolean":
			var myOption = ( options[myOption + 1] == "true") ? true:false;
			break;
		case "integer":
			var myOption = parseInt(options[myOption + 1]);
			break;
		case "string":
		default:
			var myOption = options[myOption + 1];
			break;
	}
        _log( "<- getOption()", 3);
    return myOption;
}

function setOption(key, value) 
{
        _log( "-> setOption()", 3);

    var options = getVariable('CTU_OPTIONS', '');
	if(options != '') options = options.split(",");
	else options = [];
    var myOption = options.indexOf(key);
	if(myOption < 0) {
		options.push(key);
		options.push(value);
	} else {
		options[myOption + 1] = value;
	}

    setVariable('CTU_OPTIONS', options.join(","));
        _log( "<- setOption()", 3);
}

/************************ Drag n drop*******************************/
var mouseOffset = null;
var iMouseDown  = false;
var lMouseState = false;
var dragObject  = null;
var curTarget   = null;

function mouseCoords(ev){
	return {x:ev.pageX, y:ev.pageY};
}

function makeClickable(object){
	object.onmousedown = function(){
		dragObject = this;
	}
}

function getMouseOffset(target, ev){
	var docPos    = getPosition(target);
	var mousePos  = mouseCoords(ev);
	return {x:mousePos.x - docPos.x, y:mousePos.y - docPos.y};
}

function getPosition(e){
	var left = 0;
	var top  = 0;
	while (e.offsetParent){
		left += e.offsetLeft + (e.currentStyle?(parseInt(e.currentStyle.borderLeftWidth)).NaN0():0);
		top  += e.offsetTop  + (e.currentStyle?(parseInt(e.currentStyle.borderTopWidth)).NaN0():0);
		e     = e.offsetParent;
	}
	left += e.offsetLeft + (e.currentStyle?(parseInt(e.currentStyle.borderLeftWidth)).NaN0():0);
	top  += e.offsetTop  + (e.currentStyle?(parseInt(e.currentStyle.borderTopWidth)).NaN0():0);
	return {x:left, y:top};
}

function mouseMove(ev){
	var target   = ev.target;
	var mousePos = mouseCoords(ev);

	if(dragObject){
		dragObject.style.position = 'absolute';
		dragObject.style.top      = (mousePos.y - mouseOffset.y) +"px";
		dragObject.style.left     = (mousePos.x - mouseOffset.x) +"px";
	}
	lMouseState = iMouseDown;
	return false;
}

function mouseUp(ev){
	if(dragObject) {
		switch(dragObject.id) {
			case "ttq_message":
				var key = "MSG_POSITION";
				break;
			case "timerform_wrapper":
				var key = "FORM_POSITION";
				break;
			case "ttq_history":
				var key = "HISTORY_POSITION";
				break;
			case "ttq_tasklist":
			default:
				var key = "LIST_POSITION";
				break;
		}
		setOption(key, dragObject.style.top +"_"+ dragObject.style.left);
	}
	dragObject = null;
	iMouseDown = false;
}

function mouseDown(ev){
var mousePos = mouseCoords(ev);
	var target = ev.target;
	iMouseDown = true;
	if(target.getAttribute('DragObj')){
		return false;
	}
}

function makeDraggable(item){
	if(!item) return;
	item.addEventListener("mousedown",function(ev){
		dragObject  = this.parentNode;
		mouseOffset = getMouseOffset(this.parentNode, ev);
		return false;
	}, false);
}

document.addEventListener("mousemove", mouseMove, false);
document.addEventListener("mousedown", mouseDown, false);
document.addEventListener("mouseup", mouseUp, false);

/************************************************************************************/

function xpath(query, object) {
	if(!object) var object = document;
	return document.evaluate(query, object, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
}

function t(str) {
	var index = aLangStringsMaster.indexOf(str);
	var sTranslatedStr =  aLangStrings[index];
	if(sTranslatedStr) {
		return sTranslatedStr;
	} else {
		return str;
	}
}

function $(id) {
  return document.getElementById(id);
}

function createLink() {
		_log( "-->createLink", 3);
		_log( "sAuthKey = "+sAuthKey, 3);

	var xpathRes = xpath("//div[@class='gid17']//div[@id='textmenu']/a[last()]");


	if(xpathRes.snapshotLength <= 0) {
			_log( "This is not Market.", 2)
		return false;
	} 


	if(!sAuthKey) {
		var oLink = document.createElement("a");
		oLink.id = "setupCT";
		oLink.innerHTML = t("Crop Tool Updater setup");
		oLink.title = t("Crop Tool Updater setup");
		oLink.href = "#";
		oLink.addEventListener('click',	promptKey, false);
	} else {
		var oLink = document.createElement("a");
		oLink.id = "updateCT";
		oLink.innerHTML = t("Update Crop Tool");
		oLink.title = t("Update Crop Tool");
		oLink.href = "#";
		oLink.addEventListener('click',	startUpdate, false);
	}

	var oPar = xpathRes.snapshotItem(0).parentNode;
	oPar.innerHTML += " | ";
	oPar.appendChild(oLink);

		_log( "<--createLink", 3);
}

/**
 * This function supersedes createLink(), to thwart detection attempts.
 */
function createMenuCommand()
{
    // detect if we are on the market page
    var xpathRes = xpath("//div[@class='gid17']//div[@id='textmenu']/a[last()]");


	if(xpathRes.snapshotLength <= 0) {
			_log( "This is not Market.", 2)
		return false;
	} 

	if(!sAuthKey) {
        _log('Crop Tool Updater has not been configured yet.', 1);
        return false;
    } else {
	    GM_registerMenuCommand(t("Update Crop Tool now!"), startUpdate);
    }
    return true;

}




function startUpdate() {
		_log( "-->startUpdate", 3);


	//send data
    // this village's z-coordinate:
	var key = getOption("KEY_" + getCurrentZ(), false);

	if(key == false) {
			_log( "No crop tool key URL is set for this village!", 1);
		promptKey();
		return false;
	}  else {
			_log( "Sending crop...", 2);
            // get page content
            var data = parse();


			GM_post(key, data, handleUpdateCrop);

	}

		_log( "<--startUpdate", 3);
}


/**
 * XY of the active village
 *
 * @return array
 */
function getCurrentXY()
{
    _log( '->getCurrentXY()', 3);
    var coords = [];
    var oDivs = xpath("//div[@id='side_info']//td[@class='dot hl']/../td[@class='aligned_coords']/div[@class!='pi']");
    if(oDivs.snapshotLength <= 0) {
        return false;
    }
    var x = oDivs.snapshotItem(0).innerHTML;
    var matches = x.match(/([-0-9]{1,4})/gi);
    if( matches === null ) {
        return false;
    }
   
    coords[0] = matches;

    var y = oDivs.snapshotItem(1).innerHTML;
    matches = y.match(/([-0-9]{1,4})/gi);
    if( matches === null ) {
        return false;
    }
    coords[1] = matches;

    //_log( 'coords=' + coords[0] + '|' + coords[1], 2 );
    _log( '<-getCurrentXY()', 3);

    return coords;
}

function getCurrentZ()
{
    var coords = getCurrentXY();
    if(coords == false) {
        return 'default'; // this will be added to KEY_ option
    }
    var z = coordsXYToZ( coords[0], coords[1] );
    return z;
}


function coordsXYToZ(x, y)
{
	x = parseInt(x);
	y = parseInt(y);
	var coordZ = (x + 401) + ((400 - y) * 801);
	return coordZ;
}

/**
 * Get full market page content. Parse into POST string.
 * I.e.: a=1&b=second&ddd=third
 *
 * @return string
 */
function parse()
{
    var data = '';

    //var oPage = xpath('//body');
    var oXpath = xpath('//table[@class="traders"]/..');
    if(oXpath.snapshotLength < 1) {
        // no merchants
        data = 'traders=0';
    } else {
        data = 'traders=' + encodeURIComponent( oXpath.snapshotItem(0).innerHTML );
    }

    oXpath = xpath('id("tp1")');
    data += '&time=' + encodeURIComponent( oXpath.snapshotItem(0).innerHTML );

    oXpath = xpath('id("resWrap")');
    data += '&resources=' + encodeURIComponent( oXpath.snapshotItem(0).innerHTML );

    data += '&server=' + encodeURIComponent(sCurrentServer);
    data += '&z=' + getCurrentZ();
    _log( data, 3);

    return data;
}


function promptKey() {
		_log( "-->promptKey", 3);
	var newKey = false;
	var newKey = prompt(t("Crop tool Updater:") + "\n" + t("Enter the authentication key from your Crop tool profile") + "\n", sAuthKey);
	var re = /^http[s]?:\/\/[./?=&a-zA-Z0-9]*$/i;
	if(re.test(newKey)) {
			_log( "Address is valid", 3);
		setOption("KEY_" + getCurrentZ(), newKey);
		location.reload();
	} else {
			_log( "Invalid URL!", 1);
	}
		_log( "<--promptKey", 3);
}

function onLoad() {
	sAuthKey = getOption("KEY_" + getCurrentZ(), false);

    // get stored correct coordinates on this server
	GM_registerMenuCommand(t("Crop Tool Updater setup"), promptKey);
	var re = /.*build\.php.*/i;
	if (re.test(window.location.href)) {
		//createLink();
		createMenuCommand();
	}
}