Last active
March 9, 2022 12:56
-
-
Save selwynsimsek/2cecf8cbe5fe32f17eebe2d341a9acfa to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name torn-treemap | |
// @version 8 | |
// @require http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js | |
// @require https://d3js.org/d3.v4.js | |
// @include https://www.torn.com/item.php | |
// @grant GM.setValue | |
// @grant GM.getValue | |
// @author selwyn [2093198] | |
// @description Plots a treemap of the user's inventory by total item market value on the items page. | |
// ==/UserScript== | |
function addHTML(tornAPI, visible_p) { | |
// | |
$(".abc").remove(); | |
$(".def").remove(); | |
$(".ghi").remove(); | |
// Add some styling for the tooltip that appears when one mouses over the treemap | |
var style = document.createElement('style'); | |
style.setAttribute('class','ghi'); | |
style.innerHTML = ` | |
.toolTip { | |
position: absolute; | |
display: none; | |
width: auto; | |
height: auto; | |
background: none repeat scroll 0 0 white; | |
border: 0 none; | |
border-radius: 8px 8px 8px 8px; | |
box-shadow: -3px 3px 15px #888888; | |
color: black; | |
font: 12px sans-serif; | |
padding: 5px; | |
text-align: center; | |
} | |
`; | |
document.head.appendChild(style); | |
// Create the two divs that store the header and treemap respectively | |
const link_div = document.createElement('div'); | |
link_div.className = 'def'; | |
const div = document.createElement('div'); | |
div.className = 'abc'; | |
// Insert the divs in the document | |
$(div).insertAfter($(".equipped-items-wrap")); | |
$(link_div).insertAfter($(".equipped-items-wrap")) | |
// Establish the scale of the treemap | |
div_width = div.clientWidth | |
var margin = { | |
top: 0, | |
right: 0, | |
bottom: 0, | |
left: 0 | |
}, | |
width = div_width - margin.left - margin.right, | |
height = div_width - margin.top - margin.bottom; | |
$(window).on('resize',function () { | |
if(div_width!=div.clientWidth){ | |
//alert("div_width has changed"); | |
$(window).off('resize') | |
addHTML(tornAPI,visible_p) | |
} | |
}); | |
var largest_val = -1; | |
// Create the header, which includes a horizontal divider and a show/hide button in the right of the panel | |
if (visible_p) { | |
link_div.innerHTML = ` | |
<hr class="delimiter-999 m-top10 m-bottom10"> | |
<div class="items-wrap"><header class="header___3DTGA"><p class="title___1xdgX" role="heading" aria-level="2" tabindex="0">Treemap</p><nav class="icons___3yDrV"></button><button id="treemapbutton" type="button" class="button___1h1Bp" aria-label="Collapse"><svg xmlns="http://www.w3.org/2000/svg" class="default___25YWq " fill="#999" stroke="#fff" stroke-width="0" width="16" height="11" viewBox="0 0 16 11"><path d="M1302,21l-5,5V16Z" transform="translate(29 -1294) rotate(90)"></path></svg></button></nav></header> | |
</div>`; | |
} else { | |
link_div.innerHTML = `<hr class="delimiter-999 m-top10 m-bottom10"> | |
<div class="items-wrap"><header class="header___3DTGA"><p class="title___1xdgX" role="heading" aria-level="2" tabindex="0">Treemap</p><nav class="icons___3yDrV"></button><button id="treemapbutton" class="button___1h1Bp" aria-label="Collapse"><svg xmlns="http://www.w3.org/2000/svg" class="default___25YWq " fill="#999" stroke="#fff" stroke-width="0" width="11" height="16" viewBox="0 0 11 16"><path d="M1302,21l-5,5V16Z" transform="translate(-1294 -13)"></path></svg> | |
</button></nav></header> | |
</div> | |
`; | |
$(".abc").hide(); | |
} | |
// Toggle the visibility of the treemap, and state of the toggle button, when the toggle button is clicked. | |
$('#treemapbutton').click(function(e) { | |
if ($(".abc").is(':visible')) { | |
$(".abc").hide(); | |
$("#treemapbutton").html(` | |
<svg xmlns="http://www.w3.org/2000/svg" class="default___25YWq " fill="#999" stroke="#fff" stroke-width="0" width="11" height="16" viewBox="0 0 11 16"><path d="M1302,21l-5,5V16Z" transform="translate(-1294 -13)"></path></svg> | |
`); | |
(async () => { | |
await GM.setValue('visiblep', 0); | |
})(); | |
} else { | |
$(".abc").show(); | |
$("#treemapbutton").html(` | |
<svg xmlns="http://www.w3.org/2000/svg" class="default___25YWq " fill="#999" stroke="#fff" stroke-width="0" width="16" height="11" viewBox="0 0 16 11"><path d="M1302,21l-5,5V16Z" transform="translate(29 -1294) rotate(90)"></path></svg> | |
`); | |
(async () => { | |
await GM.setValue('visiblep', 1); | |
})(); | |
} | |
}); | |
// append the svg object to the body of the page | |
var svg = d3.select(".abc") | |
.append("svg") | |
.attr("width", width + margin.left + margin.right) | |
.attr("height", height + margin.top + margin.bottom) | |
.append("g") | |
.attr("transform", | |
"translate(" + margin.left + "," + margin.top + ")"); | |
// Make the API call | |
d3.json("https://api.torn.com/user/?selections=inventory&key=" + tornAPI, function(data) { | |
newdata = { | |
"children": [] | |
} | |
names = ["Book", "Energy Drink", "Booster", "Alcohol", "Artifact", "Melee", "Flower", | |
"Enhancer", "Medical", "Jewelry", "Virus", "Temporary", "Special", "Clothing", | |
"Secondary", "Drug", "Supply Pack", "Plushie", "Defensive", "Electronic", "Primary", | |
"Candy", "Other", "Cars", "Collectibles" | |
] | |
// Refactor the JSON from the API call into a form suitable for d3 to process. | |
for (name in names) { | |
newdata["children"].push({ | |
"name": names[name], | |
"children": [] | |
}) | |
} | |
for (item in data['inventory']) { | |
itemval = data['inventory'][item]; | |
itemname = (itemval['name']) | |
itemvalue = (itemval['market_price'] * itemval['quantity']) | |
largest_val = Math.max(itemvalue, largest_val); | |
itemcategory = itemval['type'] | |
itemindex = names.indexOf(itemcategory) | |
newdata["children"][itemindex]["children"].push({ | |
"name": itemname, | |
"quantity": itemval['quantity'], | |
"price": itemval['market_price'], | |
"value": itemvalue | |
}); | |
} | |
// Give the data to this cluster layout: | |
var root = d3.hierarchy(newdata).sum(function(d) { | |
return d.value | |
}) // Here the size of each leaf is given in the 'value' field in input data | |
// Create a currency formatter for rendering money values | |
var formatter = new Intl.NumberFormat('en-US', { | |
style: 'currency', | |
currency: 'USD', | |
maximumFractionDigits: 0, | |
}); | |
// prepare a color scale | |
var color = d3.scaleOrdinal() | |
.domain(names) | |
.range(["#003f5c", "#2f4b7c", "#665191", "#a05195", "#d45087", "#f95d6a", "#ff7c43", "#ffa600", "#003f5c", "#2f4b7c", "#665191", "#a05195", "#d45087", "#f95d6a", "#ff7c43", "#ffa600", "#003f5c", "#2f4b7c", "#665191", "#a05195", "#d45087", "#f95d6a", "#ff7c43", "#ffa600", "#003f5c", "#2f4b7c", "#665191", "#a05195", "#d45087", "#f95d6a", "#ff7c43", "#ffa600"]) | |
// And a opacity scale | |
var opacity = d3.scaleLinear() | |
.domain([0, largest_val * 0.8]) | |
.range([0.5, 1.0]) | |
// Then d3.treemap computes the position of each element of the hierarchy | |
d3.treemap() | |
.size([width, height]) | |
.padding(0) | |
(root) | |
// Create a tooltip | |
var tool = d3.select("body").append("div").attr("class", "toolTip"); | |
// Each rectangle, clipPath, and text goes in a cell | |
var cell = svg.selectAll("g") | |
.data(root.leaves()) | |
.enter().append("g") | |
.attr("transform", function(d) { | |
return "translate(" + d.x0 + "," + d.y0 + ")"; | |
}); | |
// use this information to add rectangles: | |
cell | |
.append("rect") | |
.attr("id", function(d) { | |
return "" + d.data.name.replace(/\W/g, ''); | |
}) | |
.attr('width', function(d) { | |
return d.x1 - d.x0; | |
}) | |
.attr('height', function(d) { | |
return d.y1 - d.y0; | |
}) | |
.style("stroke", "black") | |
.style("fill", function(d) { | |
return color(d.parent.data.name) | |
}) | |
.style("opacity", function(d) { | |
return opacity(d.data.value) | |
}) | |
// Show/hide a tooltip on mousemove/mouseout events | |
.on("mousemove", function(d) { | |
tool.style("left", d3.event.pageX + 10 + "px") | |
tool.style("top", d3.event.pageY - 20 + "px") | |
tool.style("display", "inline-block"); | |
tool.html(d.data.name + "<br>" + "x" + d.data.quantity + "<br>" + formatter.format(d.data.value)); | |
}) | |
.on("mouseout", function(d) { | |
tool.style("display", "none"); | |
}) | |
// Add a clipPath to clip labels inside their rectangles | |
cell.append("clipPath") | |
.attr("id", function(d) { | |
return "clip-" + d.data.name.replace(/\W/g, '') | |
}) | |
.append("use") | |
.attr("xlink:href", function(d) { | |
return "#" + d.data.name.replace(/\W/g, '') | |
}); | |
// add first label | |
cell | |
.append("text") | |
.attr("x", function(d) { | |
return 4 | |
}) | |
.attr("y", function(d) { | |
return 13 | |
}) | |
.text(function(d) { | |
return d.data.name | |
}) | |
.attr("font-size", "12px") | |
.attr("fill", "black") | |
.attr("clip-path", function(d) { | |
return "url(#clip-" + d.data.name.replace(/\W/g, '') + ")"; | |
}) | |
.style("opacity", function(d) { | |
return opacity(d.data.value) | |
}).on("mousemove", function(d) { | |
tool.style("left", d3.event.pageX + 10 + "px") | |
tool.style("top", d3.event.pageY - 20 + "px") | |
tool.style("display", "inline-block"); | |
tool.html(d.data.name + "<br>" + "x" + d.data.quantity + "<br>" + formatter.format(d.data.value)); | |
}) | |
.on("mouseout", function(d) { | |
tool.style("display", "none"); | |
}) | |
// add second label | |
cell | |
.append("text") | |
.attr("x", function(d) { | |
return 4 | |
}) | |
.attr("y", function(d) { | |
return 26 | |
}) | |
.text(function(d) { | |
return "x" + d.data.quantity | |
}) | |
.attr("font-size", "12px") | |
.attr("fill", "black") | |
.attr("clip-path", function(d) { | |
return "url(#clip-" + d.data.name.replace(/\W/g, '') + ")"; | |
}) | |
.style("opacity", function(d) { | |
return opacity(d.data.value) | |
}).on("mousemove", function(d) { | |
tool.style("left", d3.event.pageX + 10 + "px") | |
tool.style("top", d3.event.pageY - 20 + "px") | |
tool.style("display", "inline-block"); | |
tool.html(d.data.name + "<br>" + "x" + d.data.quantity + "<br>" + formatter.format(d.data.value)); | |
}) | |
.on("mouseout", function(d) { | |
tool.style("display", "none"); | |
}) | |
// add third label | |
cell | |
.append("text") | |
.attr("x", function(d) { | |
return 4 | |
}) | |
.attr("y", function(d) { | |
return 39 | |
}) | |
.text(function(d) { | |
return formatter.format(d.data.value) | |
}) | |
.attr("font-size", "12px") | |
.attr("fill", "black") | |
.attr("clip-path", function(d) { | |
return "url(#clip-" + d.data.name.replace(/\W/g, '') + ")"; | |
}) | |
.style("opacity", function(d) { | |
return opacity(d.data.value) | |
}).on("mousemove", function(d) { | |
tool.style("left", d3.event.pageX + 10 + "px") | |
tool.style("top", d3.event.pageY - 20 + "px") | |
tool.style("display", "inline-block"); | |
tool.html(d.data.name + "<br>" + "x" + d.data.quantity + "<br>" + formatter.format(d.data.value)); | |
}) | |
.on("mouseout", function(d) { | |
tool.style("display", "none"); | |
}) | |
}) | |
} | |
var key = ""; | |
var visible = 2; | |
async function do_treemap() { | |
// Load visibility and API key from presets,if they exist | |
let apiKey = await GM.getValue('apiKey', 0); | |
let visible_p = await GM.getValue('visiblep', 1); | |
key=apiKey | |
visible=visible_p | |
if (apiKey) $(document).ready(function() { | |
addHTML(apiKey, visible_p) | |
}) | |
else { | |
// Need to prompt the user for an API key and store it if we are given one | |
var apiPrompt = prompt("Torn Treemap | Please enter your API Key (located on the preferences page under API Key.)"); | |
await GM.setValue('apiKey', apiPrompt); | |
$(document).ready(function() { | |
addHTML(apiPrompt, visible_p) | |
}) | |
} | |
} | |
do_treemap(); | |
//$(document).ready(function(){ | |
// //alert('document ready'); | |
// addHTML(key, visible) | |
//}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment