// ==UserScript==
// @name KoLE2-alpha
// @namespace fnoot/kol/kole2-alpha
// @description Misc enhancements for kingdom of loathing
// @include http://www.kingdomofloathing.com/*
// @version 0.1
// @grant none
// ==/UserScript==
(function() {
var saveSettings = function() {
localStorage.kole2Settings = JSON.stringify(top.kole.userSettings);
};
// used by getSetting() when called before top.kole is initialised
var userSettingsPreload = typeof localStorage.kole2Settings == "undefined" ? {} : JSON.parse(localStorage.kole2Settings);
var controlTypes = {
input: function(name, spec, parent) {
var input = crel("input", {}, parent);
input.id = "kole_config_" + name;
var setValue = function() {
if (spec.onchange) spec.onchange(input.value);
top.kole.userSettings[name] = input.value;
saveSettings();
};
input.value = getSetting(name);
input.onkeyup = setValue;
input.onpaste = setValue;
},
yesno: function(name, spec, parent) {
var select = crel("select", {}, parent);
select.id = "kole_config_" + name;
with(crel("option", {}, select)) {
innerHTML = "Yes";
value = 1;
}
with(crel("option", {}, select)) {
innerHTML = "No";
value = 0;
}
select.selectedIndex = getSetting(name) ? 0 : 1;
select.onchange = function() {
top.kole.userSettings[name] = this.selectedIndex == 0;
saveSettings();
};
},
check: function(name, spec, parent) {
var cbox = crel("input", {}, parent);
cbox.checked = getSetting(name);
cbox.id = "kole_config_" + name;
cbox.type = "checkbox";
cbox.onclick = function() {
top.kole.userSettings[name] = this.checked;
saveSettings();
if (spec.onchange) spec.onchange();
}
},
spin: function(name, spec, parent) {
var min = spec.range[0],
max = spec.range[1];
var value = getSetting(name);
var displayCallback = spec.displayCallback || function(v) {
return v;
};
var downButton = crel("button", {}, parent);
downButton.innerHTML = "<";
var valueSpan = crel("span", {
display: "inline-block",
"margin": "0 6px",
"width": "50px",
"text-align": "center"
}, parent);
var fixPrecision = function() {
value = Math.round(value * 100) / 100;
}
valueSpan.innerHTML = displayCallback(value);
var upButton = crel("button", {}, parent);
upButton.innerHTML = ">";
downButton.onclick = function() {
value -= spec.step;
fixPrecision();
if (value < min) value = min;
valueSpan.innerHTML = displayCallback(value);
top.kole.userSettings[name] = value;
if (spec.onchange) spec.onchange(value);
saveSettings();
};
upButton.onclick = function() {
value += spec.step;
fixPrecision();
if (value > max) value = max;
valueSpan.innerHTML = displayCallback(value);
top.kole.userSettings[name] = value;
if (spec.onchange) spec.onchange(value);
saveSettings();
};
},
button: function(name, spec, parent) {
with(crel("a", {}, parent)) {
onclick = function() {
spec.onclick();
return false;
};
href = "#" + name;
innerHTML = spec.buttonCaption;
}
},
raw: function(name, spec, parent) {
parent.innerHTML = spec.html;
}
};
var getSetting = function(name) {
var source = typeof top.kole == "undefined" || top.kole == null || typeof top.kole.userSettings == "undefined" ? userSettingsPreload : top.kole.userSettings;
return typeof source[name] == "undefined" ? configOptions[name].default : source[name];
};
var itemLookup = function(fuzzyName) {
if (!top.kole) return null;
var matches = [];
var item = null;
for (var name in top.kole.itemIds) {
if (name.trim().toUpperCase().indexOf(fuzzyName.toUpperCase()) > -1) {
if (name.toLowerCase() == fuzzyName.toLowerCase()) {
return {
name: name,
id: top.kole.itemIds[name]
};
}
matches.push({
name: name,
id: top.kole.itemIds[name]
});
}
}
if (matches.length > 1) {
return {
error: "Multiple matches for \"" + fuzzyName + "\". Please be more specific."
};
} else if (matches.length == 1) {
return matches[0];
}
return null;
};
var configOptions = {
hoverHints: {
default: true,
control: "check",
caption: "Hover hints",
description: "Shows information about an item/effect/icon when the mouse pointer hovers over it"
},
hintDelay: {
default: 230,
caption: "Hint delay (milliseconds)",
description: "Specifies how long you need to hover over an item/effect/icon before its description is shown",
control: "spin",
range: [0, 2000],
step: 25
},
darkness: {
default: 0.35,
caption: "Darkness",
description: "Darkens the whole game; great for headaches and photosensitives!",
control: "spin",
range: [0, 0.8],
step: 0.1,
onchange: function(value) {
top.kole.setDarkness(value);
},
displayCallback: function(value) {
return ((value / 0.8) * 100).toFixed(1).replace(/(\d+)\.0+$/, '$1') + "%";
}
},
stayLoggedIn: {
default: true,
caption: "Stay logged in",
description: "Defeats the timeout that logs you out after an idle period by sending a dummy request every two minutes",
control: "check",
onchange: function(value) {
if (value) xhr("main.php", function() {}, false);
}
},
nullifySword: {
default: true,
caption: "Fix prepositions",
description: "Undoes the effect of the Sword",
control: "check"
},
autoFight: {
default: false,
caption: "Automatic fighting",
description: "Always clicks the last item in the combat bar or \"Adventure again\" when available; requires combat bar enabled in KoL options",
control: "check"
},
autoFightDelay: {
default: 4000,
caption: "Automatic fighting delay",
description: "Defines how long to wait before taking an automatic action in a fight",
control: "spin",
range: [1000, 10000],
step: 500,
displayCallback: function(v) {
return (v / 1000) + "sec";
}
},
chatHelp: {
caption: "Chat commands",
buttonCaption: "Show",
"description": "Shows a list of chat commands added by KoLE",
"control": "button",
onclick: function() {
var pop = crel("div", {
padding: "12px"
});
crel("h1", {
margin: "0 0 12px 0",
"font-weight": "100"
}, pop).innerHTML = "KoLE Chat Commands";
crel("p", {
"font-size": "small"
}, pop).innerHTML = "These commands require <i>Modern</i> chat version selected in <a href='account.php?tab=chat'>KoL options</a>";
pop.appendChild(tabulate([
["<code>/wiki <searchterm></code>", "Search Coldfront KoL wiki"],
["<code>/qs [amount] <itemname></code>", "Quicksell item"]
], "rgba(0,0,0,0.06"));
poop(pop);
}
},
donate: {
caption: "Appreciation",
control: "raw",
description: "I am poorly and well below the poverty line. Has this been useful?",
html: '<form target="_blank" action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top" style="margin:none;padding:none;display:inline">' +
'<input type="hidden" name="cmd" value="_s-xclick">' +
'<input type="hidden" name="hosted_button_id" value="G33Q3HVDX4G3Y">' +
'<input type="submit" value="Show via PayPal" src="https://www.paypalobjects.com/en_GB/i/btn/btn_donate_SM.gif" border="0" name="submit">' +
'<img alt="" border="0" src="https://www.paypalobjects.com/en_GB/i/scr/pixel.gif" width="1" height="1">' +
'</form>'
}
};
var prepositions = ["about", "above", "across", "after", "against", "along", "among", "around", "at", "before",
"behind", "below", "beneath", "beside", "between", "beyond", "by", "down", "during", "except",
"for", "from", "in", "inside", "into", "like", "near", "of", "off", "on", "onto", "out", "outside",
"over", "past", "through", "throughout", "to", "under", "up", "upon", "with", "within", "without"
];
var nullifySword = function(s) {
for (var i = 0; i < prepositions.length; i++) {
var prep = prepositions[i];
var search = new RegExp(" " + prep, "g");
s = s.replace(search, "\x09" + prep);
var search = new RegExp(prep + " ", "g");
s = s.replace(search, prep + "\x09");
}
return s;
};
var tabulate = function(data, altColour) {
var table = crel("table", {
width: "100%",
"font-size": "small"
});
table.setAttribute("cellspacing", 0);
table.setAttribute("cellpadding", 4);
var tb = crel("tbody", {}, table);
if (data.length == 0) return table;
var alt = false;
for (var i = 0; i < data.length; i++) {
var row = data[i];
var tr = crel("tr", {}, tb);
if (alt) tr.style.background = altColour;
alt = !alt;
for (var x = 0; x < row.length; x++) {
var td = crel("td", {}, tr);
td.innerHTML = row[x];
}
}
return table;
};
var applyStyles = function(el, styles) {
for (var k in styles) {
el.style.setProperty(k, styles[k]);
//lazily prepend browser-specific prefixes
el.style.setProperty("-moz-" + k, styles[k]);
el.style.setProperty("-webkit-" + k, styles[k]);
}
};
// document.createElement > applyStyles > parent.append shortcut
var crel = function(tag, styles, parent) {
var el = document.createElement(tag);
if (typeof styles != "undefined") applyStyles(el, styles);
if (typeof parent != "undefined") parent.appendChild(el);
return el;
};
var elX = function(el) {
return el.offsetParent ? elX(el.offsetParent) + el.offsetLeft : el.offsetLeft;
};
var elY = function(el) {
return el.offsetParent ? elY(el.offsetParent) + el.offsetTop : el.offsetTop;
};
var darkCover = crel("div", {
"position": "fixed",
"top": "0",
"left": "0",
"right": "0",
"bottom": "0",
"background": "#000",
"opacity": 0,
"pointer-events": "none",
"z-index": 999999,
}, document.body);
var hintBox = crel("div", {
"position": "absolute",
"width": "260px",
"padding": "12px 0px",
"border": "1px #bbb solid",
"border-radius": "5px",
"box-shadow": "2px 2px 3px rgba(0,0,0,0.4)",
"pointer-events": "none",
"background": "#fff",
"opacity": 0,
"transform": "scale(0.1,0.1)",
"transition": "100ms opacity ease-out, 100ms transform ease-out, 100ms -moz-transform ease-out, 100ms -webkit-transform ease-out",
"z-index": 999998,
"font-size": "small"
}, document.body);
setTimeout(function() {
darkCover.style.transition = "600ms opacity";
});
var initialHintWidth = hintBox.clientWidth;
var showHint = function(forEl, html) {
forX = elX(forEl);
forY = elY(forEl);
hintBox.innerHTML = html;
hintBox.style.width = initialHintWidth + "px";
hintBox.style.left = forX + forEl.clientWidth + 12 + "px";
if ((elX(hintBox) + hintBox.clientWidth) > document.body.clientWidth) {
hintBox.style.width = document.body.clientWidth - elX(hintBox) + "px";
}
// hintBox.style.left = (forX+forEl.clientWidth+12+hintBox.clientWidth) > document.body.clientWidth
// ? forX - (hintBox.clientWidth +12)+"px"
// : forX +forEl.clientWidth +12+"px";
hintBox.style.top = (forY + (hintBox.clientHeight * 1.5)) > document.body.scrollHeight ? document.body.scrollHeight - (hintBox.clientHeight * 1.5) + "px" : forY + "px";
crel("div", {
background: "rgba(100, 150, 255,0.04)",
"position": "absolute",
"box-shadow": "0 0 22px rgba(100, 150, 255,0.09)",
"top": "42px",
"left": "0",
"right": "0",
"bottom": "0"
}, hintBox);
applyStyles(hintBox, {
opacity: 1,
transform: "scale(1,1)"
});
hintElement = forEl;
};
var hintTimer = null;
var hintElement = null;
var xhr = function(url, callback, cached) {
if (cached && typeof top.kole.xhrCache[url] != "undefined") {
setTimeout(function() {
callback(top.kole.xhrCache[url]);
}, 0);
return {
cancel: function() {}
};
};
var canceled = false;
var req = new XMLHttpRequest();
var stateChange = function() {
if (this.readyState == 4) {
top.kole.xhrCache[url] = this.responseText;
if (!canceled)
callback(this.responseText);
req.removeEventListener("readystatechange", stateChange);
}
};
req.addEventListener("readystatechange", stateChange, false);
req.open("GET", url, true);
req.send();
return {
cancel: function() {
canceled = true;
}
}
};
var extractDescription = function(url, callback, cached) {
return xhr(url, function(response) {
var tempEl = crel("div");
tempEl.innerHTML = response;
var scripts = tempEl.querySelectorAll("script");
for (var i = scripts.length - 1; i >= 0; i--) scripts[i].parentNode.removeChild(scripts[i]);
callback(tempEl.querySelectorAll("#description")[0].innerHTML);
}, cached);
};
var cancelLastHintCallback = function() {};
var cancelHint = function() {
cancelLastHintCallback();
if (hintTimer !== null) clearTimeout(hintTimer);
applyStyles(hintBox, {
opacity: 0,
transform: "scale(0.9,0.9)"
});
};
// htmlCallback(done(html)) should return {cancel:function(){...}}
var setHintTimer = function(el, htmlCallback, ___args) {
cancelHint();
// show when both timer AND callback async complete
var asyncRemaining = 2;
var hintHtml = "";
var asyncDone = function() {
asyncRemaining--;
if (asyncRemaining == 0) {
showHint(el, hintHtml);
}
};
hintTimer = setTimeout(asyncDone, getSetting("hintDelay"));
var cancelLastHintCallback = htmlCallback(function(html) {
hintHtml = html;
asyncDone();
}).cancel;
};
if (window == top) {
var whenReady = function(cb) {
for (var i = 0; i < frames.length; i++)
if (typeof frames[i].kole == "undefined") {
setTimeout(function() {
whenReady(cb);
}, 200);
return;
}
cb();
}
if (typeof this.kole != "undefined") {
alert("A browser plugin or KoL update is conflicting with KoLE");
return;
}
window.kole = null;
whenReady(function() {
window.kole = {
setDarkness: function(darkness) {
for (var i = 0; i < frames.length; i++) {
frames[i].window.kole.setDarkness(darkness);
}
},
itemIds: typeof localStorage.itemIds == "undefined" ? {} : JSON.parse(localStorage.itemIds),
xhrCache: {},
userSettings: userSettingsPreload
};
});
} else {
window.kole = {
setDarkness: function(darkness) {
darkCover.style.opacity = darkness + 0.00001; // fixes gre render bug
},
top: top.kole
};
kole.setDarkness(getSetting("darkness"));
}
var timedClick = function(el, delay, cancelCaption, oncancel) {
var cancelButton = crel("button", {
background: "#fff",
border: "2px #000 solid",
transition: delay + "ms box-shadow linear",
"box-shadow": "inset 0 0 0 rgba(255,0,0,0.9)",
position: "fixed",
bottom: 0,
right: 0,
padding: "4px",
width: "200px"
}, document.body);
applyStyles(el, {
transition: delay + "ms box-shadow linear, " + delay + "ms border-color linear",
"box-shadow": "inset 0 0 0 rgba(0,255,0,0.5)",
"border-color": "rgba(0,255,0,0)"
});
cancelButton.innerHTML = cancelCaption;
var timer = setTimeout(function() {
el.click();
}, delay + 100);
setTimeout(function() {
cancelButton.style.boxShadow = "inset 208px 0 0 rgba(255,0,0,1)";
el.style.boxShadow = "inset " + el.scrollWidth + "px 0 0 rgba(0,255,0,0.3)";
el.style.borderColor = "rgba(0,255,0,1)";
}, 100);
window.CLICKEL = el;
cancelButton.onclick = function() {
if (oncancel) oncancel();
clearTimeout(timer);
document.body.removeChild(cancelButton);
};
};
if (window.name == "mainpane") {
var openPanel = function() {
koleButton.disabled = true;
var panel = crel("div", {
"position": "fixed",
"top": "0",
"left": "0",
"right": "0",
"max-height": "100%",
"box-shadow": "0 0 8px rgba(0,0,0,0.6)",
"border-bottom": "1px #888 solid",
"transform": "scale(0.1,0.1)",
"transform-origin": "100% 0",
"padding": "12px",
"overflow": "auto",
"opacity": 0,
"z-index": 9001,
background: "#eef",
"transition": "220ms all ease-in"
}, document.body);
crel("h1", {
"font-weight": "100"
}, panel).innerHTML = "KoLE Settings";
var settingsTable = crel("table", {
"font-size": "small",
"width": "100%",
}, panel);
settingsTable.setAttribute("cellpadding", 4);
settingsTable.setAttribute("cellspacing", 0);
var tbody = crel("tbody", {}, settingsTable);
var alt = false;
for (var name in configOptions) {
alt = !alt;
var spec = configOptions[name];
var tr = crel("tr", {
background: alt ? "rgba(255,255,255,0.5)" : "transparent"
}, tbody);
var labelCell = crel("td", {
height: "2em",
"width": "200px",
"vertical-align": "middle"
}, tr);
var label = crel("label", {}, labelCell);
label.innerHTML = spec.caption;
label.setAttribute("for", "kole_config_" + name);
var editCell = crel("td", {
"vertical-align": "middle"
}, tr);
controlTypes[spec.control](name, spec, editCell);
if (spec.description) {
var descTr = crel("tr", {
"font-size": "0.8em",
"color": "#666",
background: alt ? "rgba(255,255,255,0.5)" : "transparent"
}, tbody);
var descTd = crel("td", {}, descTr);
descTd.innerHTML = spec.description;
descTd.setAttribute("colspan", 2);
}
}
with(crel("p", {
"font-size": "small"
}, panel)) {
innerHTML = "Kingdom of Loathing Enhancement <b>alpha</b> by <a href='showplayer.php?who=2362564'>fnoot</a><br>This is an <b>alpha testing</b> version; please report any problems by kmail.";
}
var closeButton = crel("button", {
position: "absolute",
top: 0,
right: 0
}, panel);
closeButton.innerHTML = "X";
closeButton.onclick = function() {
koleButton.disabled = false;
applyStyles(panel, {
opacity: 0,
"pointer-events": "none",
transform: "scale(0.2,0.2)"
});
setTimeout(function() {
document.body.removeChild(panel);
panel = null;
}, 2000);
};
setTimeout(function() {
applyStyles(panel, {
opacity: 1,
transform: "scale(1,1)"
});
}, 100)
};
var koleButton = crel("button", {
"position": "fixed",
"top": "0",
"right": "0",
"z-index": 9000
}, document.body);
with(koleButton) {
innerHTML = "KoLE";
onclick = openPanel;
}
if (getSetting("autoFight")) {
(function() { // auto fighting
var links = document.querySelectorAll("a");
var adventureAgainRegex = /Adventure Again \(/;
for (var i = 0; i < links.length; i++) {
if (adventureAgainRegex.test(links[i].innerHTML)) {
timedClick(links[i], getSetting("autoFightDelay"), "Cancel automatic fighting", function() {
top.kole.userSettings.autoFight = false;
saveSettings();
});
return;
}
}
var button12 = document.getElementById("button12");
if (button12 != null) {
timedClick(button12, getSetting("autoFightDelay"), "Cancel automatic fighting", function() {
top.kole.userSettings.autoFight = false;
saveSettings();
});
}
})();
}
setInterval(function() {
if (getSetting("stayLoggedIn")) {
xhr("main.php", function() {}, false);
}
}, 1000 * 60 * 2);
var scanItems = function() {
if (top.kole == null) return;
var itemIds = top.kole.itemIds;
var learnedItems = false;
var itemCells = document.querySelectorAll(".stuffbox table.item .ircm");
for (var i = 0; i < itemCells.length; i++) {
var cell = itemCells[i];
var itemName = cell.innerHTML;
if (typeof itemIds[itemName] == "undefined") {
learnedItems = true;
itemIds[itemName] = cell.parentNode.id.replace(/i/, '');
// console.log("Learned item '"+itemName +"' id: "+itemIds[itemName]);
}
}
if (learnedItems) localStorage.itemIds = JSON.stringify(itemIds);
};
setInterval(scanItems, 3500);
} // /mainpane
if (window.name == "chatpane") {
var chatLog = function(s) {
handleMessage({
type: 'event',
msg: "<span style='color:#08c'><span style='opacity:0.5'>[</span>kole<span style='opacity:0.5'>]</span></span> " + s
});
};
var chatCommands = {
wiki: function(args) {
window.open("http://kol.coldfront.net/thekolwiki/index.php/Special:Search?search=" + encodeURIComponent(args.trim()) + "&go=Go");
},
qs: function(args) {
args = args.trim();
var amountMatches = args.match(/(\d+|\*)\s+([\w\s]+)/);
if (amountMatches && amountMatches.length > 1) {
var amount = amountMatches[1];
args = amountMatches[2];
} else {
var amount = 1;
}
var item = itemLookup(args);
if (item === null) {
chatLog("KoLE doesn't know that item! Teach it by opening your inventory.");
} else if (item.error) {
chatLog("" + item.error);
} else {
chatLog("Quickselling " + amount + " x " + item.name);
dojax("sellstuff.php?action=sell&ajax=1&type=quant&whichitem%5B%5D=" + item.id + "&howmany=" + amount + "&pwd=" + pwdhash);
}
}
}
var previousOnload = window.onload;
window.onload = function() {
if (previousOnload) previousOnload.apply(this, arguments);
var oldForm = document.getElementById('InputForm');
if (oldForm == null) return;
oldForm = oldForm.parentNode;
newForm = oldForm.cloneNode(true);
newForm.style.background = "red";
oldForm.parentNode.replaceChild(newForm, oldForm);
var old$inp = $inp;
$inp = $$("#graf");
newForm.onsubmit = function(ev) {
ev.preventDefault();
var inp = $inp.val();
var matches = inp.match(/^\/(\w+)(\s+(.*))?/);
if (matches && matches.length > 0) {
var cmd = matches[1];
if (typeof chatCommands[cmd] != "undefined") {
$inp.val("");
chatCommands[cmd].call(this, matches.length > 1 ? matches[2] : undefined);
return;
}
}
old$inp.val(getSetting("nullifySword") ? nullifySword($inp.val()) : $inp.val());
$inp.val("");
submitchat(ev);
};
};
};
var poop = function(htmlOrElement) {
var cover = crel("div", {
position: "fixed",
top: 0,
left: 0,
right: 0,
bottom: 0,
background: "rgba(0,0,0,0.2)",
opacity: 0,
transition: "200ms all",
"z-index": 9002
});
var win = crel("div", {
width: "420px",
margin: "9% auto 0 auto",
background: "#fff",
position: "relative",
transform: "scale(3,3)",
opacity: 0,
transition: "300ms all",
"box-shadow": "1px 1px 6px rgba(0,0,0,0.4)"
}, cover);
var winner = crel("div", {
padding: "12px"
}, win);
if (typeof htmlOrElement == "string") {
winner.innerHTML = htmlOrElement;
} else {
winner.appendChild(htmlOrElement)
}
with(crel("button", {
position: "absolute",
top: 0,
right: 0
}, win)) {
onclick = function() {
cover.style.opacity = 0;
applyStyles(win, {
"transform": "scale(3,3)",
opacity: 0
});
setTimeout(function() {
document.body.removeChild(cover);
}, 600);
};
innerHTML = "X";
}
document.body.appendChild(cover);
setTimeout(function() {
cover.style.opacity = 1;
applyStyles(win, {
"transform": "scale(1,1)",
opacity: 1
});
}, 100);
};
var applyHoverHints = function() {
var els = document.querySelectorAll("a,img");
var onclickRegex = /\b(descitem|eff|javascript:poop)\("?([\w\.\?\=]+)"?\b(\s*,\s*(\w+)\b\))?/;
for (var i = 0; i < els.length; i++) {
var funcUrls = {
descitem: "desc_item.php?whichitem=",
eff: "desc_effect.php?whicheffect=",
"javascript:poop": ""
};
var onclick = els[i].getAttribute("onclick") + "";
var matches = onclick.match(onclickRegex);
if (onclick && matches && (matches.length > 0)) {
if (typeof els[i]['@kole2_hoverhint_init'] == "undefined") {
els[i]['@kole2_hoverhint_init'] = true;
els[i].style.cursor = "help";
els[i].title = "";
with({
func: matches[1],
itemId: matches[2],
otherPlayer: matches[4]
}) {
els[i].addEventListener("mouseenter", function() {
cancelHint();
if (!getSetting("hoverHints")) return;
hintElement = this;
var query = typeof otherPlayer == "undefined" ? itemId : itemId + "&otherplayer=" + otherPlayer;
setHintTimer(this, function(callback) {
return extractDescription(funcUrls[func] + query, callback, true);
});
}, false);
els[i].addEventListener("mouseout", function() {
if (this == hintElement) cancelHint();
}, false);
} // with
} // if not init
} // if onclick match
} // for i in els
}; // applyHoverHints()
applyHoverHints();
setInterval(applyHoverHints, 2500);
})();