// ==UserScript==
// @name Hentai Heroes Club Contributions
// @namespace hentaiheroes.com
// @version 2.2
// @description Save and compare checkpoints for how much members have contributed on hentaiheroes.com
// @author Qweqwe
// @match https://www.hentaiheroes.com/*
// @grant GM_addStyle
// @require https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.min.js
// ==/UserScript==
(function() {
'use strict';
if (window.top === window.self || window.location.pathname.indexOf('clubs') === -1) {
return;
}
GM_addStyle(".contribution-popup { opacity: 1; transition: opacity .3s ease-in-out; }");
GM_addStyle(".contribution-popup.fade { opacity: 0 }");
var $ = unsafeWindow.$;
var Chart = window.Chart;
var moment = window.moment;
function saveContributionCheckpoint() {
$('#members tr').each(function() {
var $tr = $(this);
var name = getName($tr);
var currentContribution = getCurrentContribution($tr);
appendContributionFor(name, currentContribution);
});
compareContributionCheckpoint();
}
function compareContributionCheckpoint() {
$('#members tr').each(function() {
var $tr = $(this);
var $contribTD = $tr.find('td:nth-child(4)');
var name = getName($tr);
var currentContribution = getCurrentContribution($tr);
if ($contribTD.get(0).childNodes[2]) {
$contribTD.get(0).removeChild($contribTD.get(0).childNodes[2]);
}
var lastContribution = getLastContribution(name);
if (!lastContribution || (currentContribution - lastContribution.contribution) < 0) {
appendContributionFor(name, currentContribution);
lastContribution = { date: moment().format(), contribution: currentContribution };
}
var diff = currentContribution - lastContribution.contribution;
var $span = $('<span/>')
.css('position', 'relative')
.html('(' + formatContribution(diff) + ')');
var $table = $('.lead_table_view.members_table');
var hover = false;
var $popup;
$span.hover(
function() {
hover = true;
if ($popup) {
$popup.removeClass('fade');
return;
}
$popup = $('<div/>')
.addClass('contribution-popup')
.css('position', 'absolute')
.css('z-index', 999)
.css('right', '30px')
.css('width', ((($table.height() * 0.75) / 2) * 2.5) + 'px')
.css('background', 'rgba(.2,.2,.2,.9)')
.css('border', '1px solid rgba(255, 165, 0, 0.75)')
.css('border-radius', '15px')
.css('padding', '10px 5px')
.css('visibility', 'hidden');
var $canvas = $('<canvas/>');
var context = $canvas.get(0).getContext('2d');
var checkpoints = getCheckpointsSorted(getContributionsFor(name));
if (diff > 0 || checkpoints.length == 1 || moment(lastContribution.date).isBefore(moment().subtract(1, 'hour'))) {
checkpoints.push({ date: moment(), contribution: currentContribution });
}
createChart(context, checkpoints, function() {
setTimeout(function() {
if (!$popup) {
return;
}
if ($table.offset().top + $table.height() < $popup.offset().top + $popup.height()) {
$popup.css('bottom', '0px');
}
$popup.css('visibility', 'initial');
}, 0);
});
$popup.append($canvas);
$span.append($popup);
},
function() {
hover = false;
$popup.addClass('fade');
setTimeout(function() {
if ($popup && !hover) {
$popup.remove();
$popup = null;
}
}, 300);
}
);
$contribTD.append($span);
});
}
function createChart(context, checkpoints, callback) {
var scatterChart = new Chart(context, {
type: 'scatter',
data: {
datasets: [{
borderColor: 'rgba(255,255,255,0.4)',
pointBorderColor: 'rgba(255,255,255,0.7)',
pointBackgroundColor: 'rgba(255,255,255,0.5)',
pointBorderWidth: 1,
showLine: true,
lineTension: 0,
data: checkpoints.reduce(function(data, checkpoint) {
var last = data[data.length - 1];
data.push({
x: moment(checkpoint.date),
y: checkpoint.contribution,
diff: last ? checkpoint.contribution - last.y : checkpoint.contribution
});
return data;
}, [])
}]
},
options: {
legend: {
display: false
},
tooltips: {
callbacks: {
title: function(items, data) {
var item = items[0];
var dataItem = data.datasets[item.datasetIndex].data[item.index];
return dataItem.y + ' (' + formatContribution(dataItem.diff) + ')';
}
}
},
animation: {
onComplete: callback
},
aspectRatio: 2.5,
scales: {
xAxes: [{
type: 'time',
position: 'bottom',
gridLines: {
color: 'rgba(255,255,255,0.3)'
},
bounds: 'ticks'
}],
yAxes: [{
position: 'left',
gridLines: {
color: 'rgba(255,255,255,0.1)'
},
ticks: {
precision: 0
}
}]
}
}
});
}
function formatContribution(contrib) {
if (contrib < 0) {
return String(contrib);
}
return "+" + contrib;
}
function getName($tr) {
return $tr.find('td:nth-child(2)').get(0).childNodes[2].textContent.trim();
}
function getCurrentContribution($tr) {
var $contribTD = $tr.find('td:nth-child(4)');
return parseFloat($contribTD.get(0).childNodes[0].textContent.replace(',', ''));
}
function appendContributionFor(name, currentContribution) {
var contributions = getContributionsFor(name);
contributions.checkpoints.push({
date: moment().format(),
contribution: currentContribution
});
contributions.checkpoints = contributions.checkpoints.filter(function(x) {
return typeof x != 'number';
});
window.localStorage.setItem(storageKey(name), JSON.stringify(contributions));
}
function getLastContribution(name) {
var contributions = getContributionsFor(name);
var checkpoints = getCheckpointsSorted(contributions);
return checkpoints[checkpoints.length - 1];
}
function getCheckpointsSorted(contributions) {
contributions.checkpoints.sort(function(a, b) {
return a.date > b.date ? 1 : -1;
});
return contributions.checkpoints;
}
function getContributionsFor(name) {
var contributions = JSON.parse(window.localStorage.getItem(storageKey(name)));
if (typeof contributions == 'number' || typeof contributions == 'string') {
contributions = migrateOldData(contributions, name);
}
return contributions || { checkpoints: [] };
}
function migrateOldData(contribution, name) {
var contributions = {
checkpoints: [{date: moment().format(), contribution: parseFloat(contribution)}]
};
window.localStorage.setItem(storageKey(name), JSON.stringify(contributions));
return contributions;
}
function storageKey(name) {
return 'contribution_checkpoint_' + encodeURIComponent(name);
}
compareContributionCheckpoint();
$("span[sort_by]").on("click", function() {
setTimeout(function() {
compareContributionCheckpoint();
}, 0);
});
var $button = $('<button />')
.addClass('orange_text_button')
.css({ 'z-index': '3', position: 'absolute', right: '65px', top: '12px', display: 'inline-block', height: '26px', 'line-height': '26px', padding: '0 5px' })
.append('Checkpoint')
.click(saveContributionCheckpoint);
$('.club-container.club_dashboard')
.prepend($button);
})();