NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Steam Wishlist Sorter // @namespace SWS // @version 1.0.2 // @description Lets you sort your Steam wishlist by comparing two games at a time. // @author Anxeal // @license MIT // @downloadURL https://github.com/Anxeal/SteamWishlistSorter/raw/master/SWS.user.js // @updateURL https://github.com/Anxeal/SteamWishlistSorter/raw/master/SWS.meta.js // @match https://store.steampowered.com/wishlist/* // @grant GM_addStyle // @grant GM_setValue // @grant GM_getValue // @grant GM.addStyle // @grant GM.setValue // @grant GM.getValue // @require https://openuserjs.org/src/libs/sizzle/gm4-polyfill.js // @require https://code.jquery.com/jquery-3.3.1.min.js // ==/UserScript== GM.addStyle(` .sws-overlay { display:flex; flex-direction:column; justify-content:center; align-items:center; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,.7); z-index:9999; } .sws-close-button { position:fixed; top:20px; right:40px; font-size:80px; cursor:pointer; text-align:center; } .sws-choice-button { box-shadow: 0px 0px 0px 5px #ccc; margin: 0.5em; width: 292px; height: 136px; cursor: pointer; transition: transform .3s ease, box-shadow .3s ease; } .sws-choice-button:hover { transform: scale(1.2); box-shadow: 0px 0px 0px 5px #09c; } .sws-choice-button:active { transform: none; } .sws-app-title-text { font-size: 20px; line-height: 60px; } .sws-sort-button { display:flex; justify-content:center; align-items:center; margin-left:15px; } .sws-progress-outer { border:5px solid #069; border-radius: 20px; background:#999; width:800px; height:20px; margin:30px; box-shadow: inset 0 0 5px #000; } .sws-progress-inner { border-radius: 10px; background-image: linear-gradient( -45deg, #09c 25%, #0cf 25%, #0cf 50%, #09c 50%, #09c 75%, #0cf 75%, #0cf ); background-size: 20px 20px; animation:sws-progress 1s linear 0s infinite; height:100%; width:0; transition: width .5s ease-out; } @keyframes sws-progress { to { background-position: 0 20px; } } `); (function($, window) { 'use strict'; // Class that does merge sort with manual comparisons from an older project class ManualSorter { constructor(array, $leftButton, $rightButton, callback) { var self = this; $leftButton.click(function() { if ($(this).is("[disabled]")) return; self.compare(-1); self.sendNext(); }); $rightButton.click(function() { if ($(this).is('[disabled]')) return; self.compare(1); self.sendNext(); }); this.arr = this.shuffleArray(array.slice()); this.step = 1; this.index = 0; this.done = false; this.compCount = 0; // approx max comp count this.maxCompCount = this.arr.length * Math.ceil(Math.log2(this.arr.length)); this.cleanVars(); this.callback = callback; this.sendNext(); } sendNext(){ this.callback(this.getNext()); } cleanVars() { this.headLeft = 0; this.headRight = 0; this.result = []; } compare(input) { if (this.done) return; var rightLimit = Math.min(this.step, this.arr.length-(this.index+1)*this.step); if (this.headLeft < this.step && this.headRight < rightLimit) { if (input < 0) { this.pushLeft(); } else { this.pushRight(); } } if (!(this.headLeft < this.step && this.headRight < rightLimit)) { while (this.headLeft < this.step) { this.pushLeft(); } while (this.headRight < rightLimit) { this.pushRight(); } for (var i = 0; i < this.result.length; i++) { this.arr[this.index * this.step + i] = this.result[i]; } this.index += 2; if ((this.index + 1) * this.step + this.headRight >= this.arr.length) { this.step *= 2; this.index = 0; } if (this.step >= this.arr.length) { // We are done sorting this.done = true; } this.cleanVars(); } } pushLeft() { this.result.push(this.arr[this.index * this.step + this.headLeft]); this.headLeft++; this.compCount++; } pushRight() { this.result.push(this.arr[(this.index + 1) * this.step + this.headRight]); this.headRight++; this.compCount++; } getNext() { if (!this.done) { return { left: this.arr[this.index * this.step + this.headLeft], right: this.arr[(this.index + 1) * this.step + this.headRight], done: false}; } else { console.log("[SWS] Done sorting!"); return { result: this.arr, done: true}; } } shuffleArray(a) { var j, x, i; for (i = a.length - 1; i > 0; i--) { j = Math.floor(Math.random() * (i + 1)); x = a[i]; a[i] = a[j]; a[j] = x; } return a; } serialize() { return JSON.stringify(this); } deserialize(json) { var obj = JSON.parse(json); console.log(obj); for(var val in obj) { this[val] = obj[val]; } } get progress(){ return this.compCount/this.maxCompCount*100; } }; var waitForWishlist = $.Deferred(); waitForWishlist.then(function(){(async function() { // g_bCanEdit => if wishlist is editable // if it isn't our wishlist, don't bother running if(!window.g_bCanEdit){ console.log("[SWS] Can't edit wishlist: Stopping."); return; } var wl = window.g_Wishlist; var $overlay = $("<div class='sws-overlay'></div>"); var $closeButton = $("<a class='sws-close-button'>×</a>"); var $appTitleText = $("<div class='sws-app-title-text '></div>"); var $leftButton = $("<div class='sws-choice-button'></div>"); var $rightButton = $leftButton.clone(); var $progress = $("<div class='sws-progress-outer'><div class='sws-progress-inner'></div></div>"); $(document.body).append($overlay); $overlay.append($closeButton).append($leftButton).append($appTitleText).append($rightButton).append($progress); $overlay.hide(); $('.sws-choice-button').hover(function(){ $appTitleText.text($(this).attr("data-app-title")).stop().animate({ opacity: 1 }, 200); }, function(){ $appTitleText.stop().animate({ opacity: 0 }, 200); }).on("mousedown", function(e){ if(e.which == 2) { // middleclick window.open('https://store.steampowered.com/app/'+$(this).attr("data-app-id")+'/', '_blank'); } }); // close button behavior $closeButton.click(function(){ $overlay.fadeOut(); }); // main buttons var $sortButton = $("<div class='sws-sort-button'><div class='btnv6_blue_hoverfade btn_medium'><span>Sort!</span></div></div>"); var $saveButton = $sortButton.clone(); var $discardButton = $sortButton.clone(); var $saveProgressButton = $sortButton.clone(); var $loadProgressButton = $sortButton.clone(); $sortButton.appendTo(".wishlist_header").children().click(function(){ $overlay.fadeIn(); $discardButton.fadeIn(); $saveProgressButton.fadeIn(); }); $sortButton.hide().fadeIn(); $saveButton.hide().children().children().text("Save"); $saveButton.appendTo(".wishlist_header").children().click(function(){ wl.SaveOrder(); location.reload(); }); $discardButton.hide().children().children().text("Discard"); $discardButton.appendTo(".wishlist_header").children().click(function(){ location.reload(); }); $saveProgressButton.hide().children().children().text("Save Progress"); $saveProgressButton.appendTo(".wishlist_header").children().click(function(){(async function() { var data = sorter.serialize(); await GM.setValue("sws-sorter-data", data); alert("Progress Saved!"); })();}); $loadProgressButton.hide().children().children().text("Load Progress"); $loadProgressButton.appendTo(".wishlist_header").children().click(function(){(async function(){ var data = await GM.getValue("sws-sorter-data"); sorter.deserialize(data); sorter.sendNext(); $sortButton.children().click(); })();}); if(await GM.getValue("sws-sorter-data")) $loadProgressButton.fadeIn(); var fadeDuration = 100; var setChoiceData = function($button, side){ var bgUrl = "url('"+window.g_rgAppInfo[side].capsule+"')"; if($button.attr("data-app-id") == side){ $button.attr("disabled", "disabled") .delay(2*fadeDuration) .removeAttr("disabled"); return; } $button.attr("disabled", "disabled").fadeOut(fadeDuration, function(){ $button.css("background-image", bgUrl); $button.attr("data-app-id", side); $button.attr("data-app-title", window.g_rgAppInfo[side].name); $button.trigger("mouseenter"); }).fadeIn(fadeDuration, function(){ $button.removeAttr("disabled"); }); } var sorter = new ManualSorter(wl.rgAllApps, $leftButton, $rightButton, function(next){ if (next.done) { wl.rgAllApps = next.result; wl.rgFilterSettings = { sort: "order", last_sort: "order", type: "all", view: wl.rgFilterSettings.view } wl.Update(); $overlay.fadeOut(); $saveButton.fadeIn(); alert("Done sorting! Please review your wishlist and save or discard."); } else { setChoiceData($leftButton, next.left); setChoiceData($rightButton, next.right); if(sorter) $progress.children().width((sorter.progress)+"%"); } }); })()}); var checkWishlist = function(){ if(!window.g_Wishlist || !window.g_Wishlist.rgAllApps){ console.log("[SWS] Waiting for wishlist..."); setTimeout(checkWishlist, 500); return; } console.log("[SWS] Wishlist loaded."); waitForWishlist.resolve(window.g_Wishlist); }; checkWishlist(); })(unsafeWindow.jQuery, unsafeWindow);