Enhanced Barter

This userscript aims to enhance your experience at barter.vg

当前为 2017-07-29 提交的版本,查看 最新版本

// ==UserScript==
// @name         Enhanced Barter
// @icon         https://bartervg.com/imgs/ico/barter/favicon-32x32.png
// @namespace    Royalgamer06
// @author       Royalgamer06
// @version      0.9.0
// @description  This userscript aims to enhance your experience at barter.vg
// @include      https://barter.vg/*
// @include      https://www.steamtrades.com/user/*?do=postcomments&message=*
// @connect      steamcommunity.com
// @connect      store.steampowered.com
// @connect      steamtrades.com
// @connect      royalgamer06.ga
// @grant        GM_xmlhttpRequest
// @grant        GM_setClipboard
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        unsafeWindow
// @require      https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js
// ==/UserScript==

this.$ = this.jQuery = jQuery.noConflict(true);
$.fn.serializeObject = function() {
    var o = {};
    var a = this.serializeArray();
    $.each(a, function() {
        if (o[this.name] !== undefined) {
            if (!o[this.name].push) {
                o[this.name] = [o[this.name]];
            }
            o[this.name].push(this.value || "");
        } else {
            o[this.name] = this.value || "";
        }
    });
    return o;
};
$.fn.zip = function(s) {
    var o = $(s);
    return this.map(function(i, e) {
        return $(e).add($(o[i]));
    });
};
var myuid, mysid;
$(document).ready(function() {
    if (location.host == "barter.vg") {
        myuid = $("#signin").attr("href").split("/")[4];
        mysid = $("abbr+ a:has(.icon)").length > 0 ? $("abbr+ a:has(.icon)").attr("href").split("/")[4] : 0;
        unsafeWindow.cancelOffers = cancelOffers;
        unsafeWindow.remindPendingOffers = remindPendingOffers;
        unsafeWindow.remindCompletedOffers = remindCompletedOffers;
        unsafeWindow.showIgnored = fixIgnored;
        unsafeWindow.showIgnored = showIgnored;
        unsafeWindow.findIgnored = findIgnored;
        unsafeWindow.syncLibrary = syncLibrary;
        unsafeWindow.extendExpiry = extendExpiry;
        unsafeWindow.massSendOffers = massSendOffers;
        unsafeWindow.massSendMutualOffers = massSendMutualOffers;
        unsafeWindow.removeOwnedGamesFromPending = removeOwnedGamesFromPending;
        $("[name=offer_message]").removeAttr("maxlength");
        if (location.href.indexOf("/u/") > -1) {
            if (location.href.indexOf("/o/") > -1) {
                if (location.href.indexOf(myuid) > -1 && $("input[value='Completed Offer']").length > 0) {
                    console.log("Found 'Completed Offer'");
                    $("input[value='Completed Offer']").click(function(ev) {
                        ev.preventDefault();
                        var steamid = $("a[href*='/steamcommunity.com/profiles/']:not([href*=" + mysid + "])").attr("href").split("/")[4];
                        var name = $("th:contains('Next Step')+ > a[href*='/steamcommunity.com/profiles/']").text().replace(" on Steam", "");
                        var msg = "+REP " + name + " is an amazing trader, recommended! We successfully completed this trade: " + location.href + ". Thanks a lot for the trade!";
                        leaveFeedback(this, msg, steamid);
                    });
                } else if (location.href.indexOf(myuid) > -1 && $(".activity").length > 0) {
                    $(".activity").first().before("<br><strong>Or...  </strong><input id=automatedoffer type=submit value='Begin Automated Offer'></input>");
                    $("#automatedoffer").click(function() {
                        var offering = prompt("Offering:", "Exact Game Title");
                        var offering_to_group = prompt("Make offer to group:", "wishlist, unowned").split(",").map(Function.prototype.call, String.prototype.trim); //tradable, wishlist, unowned, library, blacklist
                        var want_from_group = prompt("I want:", "wishlist, unowned").split(",").map(Function.prototype.call, String.prototype.trim); //tradable, wishlist, unowned, library
                        var ratio = prompt("Ratio of offering against want:", "all:1");
                        var expire_days = parseInt(prompt("Days until offer expires:", "15"));
                        var trading_cards_only = confirm("Do you only want games with trading cards?");
                        var unbundled_only = confirm("Do you only want unbundled games?");
                        var exclude_givenaway = confirm("Do you want to exclude given away (free) games?");
                        //var exclude_combined = confirm("Do you want to exclude combined games (bundles, packs)?");
                        var exclude_rating_lower_than = parseInt(prompt("Minimum rating:", "0"));
                        if (exclude_rating_lower_than === null || isNaN(exclude_rating_lower_than) || !isFinite(exclude_rating_lower_than)) {
                            exclude_rating_lower_than = false;
                        }
                        var exclude_title_containing = prompt("Exclude games containing the term(s):", "DLC, pack");
                        if (exclude_title_containing !== null && exclude_title_containing !== "") {
                            exclude_title_containing = exclude_title_containing.split(",").map(Function.prototype.call, String.prototype.trim);
                        } else {
                            exclude_title_containing = false;
                        }
                        if (confirm("Are you sure you want to send this automated offer?")) {
                            console.log("Sending mass offers...", offering, offering_to_group, want_from_group, ratio, expire_days, trading_cards_only, unbundled_only, exclude_givenaway, exclude_rating_lower_than, exclude_title_containing);
                            massSendOffers(offering, offering_to_group, want_from_group, ratio, expire_days, trading_cards_only, unbundled_only, exclude_givenaway, exclude_rating_lower_than, exclude_title_containing);
                            alert("Initiated process of sending automated offers!\nCheck the javascript console (F12) for details.\nPlease don't close this tab until all offers have finished sending.");
                        }
                    });
                }
                /*$("#offerHeader > tbody > tr:nth-child(1)").append('<button style="float: right; height: 100%;" id="switchsides">Switch Sides</button>');
                $("#switchsides").click(function() {
                    var rem = $("#main > h1 > a:nth-child(2)").attr("href").split("/")[4];
                    var uids = [];
                    $("#offerHeader > tbody > tr > td:nth-child(2) > strong > a").each(function() {
                        uids.push(this.href.split("/")[4]);
                    });
                    uids.splice($.inArray(rem, uids), 1);
                    var uid = uids[0];
                    console.log(location.href.split(rem)[0] + uid + location.href.split(rem)[1]);
                    //location.href = location.href.split(rem)[0] + uid + location.href.split(rem)[1];
                });*/
                //$("select[name=expire_days] > option[selected=selected]").html("1000");
                [3, 7].forEach(function(i) {
                    $("#exchanges > fieldset:nth-child(" + i + ")").append('<input type="submit" value="Invert all checkboxes" style="width:100%;height:2em;margin-bottom:.7em;" id="checkall' + i + '">');
                    $("#checkall" + i).on("click", function() {
                        $("#exchanges > fieldset:nth-child(" + i + ") input[type=checkbox]").each(function() {
                            this.checked = !this.checked;
                        });
                        return false;
                    });
                    $("#exchanges > fieldset:nth-child(" + i + ")").append('<input type="submit" value="Enable disabled tradables" style="width:100%;height:2em;margin-bottom:.7em;" id="enable' + i + '">');
                    $("#enable" + i).on("click", function(ev) {
                        ev.preventDefault();
                        if (confirm("Are you sure you want to enable disabled tradables? Make sure the other party is okay with this!")) {
                            setTimeout(function() {
                                $("#exchanges > fieldset:nth-child(" + i + ") input[disabled]").each(function() {
                                    this.removeAttribute("disabled");
                                    this.removeAttribute("title");
                                    this.name = "add_to_offer_1[]";
                                    this.id = $(this).find("+").attr("for");
                                    this.value = $(this).parent().find("a").attr("href").split("/")[4] + "," + $(this).find("+").attr("for").replace("edit", "");
                                });
                            }, 0);
                        }
                        $("#enable").hide();
                    });
                });
            } else if ($("#profileStats").length > 0) {
                /*if ($("form[action*='/o/']").length === 0) {
                    var profileid = location.href.split("/")[4];
                    var uid = parseInt(profileid, 16);
                    GM_setValue("ignored", GM_getValue("ignored", "") + profileid + ",");
                    $("#profileStats").after('<form action="/u/' + myuid + '/o/" method="POST" class="inline" style="margin:0em 3em 0 0;"><p><input type="hidden" name="to_user_id" value="' + uid + '"><input type="hidden" name="offer_setup" value="1"><input type="submit" value="Send offer on Barter.vg (Ignored)" id="offerButton"></p></form>');
                }*/
            }
        } else if (location.href.indexOf("/i/") > -1) {
            Array.from(document.getElementById("swishlist").parentNode.getElementsByTagName("a")).forEach(function(a) {
                a.setAttribute("href", a.href.replace("w/m/", "") + "t/f/");
                a.setAttribute("target", "_blank");
                a.onclick = function() {
                    this.parentNode.parentNode.removeChild(this.parentNode);
                };
            });
        }
    } else if (location.host == "www.steamtrades.com" && getURIParam("do") == "postcomments") {
        var steamid = getURIParam("steamid");
        var msg = applySentenceCase(getURIParam("message").replace(/\+/g, ' '));
        var profile_id = $("[name=profile_id]").val();
        var xsrf_token = $("[name=xsrf_token]").val();
        if (profile_id !== undefined) {
            var obj = { do: "review_insert", xsrf_token: xsrf_token, profile_id: profile_id, rating: 1, description: msg };
            console.log(obj);
            $.ajax({
                url: "https://www.steamtrades.com/ajax.php",
                method: "POST",
                data: obj,
                success: function() {
                    GM_setValue("leftfeedback", true);
                }
            });
        } else {
            console.log("Already left feedback previously");
            GM_setValue("leftfeedback", true);
        }
    }
});

function syncLibrary(callback) {
    $.post("/u/" + myuid + "/l/", { sync_list: "↻ Sync List", type: 1 });
    var v = parseInt(GM_getValue("dsudv", 1));
    v++;
    GM_setValue("dsudv", v);
    GM_xmlhttpRequest({
        method: "GET",
        url: "http://store.steampowered.com/dynamicstore/userdata/?v=" + v,
        onload: function(response) {
            var json = JSON.parse(response.responseText);
            var ownedApps = json.rgOwnedApps;
            $.post("/u/" + myuid + "/l/e/", { bulk_AppIDs: ownedApps.join(","), add_AppIDs: "+ Add AppIDs", action: "Edit", change_attempted: 1, add_from: "AppIDs" }, function() {
                if (callback) callback();
            });
        }
    });
}

function fixIgnored() {
    var y = $.unique(GM_getValue("ignored").split(","));
    y.splice( $.inArray(myuid, y), 1 );
    GM_setValue("ignored", y.join(","));
}

function showIgnored() {
    fixIgnored();
    console.log(GM_getValue("ignored").slice(0, -1).split(","));
}

function findIgnored() {
    $.get("/u/" + myuid + "/o/", function(data) {
        data = data.replace(/src="[^"]*"/ig, "");
        var users = [];
        $("[href*='/u/']", data).each(function() {
            let url = this.href;
            if (/^https?:\/\/barter\.vg\/u\/[a-zA-Z0-9]{1,5}\/$/.test(url) && $.inArray(url, users) == -1) {
                $.get(url, function(data) {
                    data = data.replace(/src="[^"]*"/ig, "");
                    var ignored = $.unique(GM_getValue("ignored", "").split(","));
                    if ($("form[action*='/o/']", data).length === 0 && $.inArray(url.split("/")[4], ignored) == -1 && url.split("/")[4] !== myuid) {
                        GM_setValue("ignored", ignored.join(",") + url.split("/")[4] + ",");
                    }
                });
            }
        });
    });
}

function remindPendingOffers() {
    var reminder = "Hey, I saw that you looked at the offer. Would you please be so kind and respond to it as well? I'd appreciate that very much! Thank you!";
    $("tr.active [title*=Opened]+:contains('pending')").each(function() {
        $.post(this.href, { offer_message: reminder, offer_setup: 3 });
    });
    /*var msg = "Hello, I sent you an trade offer on barter. Could you please take a look and respond? I would appreciate that! Thank you!";
    var steamids = [];
    var ajaxDone = 0;
    $.post("/u/" + myuid + "/o/", { filter_by_status: "pending" }, function(data) {
        data = data.replace(/src="[^"]*"/ig, "");
        var count = $(".active:contains('pending')", data).length;
        $($(".active:contains('pending')", data).get().reverse()).each(function() {
            let url = $(this).find("td+ td a").attr("href");
            $.get(url, function(data) {
                data = data.replace(/src="[^"]*"/ig, "");
                var id = $("abbr+ a", data).attr("href").split("/")[4];
                steamids.push(id);
            }).always(function() {
                ajaxDone++;
                if (ajaxDone == count) {
                    postSteamComment(msg, steamids);
                }
            });
        });
    });*/
}

function remindCompletedOffers() {
    var steammsg = "Hello, please mark our trade as complete on barter. I would appreciate that! Thank you!";
    var bartermsg = "Hello, please mark our trade as complete. I would appreciate that! Thank you!";
    var steamids = [];
    var ajaxDone = 0;
    $.post("/u/" + myuid + "/o/", { filter_by_status: "completed" }, function(data) {
        data = data.replace(/src="[^"]*"/ig, "");
        var count = $(".active:contains('completed'):not(.completed)", data).length;
        $($(".active:contains('completed'):not(.completed)", data).get().reverse()).each(function() {
            let url = $(this).find("td+ td a").attr("href");
            $.post(url, { offer_message: bartermsg, offer_setup: 3 });
            /*$.get(url, function(data) {
                data = data.replace(/src="[^"]*"/ig, "");
                var id = $("abbr+ a", data).attr("href").split("/")[4];
                steamids.push(id);
            }).always(function() {
                ajaxDone++;
                if (ajaxDone == count) {
                    postSteamComment(steammsg, steamids);
                }
            });*/
        });
    });
}

function leaveFeedback(elem, msg, steamid) {
    $(elem).val("Completing trade and sending feedback...").attr("disabled", true);
    $.post($(elem).parents("form").attr("action"), $(elem).parents("form").serializeObject());
    var steamids = [steamid];
    //postSteamComment(msg, steamids);
    postSteamTradesComment(msg, steamid, function() {
        setPostTradeClipboard();
        syncLibrary(location.reload);
    });
}

function setPostTradeClipboard() {
    var uids = [];
    $("#offerHeader > tbody > tr > td:nth-child(2) > strong > a").each(function() {
        uids.push(this.href.split("/")[4]);
    });
    uids.splice($.inArray(myuid, uids), 1);
    var uid = uids[0];
    var tradelink = location.href.split(myuid)[0] + uid + location.href.split(myuid)[1];
    var mysteamid = $("#main > aside > div > a:nth-child(8)").attr("href").split("/")[4];
    var sgprofile = "https://www.steamtrades.com/user/" + mysteamid;
    var msg = "Thanks for the trade!\nI completed the trade on barter.vg and left feedback on your steamtrades profile.\nIf you haven't done so already, could you please do the same?\n" + tradelink + "\n" + sgprofile;
    GM_setClipboard(msg);
}

function postSteamTradesComment(msg, steamid, callback) {
    //$("body").append("<iframe src='https://www.steamtrades.com/user/" + steamid + "?do=postcomments&message=" + msg + "' style='display: none;' />");
    window.open("https://www.steamtrades.com/user/" + steamid + "?do=postcomments&message=" + msg, "_blank");
    var check = setInterval(function() {
        if (!!GM_getValue("leftFeedback", false)) {
            GM_etValue("leftFeedback", false);
            removeInterval(check);
            callback();
        }
    }, 10);
}

function massSendOffers(want_name, offering_to_group, want_from_group, ratio, expire_days, trading_cards_only, unbundled_only, exclude_givenaway, exclude_rating_lower_than, exclude_title_containing) {
    if (!want_name) return alert("No argument \"want_name\". Example: \"The Forest\"");
    syncLibrary();
    $.getJSON("/u/" + myuid + "/t/json/", function(mytradables) {
        var offering = false;
        for (var platformid in mytradables.by_platform) {
            for (var tradeid in mytradables.by_platform[platformid]) {
                let tradable = mytradables.by_platform[platformid][tradeid];
                if ((tradable.title ? tradable.title.toLowerCase() === want_name.toLowerCase() : false) ||
                    (tradable.title_alt ? tradable.title_alt.toLowerCase() === want_name.toLowerCase() : false) ||
                    (tradable.title_formatted ? tradable.title_formatted.toLowerCase() === want_name.toLowerCase() : false) ||
                    (tradable.title_extra ? tradable.title_extra.toLowerCase() === want_name.toLowerCase() : false)) {
                    console.log("Tradable:", tradable);
                    offering = tradable.item_id + "," + tradeid;
                    break;
                }
            }
        }
        if (!offering) return alert("Could not find tradable!\nPlease provide a more accurate name of your tradable and make sure this item is in your tradables list.");
        GM_xmlhttpRequest({
            method: "GET",
            url: "https://royalgamer06.ga/barter/json.php",
            onload: function(response) {
                var optins = JSON.parse(response.responseText);
                $.getJSON("/i/" + offering.split(",")[0] + "/json2/", function(gameinfo) {
                    /*
                    TODO:
                     - Add platform exclusion support
                     - Add tag exclusion support
                     - Add tradable-wishlist-ratio support
                     - Add multi-offerings support
                     - Add better UI
                     */

                    //SETUP
                    offering_to_group = offering_to_group ? offering_to_group : ["wishlist"]; //tradable, wishlist, library, blacklist
                    want_from_group = want_from_group ? want_from_group.filter(function(group) { return !!group.toLowerCase().match(/^(tradable|wishlist|unowned|library)$/g); }) : ["wishlist"]; //tradable, wishlist, library, blacklist, unowned
                    ratio = ratio ? ratio.toLowerCase().trim() : "all:1";
                    ratio = ratio.replace(/all/g, "0").split(":");
                    expire_days = expire_days ? expire_days : 1000;
                    trading_cards_only = typeof trading_cards_only !== "undefined" ? trading_cards_only : false;
                    unbundled_only = typeof unbundled_only !== "undefined" ? unbundled_only : false;
                    exclude_givenaway = typeof exclude_givenaway !== "undefined" ? exclude_givenaway : false;
                    //exclude_combined = typeof exclude_combined !== "undefined" ? exclude_combined : false;
                    exclude_rating_lower_than = exclude_rating_lower_than ? exclude_rating_lower_than : 0;
                    exclude_title_containing = exclude_title_containing ? new RegExp(exclude_title_containing.join("|"), "gi") : new RegExp(".^");
                    //EXECUTION
                    offering_to_group.forEach(function(group) {
                        group = group.toLowerCase();
                        for (var userid in gameinfo.users[group]) {
                            let uid = parseInt(userid);
                            let user = gameinfo.users[group][uid];
                            if ((optins.hasOwnProperty(user.steam_id64) ? optins[user.steam_id64] : true) &&
                                user.tradeable_count >= parseInt(ratio[1])) {
                                $.getJSON("/u/" + uid.toString(16) + "/t/f/" + myuid + "/json/", function(tradable_groups) {
                                    $.getJSON("/u/" + uid.toString(16) + "/t/json/", function(tradables) {
                                        var ato2 = [];
                                        for (var platformid in tradables.by_platform) {
                                            for (var tradeid in tradables.by_platform[platformid]) {
                                                let tradable = tradables.by_platform[platformid][tradeid];
                                                if (isInWantGroup(tradable_groups, want_from_group, tradable.item_id) &&
                                                    tradable.extra > 0 &&
                                                    (trading_cards_only ? tradable.cards > 0 : true) &&
                                                    (unbundled_only ? tradable.bundles_all === 0 : true) &&
                                                    (exclude_givenaway ? tradable.givenaway === 0: true) &&
                                                    (tradable.reviews_positive ? tradable.reviews_positive > exclude_rating_lower_than : true) &&
                                                    !((tradable.title ? !!tradable.title.match(exclude_title_containing) : false) ||
                                                      (tradable.title_alt ? !!tradable.title_alt.match(exclude_title_containing) : false) ||
                                                      (tradable.title_formatted ? !!tradable.title_formatted.match(exclude_title_containing) : false) ||
                                                      (tradable.title_extra ? !!tradable.title_extra.match(exclude_title_containing) : false))) {
                                                    ato2.push(tradable.item_id + "," + tradeid);
                                                }
                                            }
                                        }
                                        //console.log(ato2);
                                        if (ato2.length >= parseInt(ratio[1])) {
                                            $.post("/u/" + myuid + "/o/", { to_user_id: uid, offer_setup: 1 }, function(data) {
                                                data = data.replace(/src="[^"]*"/ig, "");
                                                if ($("#offer", data).length > 0) {
                                                    let url = $("#offer", data).attr("action").split("#")[0];
                                                    $.post(url, { offer_setup: [3, 2, 2], "add_to_offer_1[]": offering, "add_to_offer_2[]": ato2, add_to_offer: "+ Add to Offer" }, function() {
                                                        $.post(url, { offer_setup: [3, 2, 2, 3], from_and_or: parseInt(ratio[0]), to_and_or: parseInt(ratio[1]), expire_days: expire_days, counter_preference: 1, propose_offer: "Finish and Propose Offer" });
                                                    });
                                                }
                                            });
                                        }
                                    });
                                });
                            }
                        }
                    });
                });
            }
        });
    });
}

function isInWantGroup(tradable_groups, want_from_group, item_id) {
    var isInWantGroup = false;
    want_from_group.forEach(function(group) {
        if ($.inArray(item_id, tradable_groups[group]) > -1) {
            isInWantGroup = true;
        }
    });
    return isInWantGroup;
}

function massSendMutualOffers(ratio, expire_days, trade_game, only_offer_trade_game) {
    ratio = ratio ? ratio : "all:1";
    ratio = ratio.replace(/all/g, "0").split(":");
    expire_days = expire_days ? expire_days : 1000;
    trade_game = trade_game ? trade_game : 0;
    only_offer_trade_game = only_offer_trade_game ? only_offer_trade_game : false;
    syncLibrary();
    console.log(ratio, expire_days, trade_game, only_offer_trade_game);
    $.post("/u/" + myuid + "/t/m/?", { tradeGame: trade_game, tradeUser: -2, expandAll: "on", openNewTab: "on", findMatches: "Mutual Matches" }, function(data) {
        data = data.replace(/src="[^"]*"/ig, "");
        console.log($("#mutualMatches tr:has([name=offer_setup])", data).zip("#mutualMatches tr:has([href*='/i/'])", data));
        $("#mutualMatches tr:has([name=offer_setup])", data).zip($("#mutualMatches tr:has([href*='/i/'])", data)).each(function() {
            console.log(this);
            let form = $(this).find("form:has([name=offer_setup])");
            console.log(form);
            let offering = [];
            if (only_offer_trade_game) {
                offering.push(trade_game);
            } else {
                $(this).find(".mh > a").each(function() {
                    offering.push(this.href.split("/")[4]);
                });
            }
            console.log(offering);
            let want = [];
            $(this).find(".mw > a").each(function() {
                want.push(this.href.split("/")[4]);
            });
            console.log(want);
            console.log($(form).attr("action"));
            console.log($(form).serializeObject());
            $.post($(form).attr("action"), $(form).serializeObject(), function(data) {
                data = data.replace(/src="[^"]*"/ig, "");
                let url = $("#offerForm", data).attr("action").split("#")[0];
                console.log(url);
                let ato1 = [];
                $(offering).each(function() {
                    ato1.push($("#exchanges > fieldset:nth-child(3) input[value*='" + this + ",']", data).val());
                });
                console.log(ato1);
                let ato2 = [];
                $(want).each(function() {
                    ato2.push($("#exchanges > fieldset:nth-child(7) input[value*='" + this + ",']", data).val());
                });
                console.log(ato2);
                $.post(url, { offer_setup: [3, 2, 2], "add_to_offer_1[]": ato1, "add_to_offer_2[]": ato2, add_to_offer: "+ Add to Offer" }, function() {
                    $.post(url, { offer_setup: [3, 2, 2, 3], from_and_or: parseInt(ratio[0]), to_and_or: parseInt(ratio[1]), expire_days: expire_days, counter_preference: 1, propose_offer: "Finish and Propose Offer" });
                });
            });
        });
    });
}

function cancelOffers(searchquery) {
    $.get("/u/" + myuid + "/o/", function(data) {
        data = data.replace(/src="[^"]*"/ig, "");
        if (!searchquery) return alert("No argument \"searchquery\". Example: \"creating\"");
        $("tr:has(:contains('"  + searchquery + "'))", data).each(function() {
            $.post($(this).find("a[href*='/u/" + myuid + "/o/']")[0].href, { offer_setup: 3, cancel_offer:"☒ Cancel Offer" });
        });
    });
}

function extendExpiry(expire_days) { //Old solution: SHOULD UPDATE THIS!
    $.post("/u/" + myuid + "/o/", { filter_by_status: "pending" }, function(data) {
        data = data.replace(/src="[^"]*"/ig, "");
        $(".active a:contains('pending')", data).each(function() {
            let url = this.href;
            $.post(url, { offer_setup: 3, edit_offer: "✐ Edit Offer" }, function(data) {
                data = data.replace(/src="[^"]*"/ig, "");
                var formdata = $("[name=propose_offer]", data).parents("form").serializeObject();
                formdata.expire_days = expire_days ? expire_days : 1000;
                formdata.propose_offer = "Finish and Propose Offer";
                $.post(url, formdata);
            });
        });
    });
}


function removeOwnedGamesFromPending() {
    syncLibrary(function() {
        $.post("/u/" + myuid + "/o/", { filter_by_status: "pending" }, function(data) {
            data = data.replace(/src="[^"]*"/ig, "");
            $(".active a:contains('pending')", data).each(function() {
                let url = this.href;
                $.post(url, { offer_setup: 3, edit_offer: "✐ Edit Offer" }, function(data) {
                    data = data.replace(/src="[^"]*"/ig, "");
                    var formdata = $("[name=propose_offer]", data).parents("form").serializeObject();
                    to_remove = [];
                    $(".bold+ li , div+ .tradables .tradables_items_list li:nth-child(1)", data).each(function() {
                        if ($(this).find("[alt*=avatar]:first").nextUntil("[alt*=avatar]").text().indexOf("library") > -1) {
                            to_remove.push($(this).find("[name='remove[]']").val());
                        }
                    });
                    if (to_remove.length > 0) {
                        if ($(".bold+ li , div+ .tradables .tradables_items_list li:nth-child(1)", data).length <= to_remove.length) {
                            $.post(url, { offer_setup: 3, cancel_offer:"☒ Cancel Offer" });
                        } else {
                            formdata.remove_offer_items = "➖ Remove Selected Tradables";
                            formdata["remove[]"] = to_remove;
                            $.post(url, formdata, function() {
                                formdata.propose_offer = "Finish and Propose Offer";
                                $.post(url, formdata);
                            });
                        }
                    } else {
                        formdata.propose_offer = "Finish and Propose Offer";
                        $.post(url, formdata);
                    }
                });
            });
        });
    });
}

function getURIParam(sParam) {
    var sPageURL = decodeURIComponent(window.location.search.substring(1)),
        sURLVariables = sPageURL.split("&"),
        sParameterName,
        i;
    for (i = 0; i < sURLVariables.length; i++) {
        sParameterName = sURLVariables[i].split("=");
        if (sParameterName[0].toLowerCase() === sParam) {
            return sParameterName[1] === undefined ? true : sParameterName[1];
        }
    }
    return false;
}

function applySentenceCase(str) {
    return str.replace(/.+?[\.\?\!](\s|$)/g, function (txt) {
        return txt.charAt(0).toUpperCase() + txt.substr(1);
    });
}