KoLE2-alpha

Misc enhancements for kingdom of loathing

目前為 2014-09-14 提交的版本,檢視 最新版本

// ==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 &lt;searchterm&gt;</code>", "Search Coldfront KoL wiki"],
                    ["<code>/qs [amount] &lt;itemname&gt;</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);

})();