DeKleineKobini / TORN: Item Favorite

// ==UserScript==
// @name         TORN: Item Favorite
// @version      1.0.0
// @author       DeKleineKobini
// @description  Favorite items and put them at the top
// @namespace    dekleinekobini.itemvalue
// @match        https://www.torn.com/item.php
// @grant        none
// @license      MIT
// @updateURL    https://openuserjs.org/meta/DeKleineKobini/TORN_Item_Favorite.meta.js
// ==/UserScript==

var logging = {
    enabled: {
        regular: true,
        debug: true
    },
    prefix: {
        regular: "[IF] ",
        debug: "[IF DEBUG] "
    }
}
logging.log = logging.enabled.regular ? function(message) { console.log(logging.prefix.regular + message); } : function(){};
logging.debug = logging.enabled.debug ? function(message) { console.log(logging.prefix.debug + message); } : function(){};
logging.debugObj = logging.enabled.debug ? function(message, obj) { logging.debug(message); console.log(obj); } : function(){};

var favorites = localStorage.getItem("itemfavorite_favorites");
if (favorites) favorites = favorites.split(",");
else favorites = [];

(function(scope) {
    "use strict";

    Number.prototype.format = function(n, x) {
        var re = '\\d(?=(\\d{' + (x || 3) + '})+' + (n > 0 ? '\\.' : '$') + ')';
        return this.toFixed(Math.max(0, ~~n)).replace(new RegExp(re, 'g'), '$&,');
    };

    setupCss(scope);

    new MutationObserver(function(mutations, me) {
        var last = document.getElementsByClassName("last-row")[0];

        if (last) {
            loadData();
            me.disconnect();
        }
    }).observe(document, {
        childList: true,
        subtree: true
    });
})(window);

(function(open) {
    XMLHttpRequest.prototype.open = function() {
        this.addEventListener("readystatechange", function() {
            if (this.readyState > 3 && this.status == 200) {
                var page = this.responseURL.substring(this.responseURL.indexOf("torn.com/") + "torn.com/".length, this.responseURL.indexOf(".php"));

                if (page !== "item" || !isJsonString(this.response)) return;

                var res = JSON.parse(this.response);
                var stripped = stripHtml(res.text);

                setTimeout(loadData, 250)
            }
        }, false);
        open.apply(this, arguments);
    };
})(XMLHttpRequest.prototype.open);

function loadData() {
    logging.log("loadData()");
    var list = $("ul[aria-hidden=false]").find("li[data-item]");
    list.each(function() {
        var favorite =  $(this).find(".actions-wrap > .left").last();

        favorite.html("<span class='icon-h' title='Favorite this Item'>" +
                      "<button aria-label='Favorite Combat Boots' class='favorite-icon-off'></button>" +
                      "<span class='opt-name'>Favorite <span class='t-hide'>this Item</span></span>");
        var favButton = favorite.find("button");
        favButton.addClass(isFavorited($(this)) ? "favorite-icon-on" : "favorite-icon-off");
        favButton.click(function(){
            var favorite = $(this);

            var item = favorite.parents("li").eq(1);
            var wasFavorited = isFavorited(item);


            favorite.toggleClass("favorite-icon-on", !wasFavorited);
            favorite.toggleClass("favorite-icon-off", wasFavorited);
            setFavorited(item, !wasFavorited);
        });
    });
    list.not(".favorite")
        .filter(function() { return isFavorited($(this)); })
        .each(function(index){
        $(this).detach().prependTo("ul[aria-hidden=false]")
        $(this).addClass("favorite");
    });
}

function setupCss(scope) {
    var style = document.createElement('style');
    document.head.appendChild(style);
    style.type = "text/css";
    style.id = "tes";

    var stylesheet = style.sheet;
    scope.css = function(selector, property, value) {
        // Append the rule (Major browsers)
        try {
            stylesheet.insertRule(selector + ' {' + property + ':' + value + '}', stylesheet.cssRules.length);
        } catch (err) {
            try {
                stylesheet.addRule(selector, property + ':' + value); // (pre IE9)
            } catch (err) {
                console.log("Couldn't add style");
            }
        } // (alien browsers)
    }

    css("button[class^=favorite-icon-]", "background", "url(/images/v2/items/icons_dark_bg.png) left top no-repeat");
    css("button[class^=favorite-icon-]", "width", "28px");
    css("button[class^=favorite-icon-]", "height", "32px");
    css("button[class^=favorite-icon-]", "display", "block");
    css("button[class^=favorite-icon-]", "margin-left", "-1px");
    css("button[class^=favorite-icon-]", "vertical-align", "middle");
    css("button[class^=favorite-icon-]", "border", "0px solid transparent");

    css(".favorite-icon-off", "background-position", "-36px -783px !important");
    css(".favorite-icon-on", "background-position", "-2px -783px !important");

}

function isFavorited(element) {
    return favorites.includes(element.attr("data-rowkey"));
}

function setFavorited(element, val) {
    let armoryId = element.attr("data-armoryid");

    if (armoryId) {
        _setFavorited("u" + armoryId, val);
        _setFavorited("g" + element.attr("data-item"), val);
    } else {
        _setFavorited(element.attr("data-rowkey"), val);
    }
}

function _setFavorited(key, val) {
    logging.debugObj("old: " , favorites)
    logging.debug("set: " + key + " = " + val)
    if (val) {
        favorites.push(key);
    } else {
        let index = favorites.indexOf(key);
        if (index > -1) favorites.splice(index, 1)
    }
    localStorage.setItem("itemfavorite_favorites", favorites);
}



function formatCurrency(value, decimals, currency) {
    if (!decimals) decimals = 0;
    if (!currency) currency = "$";

    return currency + value.format(decimals);
}

function isJsonString(str) {
    try {
        JSON.parse(str);
    } catch (e) {
        return false;
    }
    return true;
}

function stripHtml(html) {
    var tmp = document.createElement("div");
    tmp.innerHTML = html;
    var stripped = tmp.textContent || tmp.innerText || "";

    stripped = replaceAll(stripped, "\n", "");
    stripped = replaceAll(stripped, "\t", "");
    stripped = replaceAll(stripped, "  ", " ");

    return stripped;
}

function replaceAll(str, text, replace) {
    while (contains(str, text)) {
        str = str.replace(text, replace);
    }
    return str;
}

function contains(str, text) {
    return str.indexOf(text) >= 0;
}