Ikariam City Select Reorder-er
— Last update 17 hours ago — Installed 28,077 times.There are 13 previous versions of this script.
Add Syntax Highlighting (this will take a few seconds, probably freezing your browser while it works)
var scriptMetadata = parseMetadata(<><![CDATA[
// ==UserScript==
// @name Ikariam City Select Reorder-er
// @author overkill
// @namespace overkill_gm
// @version 3.6
// @description Lets you reorder your cities in the drop down
// @homepage http://userscripts.org/scripts/show/27630
// @include http://*.ikariam.*/*
// @exclude http://board.ikariam.*
// ==/UserScript==
]]></>.toString());
function parseMetadata(a){var b=a.split(/[\r\n]+/).filter(/\/\/ @/);var c={include:[],exclude:[]};for each(var d in b){[d,name,value]=d.match(/\/\/ @(\S+)\s*(.*)/);if(c[name]instanceof Array)c[name].push(value);else c[name]=value}return c}
function debug(aMsg) { setTimeout(function() { throw new Error("[debug] " + aMsg); }, 0);}
function stripHTML(s){ return s.replace(/<[^>]*>/g, ""); }
function onClick(node, fn, capture, e) { node.addEventListener((e||"") + "click", fn, !!capture); }
function node(type, className, styles, content) {
var n = document.createElement(type||"div");
if (className) n.className = className;
if (styles) for (var prop in styles) n.style[prop] = styles[prop];
if (content) n.innerHTML = "string" == typeof content ? content : content.toXMLString();
return n;
}
function $(id) { return document.getElementById(id); }
function $x( xpath, root ) {
var doc = root ? root.evaluate ? root : root.ownerDocument : document, next;
var got = doc.evaluate( xpath, root||doc, null, 0, null ), result = [];
switch (got.resultType) {
case got.STRING_TYPE:
return got.stringValue;
case got.NUMBER_TYPE:
return got.numberValue;
case got.BOOLEAN_TYPE:
return got.booleanValue;
default:
while (next = got.iterateNext())
result.push( next );
return result;
}
}
function $X( xpath, root ) { var got = $x( xpath, root ); return got instanceof Array ? got[0] : got; }
function trim(str) {
str = str.replace(/^\s\s*/, '');
var ws = /\s/;
var i = str.length;
while (ws.test(str.charAt(--i)));
return str.slice(0, i + 1);
}
function in_array(needle, haystack) { for (var key in haystack) if (haystack[key] == needle) return true; return false; }
function duration(seconds){
var x = [Math.floor(seconds / 86400) , Math.floor(seconds/3600) % 24 , Math.floor(seconds/60) % 60 , Math.round(seconds % 60) ];
var y = ['d' , 'h' , 'm' , 's'];
var r = [];
for (var i = 0; i < x.length; ++i){ if (x[i] > 0) { r.push(x[i].toString() + y[i]); } }
return r.join(' ');
}
function distance(r1,r2){
if (r1 && r2 && (typeof r1 == "string") && (typeof r2 == "string")) { // uhm, has to be a better way of doing this
r1=r1.split(':');
r2=r2.split(':');
if (r1.length && r2.length) return Math.round(Math.sqrt(Math.pow(r1[0]-r2[0],2) + Math.pow(r1[1]-r2[1],2))*100)/100;
}
return false;
}
function getListOfCities(){
var city,currentCityList = [];
for each(city in $x("./option",$('citySelect'))) currentCityList.push(parseInt(city.value,10));
return currentCityList;
}
function generateSelfCache(){
var cache = {};
for each (var town in $x('./option',$('citySelect'))){
if (town.innerHTML.charAt(0) == '[') { //todo: move this if statement outside the loop
cache[town.value] = {
'name' : town.innerHTML.replace(/\[[0-9:\s]+\]\s+/,''),
'position' : town.innerHTML.match(/\[([0-9:\s]+)\]/,'')[1].replace(/00/g,'100'),
'tradegood': town.title.substring(12)
};
} else {
var coords = town.title;
coords = coords.substr(1,coords.length-2);
cache[town.value] = {
'name':town.innerHTML,
'position':coords,
'tradegood':unsafeWindow.LocalizationStrings['resources'][town.className.charAt(town.className.indexOf('tradegood')+9)]
};
}
}
return cache;
}
function writeCitySelectHTML(){
var optionParent = $('citySelect');
var optionElems = $x('./option',optionParent);
var liParent = optionParent.previousSibling.childNodes[1];
var liElems = liParent.getElementsByTagName('li');
var oldOptions = {};
var oldLis = {};
var n_cities = optionElems.length;
var id, newLi;
for (var i = n_cities-1; i >= 0; --i) {
oldOptions[optionElems[i].value] = optionElems[i].cloneNode(true);
newLi = liElems[i].cloneNode(true);
newLi.className = newLi.className.replace(/( first)|( last)|( active)/g,'');
oldLis[optionElems[i].value] = newLi;
optionParent.removeChild(optionElems[i]);
liParent.removeChild(liElems[i]);
}
for (i = 0; i < n_cities; ++i) {
id = myCityList[i];
optionParent.appendChild(oldOptions[id]);
newLi = oldLis[id];
if (id == currentCityId) { newLi.className += ' active'; }
if (i == 0) { newLi.className += ' first'; }
if (i == n_cities-1) { newLi.className += ' last'; }
onClick(newLi, selectCity, false);
liParent.appendChild(newLi);
}
}
// called when a new city is chosen. rewrites the citySelect form and submits it
function selectCity(event){
var optionParent = $('citySelect');
var optionElems = $x('./option',optionParent);
var liParent = optionParent.previousSibling.childNodes[1];
var liElems = liParent.getElementsByTagName('li');
var n_cities = optionElems.length;
var x = 0;
while ((x < n_cities) && (this != liElems[x])) { ++x; }
if (x >= n_cities) {
alert("An error has occured. Please Disable this script and contact the author.");
return;
}
//debug(this.parentNode.parentNode.parentNode.parentNode.parentNode);
for (var i = n_cities-1; i >= 0 ; --i) {
optionElems[i].selected = i == x;
}
//debug(active.className);
var active = optionParent.previousSibling.childNodes[0];
var newClass = liElems[x].className.match(/tradegood./);
if (newClass) { active.className = "avatarCities "+newClass[0]+" dropbutton"; }
active.innerHTML = liElems[x].innerHTML;
// don't bother changing title
var form = this.parentNode.parentNode.parentNode.parentNode.parentNode; // errr... this is silly =P
form.submit();
}
///////////////////////////////////////// START OPTIONS
function moveUp(){
var clicked = this.parentNode;
clicked.parentNode.insertBefore(clicked,clicked.previousSibling);
saveListFromOption();
}
function moveDown(){
var clicked = this.parentNode;
clicked.parentNode.insertBefore(clicked,clicked.nextSibling.nextSibling);
saveListFromOption();
}
// will add content to the OPTIONS page in place-th place (optional) and encapsulate it in a div. returns the DIV element of the options
function addOptionsToPage(content,place){
place = place || 5; // place must be > 0 and a number
var parentElem = $('options_changePass').parentNode; // an arbitrary element with an id was chosen
return parentElem.insertBefore(node("div", "", { textAlign: "center" },content),$X('./div['+place+']',parentElem));
}
function showOptions(){
var element, id;
var opts = '<h3>Reorder Towns v'+scriptMetadata['version']+' (by <a href="http://userscripts.org/users/53907">Overkill</a>)</h3><i>(will automatically save, no need to hit the "Save settings!" button)</i><br /><ul id="city_reorder" style="width:300px; margin:3px auto;">';
for (var i = 0; i < myCityList.length ; ++i) {
id = myCityList[i];
opts += '<li id="reorder'+ myCityList[i] +'"><span>' + selfCache[id].name + '</span><span class="ok_up">▲</span><span class="ok_down">▼</span></li>';
}
opts += '</ul>Put Transport Helper<select>';
opts += '<option' + ((transporterContainer == '') ? ' selected' : '') + ' value="">disabled</option>';
opts += '<option' + ((transporterContainer == 'globalResources') ? ' selected' : '') + ' value="globalResources">top</option>';
opts += '<option' + ((transporterContainer == 'mainview') ? ' selected' : '') + ' value="mainview">bottom</option>';
opts += '<option' + ((transporterContainer == 'Overkill Bar') ? ' selected' : '') + '>Overkill Bar</option>';
opts += '</select>';
var newDiv = addOptionsToPage(opts,3);
for each (element in $x('.//span[@class="ok_up"]' ,newDiv)){ onClick(element,moveUp); }
for each (element in $x('.//span[@class="ok_down"]',newDiv)){ onClick(element,moveDown); }
//onClick($X('.//select',newDiv),saveListFromOption);
$X('.//select',newDiv).addEventListener("change", saveListFromOption, false);
GM_addStyle('#city_reorder span:first-child { display:inline-block; width: 200px; }');
GM_addStyle('#city_reorder span + span { cursor: pointer; }');
}
function saveListFromOption(){
//debug("saving list");
myCityList = [];
var li = $x("./li",$('city_reorder'));
for (var i = 0; i < li.length; i++)
myCityList.push(parseInt(li[i].id.replace(/^reorder/,''),10));
//debug('saving ' + uneval(myCityList));
//debug('saving use_bar ' + $X('..//select',$('city_reorder')).value);
GM_setValue('cities_'+server,uneval(myCityList));
GM_setValue('use_bar',$X('..//select',$('city_reorder')).value);
}
///////////////////////////////////////// END OPTIONS
//rearrange Balances page
function reorderBalances(){
var cities = $x(".//tr",$('balance'));
var n = cities.length;
var row, rows = {};
var cityName, id, p;
if ((n > 3) && (n == myCityList.length+3)) {
var resultRow = cities[n-2];
p = cities[0].parentNode;
for (var i = n-3; i > 0; --i) {
cityName = trim(cities[i].getElementsByTagName('td')[0].innerHTML);
if (!rows[cityName]) rows[cityName] = p.removeChild(cities[i]); // if multiple cities have the same name, ignore after the 1st city
}
for (i = 0; i < myCityList.length; ++i) {
if (row = rows[selfCache[myCityList[i]].name]){
row.className = (i % 2)? "alt" : "";
p.insertBefore(row,resultRow);
}
}
}
}
function writeTransportBoxHTML(){
var output = '', id, d, selected, dOutput;
for (var i = 0,n = myCityList.length; i < n; ++i) {
id = myCityList[i];
selected = (id == currentCityId) ? ' selected ' : '';
dOutput = ((d = distance(selfCache[currentCityId].position,selfCache[id].position)) !== false) ? ' (' + duration(Math.max(1200*d,600)) + ')' : '';
output += '<option value="'+id+'"' + selected + '>' + selfCache[id].name + dOutput + '</option>';
}
var html = "<form method='get' action='index.php' style='float:right;'><select id='overkillTransport' name='destinationCityId' onchange='this.parentNode.submit()'>"+output+"</select><input type=\"hidden\" name=\"view\" value=\"transport\" /></form>";
switch (transporterContainer) {
case 'mainview' :
GM_addStyle("#overkillTransport { background:wheat; font-size:8pt; border: 0 none; margin:0; padding:0; height:15px; }");
GM_addStyle("#overkillTransport option { padding: 0; }");
$(transporterContainer).appendChild(node('div','',{position:'absolute'},html));
break;
case 'globalResources' :
GM_addStyle("#overkillTransport { background:wheat; font-size:8pt; border: 0 none; margin:0; padding:0; height:15px; }");
GM_addStyle("#overkillTransport option { padding: 0; }");
$(transporterContainer).appendChild(node('div','',{position:'absolute',top:'-11px'},html));
break;
case 'Overkill Bar' :
GM_addStyle("#overkillTransport { font-size:8pt; border: 0 none; margin: 0 0 0 4px; padding:0; height:15px; }");
GM_addStyle("#overkillTransport option { padding: 0; }");
var barTab = overkillBar(html+'Transport');
overkillBar_add(barTab,'<a href="'+scriptMetadata['homepage']+'">Version: '+scriptMetadata['version']+'</a>');
overkillBar_add(barTab,'<a href="/index.php?view=options#city_reorder">Change Options</a>');
break;
}
}
if ($('servertime') && unsafeWindow.IKARIAM){
// global variabless
var server = document.location.toString().split('/')[2];
var currentCityId = parseInt($X('//select[@id="citySelect"]/option[@selected]').value,10);
var selfCache = generateSelfCache();
var myCityList = eval(GM_getValue('cities_'+server,uneval(getListOfCities()))); // load saved order
var transporterContainer = GM_getValue('use_bar','globalResources');
var change = false;
for (var i = myCityList.length-1; i>=0; --i) {
if (!selfCache[myCityList[i]]) { myCityList.splice(i,1); change = true; }
}
for (var id in selfCache) {
id = parseInt(id,10);
if (!in_array(id,myCityList)) { myCityList.push(id); change = true; }
}
if (change) GM_setValue('cities_'+server,uneval(myCityList));
writeCitySelectHTML();
if (document.body.id == 'finances') reorderBalances();
else if (document.body.id == 'options') showOptions();
if (transporterContainer) writeTransportBoxHTML();
}
/*
OVERKILL BAR -- add everything below this to script to enable the overkill bar. Add a new tab and initialize the bar with
var tab = overkillBar('tab title');
*/
function overkillBar(title){
if (!$('overkillBar')){
var div = node('div','','','<ul></ul>');
div.id = "overkillBar";
document.body.appendChild(node('div','',{"height":"3em;"},' '));
document.body.appendChild(div);
var ul = $X('./ul',div);
ul.appendChild(node('li','','','<h2 style="font-weight:bold;">OVERKILL</h2><ul><li><a href="http://userscripts.org/users/53907/scripts">Homepage</a></li></ul>'));
//test
//styleBar();
//production
GM_addStyle('#overkillBar ul{margin:0;padding:0;border:0;list-style-type:none;display:block}#overkillBar ul li{margin:0;padding:0;border:0;display:block;float:left;position:relative;z-index:5}#overkillBar ul li:hover{z-index:10000}#overkillBar ul li li{float:none}#overkillBar ul ul{visibility:hidden;position:absolute;z-index:10;left:0;bottom:0}#overkillBar ul li:hover>ul{visibility:visible;bottom:100%}#overkillBar ul li li:hover>ul{bottom:0;left:100%}#overkillBar ul:after,#overkillBar ul ul:after{content:".";height:0;display:block;visibility:hidden;overflow:hidden;clear:both}#overkillBar ul ul{ background:none;padding:30px 30px 10px 30px;margin:0 0 -10px -30px}#overkillBar ul ul ul{padding:30px 30px 30px 10px;margin:0 0 -30px -10px}#overkillBar{ position:fixed; bottom:0px; width:100%; z-Index:10}#overkillBar h2{ font-size:14px}#overkillBar ul{color:#eee;background:#234}#overkillBar ul ul li{color:#eee;background:#234; font-size:smaller}#overkillBar ul ul{width:15em}#overkillBar ul a{text-decoration:none;color:#eee;padding:.4em 1em;display:block;position:relative}#overkillBar ul a:hover,#overkillBar ul li:hover>a{color:#fc3}#overkillBar ul li{ padding:3px; white-space:nowrap}#overkillBar ul li{border:1px solid #ccc}#overkillBar ul>li+li{border-left:0}#overkillBar ul ul>li+li{border-left:1px solid}#overkillBar ul ul>li+li{border-top:0}#overkillBar ul li li:hover>ul{bottom:5px;left:90%}#overkillBar em{ font-style:italic}');
}
var ul = $X('./ul',$('overkillBar'));
var tab = ul.appendChild(node('li','','','<h2>'+title+'</h2>'));
return tab.appendChild(node('ul','','',''));
}
function overkillBar_add(barTab,content){
return barTab.appendChild(node('li','','',"string" == typeof content ? content : content.toXMLString()));
}
function overkillBarOptions(barTab){
var gameServer = document.domain.replace(/ikariam\./,'');
this.save = function(){
var copy = {};
for (thing in this)
if ((typeof this[thing] !== 'function') && (typeof this[thing] !== 'object'))
copy[thing] = this[thing];
//debug("save : "+uneval(copy));
GM_setValue('_okbarOptions_'+gameServer,uneval(copy));
}
this.setTab = function(barTab){
this.barTab = barTab;
}
this.addInput = function(label,defaultValue){
var options = this;
var labelName = stripHTML(label);
//debug(label + ' ' + labelName);
if (!options[labelName]) { options[labelName] = defaultValue || ''; }
var ctrl = options.barTab.appendChild(node('li','',{cursor:'pointer'},label+': '+(options[labelName]?options[labelName]:'not set')));
onClick(ctrl,function(){
var newValue = prompt('New '+labelName,options[labelName]);
if (newValue !== null) options[labelName] = newValue;
this.innerHTML = label+': '+(options[labelName]?options[labelName]:'not set');
options.save();
});
}
this.addCB = function(label,defaultValue){
var options = this;
var labelName = trim(stripHTML(label));
//debug(label + ' ' + labelName);
if (typeof options[labelName] != 'boolean') { options[labelName] = !!defaultValue; }
var ctrl = options.barTab.appendChild(node('li','',{cursor:'pointer'},label+' '+(options[labelName]?'✓':'')));
onClick(ctrl,function(){
options[labelName] = !options[labelName];
this.innerHTML = label+' '+(options[labelName]?'✓':'');
options.save();
});
}
this.barTab = barTab;
var options = eval(GM_getValue('_okbarOptions_'+gameServer),{});
for (thing in options) this[thing] = options[thing];
}
function styleBar(){
GM_addStyle(
"" + <><![CDATA[
/* inspired by http://aplus.rs/adxmenu/examples/hbt/ test with this CSS then compress http://www.cssdrive.com/compressor/compress.php */
/* - - - ADxMenu: BASIC styles - - - */
/* remove all list stylings */
#overkillBar ul { margin: 0; padding: 0; border: 0; list-style-type: none; display: block; }
#overkillBar ul li {
margin: 0;
padding: 0;
border: 0;
display: block;
float: left; /* move all main list items into one row, by floating them */
position: relative; /* position each LI, thus creating potential IE.win overlap problem */
z-index: 5; /* thus we need to apply explicit z-index here... */
}
#overkillBar ul li:hover {
z-index: 10000; /* ...and here. this makes sure active item is always above anything else in the menu */
/*white-space: normal;/* required to resolve IE7 :hover bug (z-index above is ignored if this is not present)
see http://www.tanfa.co.uk/css/articles/pure-css-popups-bug.asp for other stuff that work */
}
#overkillBar ul li li {
float: none;/* items of the nested menus are kept on separate lines */
}
#overkillBar ul ul {
visibility: hidden; /* initially hide all submenus. */
position: absolute;
z-index: 10;
left: 0; /* while hidden, always keep them at the bottom left corner, */
bottom: 0; /* to avoid scrollbars as much as possible */
}
#overkillBar ul li:hover>ul {
visibility: visible; /* display submenu them on hover */
bottom: 100%; /* 1st level go above their parent item */
}
#overkillBar ul li li:hover>ul { /* 2nd+ levels go on the right side of the parent item */
bottom: 0;
left: 100%;
}
/* -- float.clear --
force containment of floated LIs inside of UL */
#overkillBar ul:after, #overkillBar ul ul:after {
content: ".";
height: 0;
display: block;
visibility: hidden;
overflow: hidden;
clear: both;
}
/* -- float.clear.END -- */
/* sticky submenu: it should not disappear when your mouse moves a bit outside the submenu
YOU SHOULD NOT STYLE the background of the "#overkillBar UL" or this feature may not work properly!
if you do it, make sure you 110% know what you do */
#overkillBar ul ul {
background: none;
padding: 30px 30px 10px 30px;
margin: 0 0 -10px -30px;
}
#overkillBar ul ul ul {
padding: 30px 30px 30px 10px;
margin: 0 0 -30px -10px;
}
/* - - - ADxMenu: DESIGN styles - - - */
#overkillBar {
position:fixed;
bottom: 0px;
width: 100%;
z-Index: 10;
}
#overkillBar h2 {
font-size: 14px;
}
#overkillBar ul {
color: #eee;
background: #234;
}
#overkillBar ul ul li {
color: #eee;
background: #234;
font-size: smaller;
}
#overkillBar ul ul {
width: 15em;
}
#overkillBar ul a {
text-decoration: none;
color: #eee;
padding: .4em 1em;
display: block;
position: relative;
}
#overkillBar ul a:hover, #overkillBar ul li:hover>a {
color: #fc3;
}
#overkillBar ul li {
padding: 3px;
white-space: nowrap;
}
#overkillBar ul li { /* create borders around each item */
border: 1px solid #ccc;
}
#overkillBar ul>li + li { /* and remove the left border on all but first item in the list level 1 */
border-left: 0;
}
#overkillBar ul ul>li + li { /* restore border remove the left border on all but first item in the list level 1 */
border-left: 1px solid #ccc;
}
#overkillBar ul ul>li + li { /* and remove the top border on all but first item in the list level 2*/
border-top: 0;
}
#overkillBar ul li li:hover>ul { /* inset 2nd+ submenus, to show off overlapping */
bottom: 5px;
left: 90%;
}
#overkillBar em { font-style : italic; }
]]></>);
}
