Skip to content

Instantly share code, notes, and snippets.

@selwynsimsek
Last active March 9, 2022 12:56
Show Gist options
  • Save selwynsimsek/2cecf8cbe5fe32f17eebe2d341a9acfa to your computer and use it in GitHub Desktop.
Save selwynsimsek/2cecf8cbe5fe32f17eebe2d341a9acfa to your computer and use it in GitHub Desktop.
// ==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