User script: Difference between revisions

From GreaseSpot Wiki
Jump to navigationJump to search
Undo revision 4439 by 124.176.248.160 (Talk)
No edit summary
Line 1: Line 1:
{{stub}}
// ==UserScript==
// @name          Travian Building Mover
// @namespace      Travian Building Mover
// @description    This repositions the buildings on the dorf2.php page
// @version        1.5.1
// @include        http://*.travian.*/dorf2.php*
// @license        GPL 3 or any later version
// ==/UserScript==


'''User scripts''', or '''userscripts''', are scripts that make on-the-fly changes to specific web pages on the client side (in the browser or a proxy server), typically to change their appearance or to add or modify functionality.
/*****************************************************************************
* Copyright (C) 2009 Adriaan Tichler
*
* This is free software; you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation; either version 3 of the License, or (at your option) any later
* version.
*
* This is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Public License for more details
*
* To obtain a copy of the GNU General Public License, please see
* <http://www.gnu.org.licenses/>
*****************************************************************************/


User scripts for [[Greasemonkey]] and Greasemonkey-compatible alternatives are written in [http://developer.mozilla.org/en/docs/JavaScript JavaScript], but there is at least one example of using another scripting language, namely Ruby in [http://github.com/why/mousehole/tree/master/ MouseHole].
// First, get the user mappings...
var mapping = eval(GM_getValue('mapping', '({})'));


For tools other than Greasemonkey that apply user scripts to web sites, see [[Cross-browser_userscripting|Cross-browser Userscripting]] and [http://en.wikipedia.org/wiki/Greasemonkey#Compatibility Wikipedia].
function reset_vil(){
    if (!window.confirm("Reset this village's buildings to their original position?")) return;
    delete mapping[did];
    GM_setValue('mapping', uneval(mapping));
    location.reload();
}
 
// Register these first, in case the script fails later and we still want to reset things
GM_registerMenuCommand("BM: Reset this village's buildings", reset_vil);
GM_registerMenuCommand("BM: Reset all buildings", function(){
        if (!window.confirm("Reset all buildings in all villages to their original position?")) return;
        GM_setValue('mapping', '({})');
        location.reload();
    });
 
// Init
var server = location.host;
var uid = document.evaluate("id('side_navi')//a[contains(@href, 'spieler.php')]/@href", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue.textContent.match(/uid=(\d+)/)[1];
 
// Add support for the 'toggle building levels' button, so we can make it persistent
// Otherwise, this value keeps getting cleared whenever cookies get cleared, which for the more paranoid of us would be every time the browser restarts ;-)
var toggle = eval(GM_getValue('show_building_level', '({})'));
var button = document.getElementById('lswitch');
if (button != undefined){
    var current_toggle = button.className == "on";
    // If we don't have a saved setting for this server and user, then take the present value of the toggle
    if (toggle[server+'_'+uid] == undefined){
        toggle[server+'_'+uid] = current_toggle;
        GM_setValue('show_building_level', uneval(toggle));
    }
    // Otherwise, correct the value of the toggle to what we have saved
    // *note* calling vil_levels_toggle() in this fashion is safer than using unsafeWindow, because unsafeWindow would elevate the privilege of the function.
    else if (current_toggle != toggle[server+'_'+uid]) location.href = "javascript:void(vil_levels_toggle());";
 
    // Either way, add a listener to update our version of the toggle when it's clicked
    button.addEventListener('click', function(){
            toggle[server+'_'+uid] = !toggle[server+'_'+uid];
            GM_setValue('show_building_level', uneval(toggle));
        }, false);
}
 
// Look at where the original buildings are *first*
var data = [];
var poly = document.getElementById('map2').childNodes;
var img = document.getElementById('map2').nextSibling.nextSibling.childNodes;
 
// Raw_num won't have the right number of elements if there are unused building spots in the village
var num = [];
var raw_num = document.getElementById('village2_levels');
// Some servers use a different ID...
if (raw_num == undefined) raw_num = document.getElementById('levels');
if (raw_num != undefined){
    raw_num = raw_num.childNodes;
    for (var i in raw_num){
        if (raw_num[i].className == undefined || raw_num[i].className.indexOf('level') >= 0) continue; // Don't do walls, closing divs, or the rally point
        num[raw_num[i].className.split('d')[1] - 0] = raw_num[i]; // re-index these nodes so they don't get mistakenly translated by an empty spot
    }
}
 
// Store the current info about all of the buildings...
for (var i=1; i <= 20; i++){
    data[i] = [];
    //GM_log(i + ' ' + img[i].alt + ' | ' + poly[i].title);
 
    data[i]['className'] = img[i].className.split(' ')[2];
    if (num[i] != undefined) data[i]['num'] = num[i].className;
 
    data[i]['title'] = poly[i].title;
    data[i]['href'] = poly[i].href;
}
 
// Get the active village, to store the new mappings
// We don't have to worry about postfixes because we're only running on one page
var did = document.evaluate('//tr[@class="sel"]/td[@class="text"]/a', document, null, XPathResult.ANY_UNORDERED_NODE_TYPE, null).singleNodeValue;
if (did) did = did.href.match('newdid=([0-9]*)')[1];
else {
    // If this fails, there's a possibility that we're just running the other type of travian (??? still 3.5, but with different code...)
    did = document.evaluate('//table[@id="vlist"]/tbody/tr/td[@class="dot hl"]', document, null, XPathResult.ANY_UNORDERED_NODE_TYPE,null).singleNodeValue;
    if (did) did = did.parentNode.innerHTML.match('newdid=([0-9]*)')[1];
}
// Single village support courtesy of Booboo
if (!did) {
    var single_villages = eval(GM_getValue('single_villages', '({})'));
    if (!single_villages[server + "_" + uid]){
GM_xmlhttpRequest({
                method: 'GET',
                url: "http://" + server + "/dorf3.php",
                onload: function(xhr){
                    var did = xhr.responseText.match(/newdid=(\d+)/)[1];
                    single_villages[server + "_" + uid] = did;
                    GM_setValue('single_villages', uneval(single_villages));
                    window.location.reload();
                }
            });
        return;
    }
    else did = single_villages[server + "_" + uid];
}
 
if (mapping[did] == undefined) mapping[did] = {};
 
// This moves a building from src to dest.
function move(dest, src){
    //GM_log('Moving '+src+' to '+dest);
    var base = img[dest].className.split(' '); // We only change the last part of the class name
    img[dest].className = base[0]+' '+base[1]+' '+data[src].className;
 
    if (num[src] != undefined) num[src].className = 'd'+dest; // Move the building numbers. This goes backwards from everything else...
 
    poly[dest].title = data[src].title;
    poly[dest].href = data[src].href;
}
 
// The index is the destination, the value the source
for (var i in mapping[did]) move(i, mapping[did][i]);
 
// Get input from the user... add the moving truck.
var div = document.createElement('div');
div.setAttribute('style', 'position:absolute; top:489px; left:163px; padding:2px; z-index:100; border:none; cursor:pointer');
div.innerHTML = '<img title="Swap Buildings" src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/4QCURXhpZgAASUkqAAgAAAABAGmHBAABAAAAGgAAAAAAAAABAIaSBwBgAAAALAAAAAAAAABBU0NJSQAAAFdpbmRvd0V4dDogMzAzNSwgMjY3NA1XaW5kb3dPcmc6IDAsIDANQ29udGVudDogMTAsIDI2LCAzMDEyLCAyNjcyDUlnbm9yZWQgT3Bjb2RlczoNJDEwNQD/2wBDAAUDBAQEAwUEBAQFBQUGBwwIBwcHBw8LCwkMEQ8SEhEPERETFhwXExQaFRERGCEYGh0dHx8fExciJCIeJBweHx7/2wBDAQUFBQcGBw4ICA4eFBEUHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh7/wAARCAAcACEDASIAAhEBAxEB/8QAGQAAAwEBAQAAAAAAAAAAAAAAAAcIBgED/8QAMRAAAgIBAwMCAwUJAAAAAAAAAQIDBAUGERIABxMhMQgVFxQiMmGUIzRRUlNWcZHS/8QAGAEBAAMBAAAAAAAAAAAAAAAABQAEBgH/xAAkEQABAwMDBAMAAAAAAAAAAAABAAIDBBExEiEiBRRRYUFSwf/aAAwDAQACEQMRAD8AoP4khmPoxqF8FlbmJvRRxSpbqTtDLEqTxtIVdSCPuBh+e/UNyYPW8unY83FqS/NesTeCpG1mQ2rTjcSEFSTupjb3A9Nt2I2DXN3uzWLl7Sa2px2uVk4G+EVY2P3xBJt7D+PUZZm/Rg7lYiOu9z5LirMzJY8TVuAmaWWX9oxU7gsoB5KNh6b9F175w5ojxuceFcpGsJ5Khuxd7WmE0hJmEzGd1VjPmVmGShkyzXEhifxcq7OeXNWjfeF2Kt6cSjDZ35iMlQzGMr5PHTpYqWI/JFIu+zL/AIPqD+R9R0kex+pdOVu2tQ2M5jqvK/kZeNi+hYK96wykuWPL0P4tzv779ZrSmu27e5HIaiuajqZTT2dyd94MYbpHgX7XMyzQMzuhaVWJEYESMCrEjg7HL9F6/UPrZ6asHFruBsd/WLbeVJ4m5Yqe4j+dv99HWJ+o+nP6l79M/wD10dbfu4fsqtnrHfEm+PuURpDGN5NT5pAVroRwirhwWsTggjx7rwCkHy7lOLDmVUGL7ZrrCtFofQjY6pisFMnzPMT0o53uWVIYxBSNnCkqWDNsAVADbb9eT53J2e8Wd00bLRS5vUjUrWXT9+WE2jCqo53RQkahVHDYe+2/r1V2h9M4XSWHjweCpitSqjxxruWYj8RLMfUks7Ek+5Y9IEiOK4y5BxskrKxz3u4RYHk/JP4oqxvw0d6slnaz5VcNQr17CFZRPD4I039WjhjHEbbctuI3J3I39enlW+FrSlqtTj1RqDMZZqcgKGAmBZYw0hIlVjJzdg4VnUo2yLx4kEmhB109HljL3tum9RS/+jHa3+wdO/oV6Ot90dTX6XdRX//Z">';
document.body.appendChild(div);
 
function notify(msg){
    var div = document.createElement('div');
    div.setAttribute('style', 'position:absolute; top:350px; left:400px; padding:2px; z-index:160; border:solid black 1px; background:#fff; -moz-border-radius:5px;');
    div.innerHTML = msg;
    document.body.appendChild(div);
 
    window.setTimeout(function(){div.parentNode.removeChild(div);}, 2000);
}
 
var truck_stage = 0;
div.addEventListener('click', function(){
        // If the truck gets clicked a second time...
        if (truck_stage == 1){
            reset_vil();
            return;
        }
        truck_stage++;
        div.childNodes[0].title = 'Restore building positions';
 
        // Add listeners to all of the objects
        var stage = 0;
        var src;
 
        // Cut all of the village links, so clicking on the villages no longer redirects
        // Also, store index info in here - it's the easiest way I can think of getting this info to the click listener routine
        for (var i in poly)
            if (poly[i].href != undefined)
                poly[i].href = '#'+(poly[i].href.split('id=')[1] - 18);
        var wall = document.getElementById('map1').childNodes;
        for (var i in wall)
            if (wall[i].href != undefined)
                wall[i].href = '#22';
 
        // Listen for a click on each building
        for (var i in poly) poly[i].addEventListener('click', function(e){
                var dest = e.target.href.split('#')[1]; // Extract the index info from above
 
                // Error conditions
                if (dest == '21'){
                    notify('<b>You cannot move the rally point</b>');
                    return;
                }
                if (dest == '22'){
                    notify('<b>You cannot move the walls</b>');
                    return;
                }
 
                // This is the first click
                if (stage == 0){
                    src = dest; // actually...
 
                    notify('<b>Click on the second one</b>');
                    stage++;
                } else { // Now we have to save said data
                    var m = mapping[did];
 
                    //GM_log('src='+src+' dest='+dest);
                    // First, find who *holds* src right now
                    var temp = src;
                    while (m[temp] != undefined && m[temp] != src) temp = m[temp];
                    // Find who *holds* dest right now
                    var temp2 = dest;
                    while (m[temp2] != undefined && m[temp2] != dest) temp2 = m[temp2];
                    //GM_log('m['+temp+']='+dest);
                    //GM_log('m['+temp2+']='+src);
                    m[temp] = parseInt(dest);
                    m[temp2] = parseInt(src);
 
                    GM_setValue('mapping', uneval(mapping));
                    window.location.reload();
                }
            }, false);
 
        notify('<b>Click on the first building</b>');
    }, false);

Revision as of 11:12, 10 February 2010

// ==UserScript== // @name Travian Building Mover // @namespace Travian Building Mover // @description This repositions the buildings on the dorf2.php page // @version 1.5.1 // @include http://*.travian.*/dorf2.php* // @license GPL 3 or any later version // ==/UserScript==

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

* Copyright (C) 2009 Adriaan Tichler
*
* This is free software; you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation; either version 3 of the License, or (at your option) any later
* version.
*
* This is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Public License for more details
*
* To obtain a copy of the GNU General Public License, please see
* <http://www.gnu.org.licenses/>
*****************************************************************************/

// First, get the user mappings... var mapping = eval(GM_getValue('mapping', '({})'));

function reset_vil(){

   if (!window.confirm("Reset this village's buildings to their original position?")) return;
   delete mapping[did];
   GM_setValue('mapping', uneval(mapping));
   location.reload();

}

// Register these first, in case the script fails later and we still want to reset things GM_registerMenuCommand("BM: Reset this village's buildings", reset_vil); GM_registerMenuCommand("BM: Reset all buildings", function(){

       if (!window.confirm("Reset all buildings in all villages to their original position?")) return;
       GM_setValue('mapping', '({})');
       location.reload();
   });
// Init

var server = location.host; var uid = document.evaluate("id('side_navi')//a[contains(@href, 'spieler.php')]/@href", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue.textContent.match(/uid=(\d+)/)[1];

// Add support for the 'toggle building levels' button, so we can make it persistent // Otherwise, this value keeps getting cleared whenever cookies get cleared, which for the more paranoid of us would be every time the browser restarts ;-) var toggle = eval(GM_getValue('show_building_level', '({})')); var button = document.getElementById('lswitch'); if (button != undefined){

   var current_toggle = button.className == "on";
   // If we don't have a saved setting for this server and user, then take the present value of the toggle
   if (toggle[server+'_'+uid] == undefined){
       toggle[server+'_'+uid] = current_toggle;
       GM_setValue('show_building_level', uneval(toggle));
   }
   // Otherwise, correct the value of the toggle to what we have saved
   // *note* calling vil_levels_toggle() in this fashion is safer than using unsafeWindow, because unsafeWindow would elevate the privilege of the function.
   else if (current_toggle != toggle[server+'_'+uid]) location.href = "javascript:void(vil_levels_toggle());";
   // Either way, add a listener to update our version of the toggle when it's clicked
   button.addEventListener('click', function(){
           toggle[server+'_'+uid] = !toggle[server+'_'+uid];
           GM_setValue('show_building_level', uneval(toggle));
       }, false);

}

// Look at where the original buildings are *first* var data = []; var poly = document.getElementById('map2').childNodes; var img = document.getElementById('map2').nextSibling.nextSibling.childNodes;

// Raw_num won't have the right number of elements if there are unused building spots in the village var num = []; var raw_num = document.getElementById('village2_levels'); // Some servers use a different ID... if (raw_num == undefined) raw_num = document.getElementById('levels'); if (raw_num != undefined){

   raw_num = raw_num.childNodes;
   for (var i in raw_num){
       if (raw_num[i].className == undefined || raw_num[i].className.indexOf('level') >= 0) continue; // Don't do walls, closing divs, or the rally point
       num[raw_num[i].className.split('d')[1] - 0] = raw_num[i]; // re-index these nodes so they don't get mistakenly translated by an empty spot
   }

}

// Store the current info about all of the buildings... for (var i=1; i <= 20; i++){

   data[i] = [];
   //GM_log(i + ' ' + img[i].alt + ' | ' + poly[i].title);
   data[i]['className'] = img[i].className.split(' ')[2];
   if (num[i] != undefined) data[i]['num'] = num[i].className;
   data[i]['title'] = poly[i].title;
   data[i]['href'] = poly[i].href;

}

// Get the active village, to store the new mappings // We don't have to worry about postfixes because we're only running on one page var did = document.evaluate('//tr[@class="sel"]/td[@class="text"]/a', document, null, XPathResult.ANY_UNORDERED_NODE_TYPE, null).singleNodeValue; if (did) did = did.href.match('newdid=([0-9]*)')[1]; else {

   // If this fails, there's a possibility that we're just running the other type of travian (??? still 3.5, but with different code...)
   did = document.evaluate('//table[@id="vlist"]/tbody/tr/td[@class="dot hl"]', document, null, XPathResult.ANY_UNORDERED_NODE_TYPE,null).singleNodeValue;
   if (did) did = did.parentNode.innerHTML.match('newdid=([0-9]*)')[1];

} // Single village support courtesy of Booboo if (!did) {

   var single_villages = eval(GM_getValue('single_villages', '({})'));
   if (!single_villages[server + "_" + uid]){

GM_xmlhttpRequest({

               method: 'GET',
               url: "http://" + server + "/dorf3.php",
               onload: function(xhr){
                   var did = xhr.responseText.match(/newdid=(\d+)/)[1];
                   single_villages[server + "_" + uid] = did;
                   GM_setValue('single_villages', uneval(single_villages));
                   window.location.reload();
               }
           });
       return;
   }
   else did = single_villages[server + "_" + uid];

}

if (mapping[did] == undefined) mapping[did] = {};

// This moves a building from src to dest. function move(dest, src){

   //GM_log('Moving '+src+' to '+dest);
   var base = img[dest].className.split(' '); // We only change the last part of the class name
   img[dest].className = base[0]+' '+base[1]+' '+data[src].className;
   if (num[src] != undefined) num[src].className = 'd'+dest; // Move the building numbers. This goes backwards from everything else...
   poly[dest].title = data[src].title;
   poly[dest].href = data[src].href;

}

// The index is the destination, the value the source for (var i in mapping[did]) move(i, mapping[did][i]);

// Get input from the user... add the moving truck. var div = document.createElement('div'); div.setAttribute('style', 'position:absolute; top:489px; left:163px; padding:2px; z-index:100; border:none; cursor:pointer'); div.innerHTML = '<img title="Swap Buildings" src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/4QCURXhpZgAASUkqAAgAAAABAGmHBAABAAAAGgAAAAAAAAABAIaSBwBgAAAALAAAAAAAAABBU0NJSQAAAFdpbmRvd0V4dDogMzAzNSwgMjY3NA1XaW5kb3dPcmc6IDAsIDANQ29udGVudDogMTAsIDI2LCAzMDEyLCAyNjcyDUlnbm9yZWQgT3Bjb2RlczoNJDEwNQD/2wBDAAUDBAQEAwUEBAQFBQUGBwwIBwcHBw8LCwkMEQ8SEhEPERETFhwXExQaFRERGCEYGh0dHx8fExciJCIeJBweHx7/2wBDAQUFBQcGBw4ICA4eFBEUHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh7/wAARCAAcACEDASIAAhEBAxEB/8QAGQAAAwEBAQAAAAAAAAAAAAAAAAcIBgED/8QAMRAAAgIBAwMCAwUJAAAAAAAAAQIDBAUGERIABxMhMQgVFxQiMmGUIzRRUlNWcZHS/8QAGAEBAAMBAAAAAAAAAAAAAAAABQAEBgH/xAAkEQABAwMDBAMAAAAAAAAAAAABAAIDBBExEiEiBRRRYUFSwf/aAAwDAQACEQMRAD8AoP4khmPoxqF8FlbmJvRRxSpbqTtDLEqTxtIVdSCPuBh+e/UNyYPW8unY83FqS/NesTeCpG1mQ2rTjcSEFSTupjb3A9Nt2I2DXN3uzWLl7Sa2px2uVk4G+EVY2P3xBJt7D+PUZZm/Rg7lYiOu9z5LirMzJY8TVuAmaWWX9oxU7gsoB5KNh6b9F175w5ojxuceFcpGsJ5Khuxd7WmE0hJmEzGd1VjPmVmGShkyzXEhifxcq7OeXNWjfeF2Kt6cSjDZ35iMlQzGMr5PHTpYqWI/JFIu+zL/AIPqD+R9R0kex+pdOVu2tQ2M5jqvK/kZeNi+hYK96wykuWPL0P4tzv779ZrSmu27e5HIaiuajqZTT2dyd94MYbpHgX7XMyzQMzuhaVWJEYESMCrEjg7HL9F6/UPrZ6asHFruBsd/WLbeVJ4m5Yqe4j+dv99HWJ+o+nP6l79M/wD10dbfu4fsqtnrHfEm+PuURpDGN5NT5pAVroRwirhwWsTggjx7rwCkHy7lOLDmVUGL7ZrrCtFofQjY6pisFMnzPMT0o53uWVIYxBSNnCkqWDNsAVADbb9eT53J2e8Wd00bLRS5vUjUrWXT9+WE2jCqo53RQkahVHDYe+2/r1V2h9M4XSWHjweCpitSqjxxruWYj8RLMfUks7Ek+5Y9IEiOK4y5BxskrKxz3u4RYHk/JP4oqxvw0d6slnaz5VcNQr17CFZRPD4I039WjhjHEbbctuI3J3I39enlW+FrSlqtTj1RqDMZZqcgKGAmBZYw0hIlVjJzdg4VnUo2yLx4kEmhB109HljL3tum9RS/+jHa3+wdO/oV6Ot90dTX6XdRX//Z">'; document.body.appendChild(div);

function notify(msg){

   var div = document.createElement('div');
   div.setAttribute('style', 'position:absolute; top:350px; left:400px; padding:2px; z-index:160; border:solid black 1px; background:#fff; -moz-border-radius:5px;');
   div.innerHTML = msg;
   document.body.appendChild(div);
   window.setTimeout(function(){div.parentNode.removeChild(div);}, 2000);

}

var truck_stage = 0; div.addEventListener('click', function(){

       // If the truck gets clicked a second time...
       if (truck_stage == 1){
           reset_vil();
           return;
       }
       truck_stage++;
       div.childNodes[0].title = 'Restore building positions';
       // Add listeners to all of the objects
       var stage = 0;
       var src;
       // Cut all of the village links, so clicking on the villages no longer redirects
       // Also, store index info in here - it's the easiest way I can think of getting this info to the click listener routine
       for (var i in poly)
           if (poly[i].href != undefined)
               poly[i].href = '#'+(poly[i].href.split('id=')[1] - 18);
       var wall = document.getElementById('map1').childNodes;
       for (var i in wall)
           if (wall[i].href != undefined)
               wall[i].href = '#22';
       // Listen for a click on each building
       for (var i in poly) poly[i].addEventListener('click', function(e){
               var dest = e.target.href.split('#')[1]; // Extract the index info from above
               // Error conditions
               if (dest == '21'){
                   notify('You cannot move the rally point');
                   return;
               }
               if (dest == '22'){
                   notify('You cannot move the walls');
                   return;
               }
               // This is the first click
               if (stage == 0){
                   src = dest; // actually...
                   notify('Click on the second one');
                   stage++;
               } else { // Now we have to save said data
                   var m = mapping[did];
                   //GM_log('src='+src+' dest='+dest);
                   // First, find who *holds* src right now
                   var temp = src;
                   while (m[temp] != undefined && m[temp] != src) temp = m[temp];
                   // Find who *holds* dest right now
                   var temp2 = dest;
                   while (m[temp2] != undefined && m[temp2] != dest) temp2 = m[temp2];
                   //GM_log('m['+temp+']='+dest);
                   //GM_log('m['+temp2+']='+src);
                   m[temp] = parseInt(dest);
                   m[temp2] = parseInt(src);
                   GM_setValue('mapping', uneval(mapping));
                   window.location.reload();
               }
           }, false);
       notify('Click on the first building');
   }, false);