danilopatro / RingsDB Collection Statistics

// ==UserScript==
// @name         RingsDB Collection Statistics
// @namespace    http://tampermonkey.net/
// @version      8
// @description  Generate information (table and graphs) about your collection informed at RingsDB.com.
// @author       Danilo
// @copyright    2020, Danilo (https://github.com/danilopatro)
// @license      Apache-2.0
// @homepage     https://github.com/danilopatro/RingsDB-Collection-Statistics
// @supportURL   https://github.com/danilopatro/RingsDB-Collection-Statistics/issues
// @icon         https://raw.githubusercontent.com/danilopatro/RingsDB-Collection-Statistics/master/icon-192.ico
// @match        *://www.ringsdb.com/collection/*
// @match        *://ringsdb.com/collection/*
// @grant        GM_getResourceText
// @grant        GM_addStyle
// @require      https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js
// @require      https://code.jquery.com/ui/1.12.1/jquery-ui.js
// @require      https://cdn.jsdelivr.net/npm/chart.js@3.0.0/dist/chart.min.js
// @require      https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2.0.0
// @resource     jqueryCSS https://code.jquery.com/ui/1.12.1/themes/smoothness/jquery-ui.css
// @run-at       document-idle
// ==/UserScript==

/*
var cards_api_url = window.location.href.includes('https') ? 'https://www.ringsdb.com/api/public/cards/' : 'http://www.ringsdb.com/api/public/cards/';
var packs_api_url = window.location.href.includes('https') ? 'https://www.ringsdb.com/api/public/packs/' : 'http://www.ringsdb.com/api/public/packs/';
var collection_url = window.location.href.includes('https') ? 'https://www.ringsdb.com/collection/packs' : 'http://www.ringsdb.com/collection/packs';
*/

//var base_url = window.location.hostname
//var base_url = window.location.hostname.includes('//www.') ? window.location.hostname : 'https://www.'.concat(window.location.hostname)
var base_url = 'https://'.concat(window.location.hostname.replace('www.',''))
//console.log('URL base: '+ base_url);
var cards_api_url = base_url + '/api/public/cards/';
var packs_api_url = base_url + '/api/public/packs/';
var collection_url = base_url + '/collection/packs';

var Cards = new Array();
var Sets = new Array();
var Collection = new Array();
var Collection_codes = new Array();
var Cards_Collection = new Array();
var Cards_Quantity = 0;
var Cards_Quantity_total = 0;
var Cards_Quantity_unique = 0;
var Cards_Quantity_unique_total = 0;
var Packs_Quantity = new Array();
var Packs_Quantity_total = new Array();
var Packs_Quantity_unique = new Array();
var Packs_Quantity_unique_total = new Array();
var Sphere_Quantity = new Array();
var Sphere_Quantity_total = new Array();
var Sphere_Quantity_unique = new Array();
var Sphere_Quantity_unique_total = new Array();
var Type_Quantity = new Array();
var Type_Quantity_total = new Array();
var Type_Quantity_unique = new Array();
var Type_Quantity_unique_total = new Array();

var Sphere_colors = {Tactics: '#ED2E30', Spirit: '#00B1D4', Leadership: '#AD62A5', Lore: '#51B848', Neutral: '#616161', Baggins: '#B39E26', Fellowship: '#B56C0C'};
var Sphere_symbols = {Tactics: '<span class="icon icon-tactics fg-tactics"></span>',
                      Spirit: '<span class="icon icon-spirit fg-spirit"></span>',
                      Leadership: '<span class="icon icon-leadership fg-leadership"></span>',
                      Lore: '<span class="icon icon-lore fg-lore"></span>',
                      Neutral: '<span class="icon icon-neutral fg-neutral"></span>',
                      Baggins: '<span class="icon icon-baggins fg-baggins"></span>',
                      Fellowship: '<span class="icon icon-fellowship fg-fellowship"></span>'};

var Type_colors = {Hero: '#dc9336', Ally: '#b15553', Event: '#509aaf', Attachment: '#77ad52', 'Player Side Quest': '#878a75', Treasure: '#c4c23b', Campaign: '#a1e3ff', Contract: '#3f1e66'};


var newCSS = GM_getResourceText ("jqueryCSS");
GM_addStyle (newCSS);
//GM_addStyle("#base {  width: 100%; height: 100%; display: none; }");
GM_addStyle("#base {  width: 100%; height: 100%; }");
GM_addStyle(".table_canvas {  width: 100%; border-collapse:separate; border-spacing: 10px 15px; }");
GM_addStyle(".table_canvas td {  width: 50%; }");
GM_addStyle(".table_canvas th {  padding: 5px; text-align: center; font-size: 15px; background-color: #eee; width: 33%; ; border-collapse:separate; border: 1px solid #DDD; -webkit-box-shadow: 3px 3px 10px -5px rgba(0,0,0,0.75); -moz-box-shadow: 3px 3px 10px -5px rgba(0,0,0,0.75); box-shadow: 3px 3px 10px -5px rgba(0,0,0,0.75);}");
GM_addStyle(".table_cards {  width: 100%; text-align: center; border-collapse:separate; border: 1px solid #DDD; -webkit-box-shadow: 3px 3px 10px -5px rgba(0,0,0,0.75); -moz-box-shadow: 3px 3px 10px -5px rgba(0,0,0,0.75); box-shadow: 3px 3px 10px -5px rgba(0,0,0,0.75); }");
GM_addStyle(".table_cards th {  padding: 5px; text-align: center; background-color: #eee; width: 33%; }");
GM_addStyle(".table_cards td {  padding: 5px; width: 33%; }");
GM_addStyle(".table_cards td:first-child {  text-align: left; padding-left: 10%; }");
GM_addStyle(".table_cards tr:nth-child(odd) {  background-color: #f5f5f5; }");
GM_addStyle(".table_cards tr:last-child {  background-color: #eee; }");
GM_addStyle("canvas {  width: 100%; height: 100%; }");

var graph_width = '100%';
var graph_height = graph_width;

/**
 * Get HTML asynchronously
 * @param  {String}   url      The URL to get HTML from
 * @param  {Function} callback A callback funtion. Pass in "response" variable to use returned HTML.
 */
var getHTML = function ( url, callback ) {

    // Feature detection
    if ( !window.XMLHttpRequest ) return;

    // Create new request
    var xhr = new XMLHttpRequest();

    // Setup callback
    xhr.onload = function() {
        if ( callback && typeof( callback ) === 'function' ) {
            callback( this.responseXML );
        }
    }

    // Get the HTML
    //console.log(window.location);
    //console.log('Connection function getHTML:');
    xhr.open( 'GET', url );
    xhr.setRequestHeader('Access-Control-Allow-Credentials', 'true');
    xhr.setRequestHeader('Access-Control-Allow-Origin', '*');
    xhr.setRequestHeader('Access-Control-Allow-Methods', 'GET,OPTIONS,PATCH,DELETE,POST,PUT');
    xhr.setRequestHeader('Access-Control-Allow-Headers', 'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version, Authorization');
    //xhr.setRequestHeader('X-PINGOTHER', 'pingpong');
    //xhr.setRequestHeader('Access-Control-Request-Method', 'GET');
    //xhr.setRequestHeader('Access-Control-Request-Headers', 'text/html');
    //xhr.setRequestHeader("X-PINGOTHER", "pingpong");
    //xhr.setRequestHeader("Content-Type", "text/xml");
    //xhr.responseType = 'document';
    xhr.send();

};

function criaDIV(stringCollection){
    var html_corpo = " \
<TABLE class='table_cards'>"+generateTable(Sphere_Quantity_unique, Sphere_Quantity)+ "</TABLE> \
<br><button style='padding: 4px;' onclick=\"$(\'#base\').toggle()\">Show/Hide Charts</button><br><br> \
<div id='base' > \
<TABLE class='table_canvas'> \
<TR> \
<TH COLSPAN='2'>Graphs by Sphere</TH>\
</TR> <TR> \
<TD> <canvas  width='"+graph_width+"' height='"+graph_height+"' id='sphere'> </canvas > </TD>\
<TD> <canvas  width='"+graph_width+"' height='"+graph_height+"' id='spherePie'> </canvas > </TD>\
</TR> <TR> \
<TH COLSPAN='2'>Graphs by Type</TH>\
</TR> <TR> \
<TD> <canvas  width='"+graph_width+"' height='"+graph_height+"' id='type'> </canvas > </TD>\
<TD> <canvas  width='"+graph_width+"' height='"+graph_height+"' id='typePie'> </canvas > </TD>\
</TR> <TR> \
<TH COLSPAN='2'>Status of the Collection</TH>\
</TR> <TR> \
<TD> <canvas  width='"+graph_width+"' height='"+graph_height+"' id='total'> </canvas > </TD>\
<TD> <canvas  width='"+graph_width+"' height='"+graph_height+"' id='spherePolar'> </canvas > </TD>\
</TR> \
</TABLE> \
</div>";
    return html_corpo;
};

function carregaCollection (response) {

    var someElement = $( "label[class*='active']" , response).get();

    if(someElement == null || someElement.length == 0)
        console.log("Not logged in RingsDb.com");
    else {
        for(var i = 0; i < someElement.length; i++) {
            Collection.push(someElement[i].innerHTML);
        }
        filterCollection();
    }
}

function filterCollection () {
    for(var i = 0; i < Sets.length; i++) {
        if(Collection.includes(Sets[i].name)) {
            Collection_codes.push(Sets[i].code);
        }
    }
    filterCards();
    inputSearchString();
}

function inputSearchString () {
    if(Collection_codes.length == 0)
        console.log("No code loaded.");
    else {
        $(criaDIV(" e:"+Collection_codes.join("|"))).insertBefore($("#owned_packs form"));
        geraChart(Sphere_Quantity, 'All cards', 'sphere', 'bar', Sphere_Quantity_unique, 'Distinct cards');
        geraChart(Sphere_Quantity, 'All cards', 'spherePie', 'pie', Sphere_Quantity_unique, 'Distinct cards');
        geraChart(Type_Quantity, 'All cards', 'type', 'bar', Type_Quantity_unique, 'Distinct cards');
        geraChart(Type_Quantity, 'All cards', 'typePie', 'pie', Type_Quantity_unique, 'Distinct cards');
        geraChart(Sphere_Quantity_unique, 'Distinct cards', 'spherePolar', 'polarArea', new Array(), '', Sphere_Quantity_unique_total);
        geraChart(Cards_Quantity_unique_total, 'Total', 'total', 'bar', Cards_Quantity_unique, '', new Array());
    }
}

function loadCards (callback) {
    $.getJSON(cards_api_url, function(data) {
        for(var i = 0; i < data.length; i++) {
            Cards.push({
                code: data[i].code,
                pack_name: data[i].pack_name,
                sphere_name:  data[i].sphere_name,
                type_name:  data[i].type_name,
                quantity:  data[i].quantity
            });
        }
    });
    if ( callback && typeof( callback ) === 'function' ) {
        callback();
    }
}

function loadSets (callback) {
    $.getJSON(packs_api_url, function(data) {
        for(var i = 0; i < data.length; i++) {
            Sets.push({
                code: data[i].code,
                name:  data[i].name
            });
        }
    });
    if ( callback && typeof( callback ) === 'function' ) {
        callback();
    }
    getHTML(collection_url, carregaCollection);
}

function filterCards () {
    for(var i = 0; i < Cards.length; i++) {
        Cards_Quantity_total += Cards[i].quantity * Cards[i].pack_name == 'Core Set' ? multipleCoreSets(Collection) : 1;
        Cards_Quantity_unique_total += 1;
        Packs_Quantity_total[Cards[i].pack_name] = isNaN(Packs_Quantity_total[Cards[i].pack_name]) ? Cards[i].quantity * Cards[i].pack_name == 'Core Set' ? multipleCoreSets(Collection) : 1 : Packs_Quantity_total[Cards[i].pack_name] + Cards[i].quantity * Cards[i].pack_name == 'Core Set' ? multipleCoreSets(Collection) : 1;
        Packs_Quantity_unique_total[Cards[i].pack_name] = isNaN(Packs_Quantity_unique_total[Cards[i].pack_name]) ? 1 : Packs_Quantity_unique_total[Cards[i].pack_name] + 1;
        Sphere_Quantity_total[Cards[i].sphere_name] = isNaN(Sphere_Quantity_total[Cards[i].sphere_name]) ? Cards[i].quantity * Cards[i].pack_name == 'Core Set' ? multipleCoreSets(Collection) : 1 : Sphere_Quantity_total[Cards[i].sphere_name] + Cards[i].quantity * Cards[i].pack_name == 'Core Set' ? multipleCoreSets(Collection) : 1;
        Sphere_Quantity_unique_total[Cards[i].sphere_name] = isNaN(Sphere_Quantity_unique_total[Cards[i].sphere_name]) ? 1 : Sphere_Quantity_unique_total[Cards[i].sphere_name] + 1;
        Type_Quantity_total[Cards[i].type_name] = isNaN(Type_Quantity_total[Cards[i].type_name]) ? Cards[i].quantity * Cards[i].pack_name == 'Core Set' ? multipleCoreSets(Collection) : 1 : Type_Quantity_total[Cards[i].type_name] + Cards[i].quantity * Cards[i].pack_name == 'Core Set' ? multipleCoreSets(Collection) : 1;
        Type_Quantity_unique_total[Cards[i].type_name] = isNaN(Type_Quantity_unique_total[Cards[i].type_name]) ? 1 : Type_Quantity_unique_total[Cards[i].type_name] + 1;
        if(Collection.includes(Cards[i].pack_name)) {
            Cards_Collection.push([Cards[i].pack_name,
                                   Cards[i].sphere_name,
                                   Cards[i].type_name,
                                   Cards[i].quantity]);
            Cards_Quantity += Cards[i].quantity * Cards[i].pack_name == 'Core Set' ? multipleCoreSets(Collection) : 1;
            Cards_Quantity_unique += 1;
            Packs_Quantity[Cards[i].pack_name] = isNaN(Packs_Quantity[Cards[i].pack_name]) ? Cards[i].quantity * (Cards[i].pack_name == 'Core Set' ? multipleCoreSets(Collection) : 1) : Packs_Quantity[Cards[i].pack_name] + Cards[i].quantity * (Cards[i].pack_name == 'Core Set' ? multipleCoreSets(Collection) : 1);
            Packs_Quantity_unique[Cards[i].pack_name] = isNaN(Packs_Quantity_unique[Cards[i].pack_name]) ? 1 : Packs_Quantity_unique[Cards[i].pack_name] + 1;
            Sphere_Quantity[Cards[i].sphere_name] = isNaN(Sphere_Quantity[Cards[i].sphere_name]) ? Cards[i].quantity * (Cards[i].pack_name == 'Core Set' ? multipleCoreSets(Collection) : 1) : Sphere_Quantity[Cards[i].sphere_name] + Cards[i].quantity * (Cards[i].pack_name == 'Core Set' ? multipleCoreSets(Collection) : 1);
            Sphere_Quantity_unique[Cards[i].sphere_name] = isNaN(Sphere_Quantity_unique[Cards[i].sphere_name]) ? 1 : Sphere_Quantity_unique[Cards[i].sphere_name] + 1;
            Type_Quantity[Cards[i].type_name] = isNaN(Type_Quantity[Cards[i].type_name]) ? Cards[i].quantity * (Cards[i].pack_name == 'Core Set' ? multipleCoreSets(Collection) : 1) : Type_Quantity[Cards[i].type_name] + Cards[i].quantity * (Cards[i].pack_name == 'Core Set' ? multipleCoreSets(Collection) : 1);
            Type_Quantity_unique[Cards[i].type_name] = isNaN(Type_Quantity_unique[Cards[i].type_name]) ? 1 : Type_Quantity_unique[Cards[i].type_name] + 1;

        }
    }
}

function multipleCoreSets(CollectionLoaded) {
    return CollectionLoaded.includes('3') ? 3 :
            CollectionLoaded.includes('2') ? 2 : 1;
}

// @source https://stackoverflow.com/questions/1058427/how-to-detect-if-a-variable-is-an-array
function isArray(obj) {
    return Object.prototype.toString.call(obj) === '[object Array]';
}

// @source https://stackoverflow.com/questions/6823286/create-unique-colors-using-javascript
function randomColors(total, color = 240)
{
    var i = 40 / (total - 1); // distribute the colors evenly on the hue range
    var r = []; // hold the generated colors
    for (var x=0; x<total; x++)
    {
        r.push(get_random_color(30+x*i, color)); // you can also alternate the saturation and value for even more contrast between the colors
    }
    return r;
}

function rand(min, max) {
    return min + Math.random() * (max - min);
}

function get_random_color(i, color) {
    var h = rand(color, color);
    var s = rand(100, 100);
    var l = rand(i, i);
    return 'hsl(' + h + ',' + s + '%,' + l + '%)';
}

function numberWithPoints(x) {
    return x.toString().replace(/\B(?<!\.\d*)(?=(\d{3})+(?!\d))/g, ".");
}

function hideShow(element) {
    var x = document.getElementById(element);
    if (x.style.display === "none") {
        x.style.display = "block";
    } else {
        x.style.display = "none";
    }
}

function generateTable(data, data2, headers = ['Sphere','Number of distinct cards','Total number of cards' ]) {
    var html = '<tr>\r\n<th nowrap>' + headers[0] + '</th><th nowrap>' + headers[1] + '</th><th nowrap>'+ headers[2] + '</th></tr>\r\n';
    var data_labels = Object.getOwnPropertyNames(data);
    var n_cards = 0;
    var q_cards = 0;
    data_labels.shift();
    data = Object.values(data);
    data2 = Object.values(data2);
    if (typeof (data[0]) === 'undefined') {
        return null;
    }

    for (var row = 0; row < data.length; row++) {
        html += '<tr class="fg-'+data_labels[row].toLowerCase() +'">\r\n';
        html += '<td>' + returnSymbol(data_labels[row]) + '</td><td>' + data[row] + '</td><td>' + data2[row] + '</td>\r\n';
        html += '</tr>\r\n';
        n_cards += data[row];
        q_cards += data2[row];
    }
    html += '<tr>\r\n';
    html += '<td><b>TOTAL</b></td><td><b>' + n_cards + '</b></td><td><b>' + q_cards + '</b></td>\r\n';
    html += '</tr>\r\n';
    return html;
}

function returnSymbol(sphere) {
    var labels_info = Object.getOwnPropertyNames(Sphere_symbols);
    return Sphere_symbols[sphere] + " " + sphere;
}

function divideArrays(A1, A2) {
    var temp = new Array();
    var kA1 = Object.getOwnPropertyNames(A1);
    kA1.shift();
    for (var x = 0; x < kA1.length; x++) {
        temp[kA1[x]] = A1[kA1[x]]/A2[kA1[x]];
    }
    return temp;
}

// @source https://stackoverflow.com/questions/5223/length-of-a-javascript-object
Object.size = function(obj) {
    var size = 0, key;
    for (key in obj) {
        if (obj.hasOwnProperty(key)) size++;
    }
    return size;
};

loadCards(loadSets);

function geraChart(info, name, canvas, type = 'bar', info2 = info, name2 = name, info_tot = info, info2_tot = info2) {
    info = isArray(info) ? info : [info];
    info2 = isArray(info2) ? info2 : [info2];
    var labels_info = Object.getOwnPropertyNames(info);
    labels_info.shift();
    var values_info = canvas == 'spherePolar' ? Object.values(divideArrays(info,info_tot)) : Object.values(info);
    var values_info2 = canvas == 'spherePolar' ? values_info2 = Object.values(divideArrays(info2,info2_tot)) : Object.values(info2);
    var data1 = {
        labels: name,
        data: values_info,
        backgroundColor: function(context) {
            var index = context.dataIndex;
            var value = context.dataset.data[index];
            return Object.getOwnPropertyNames(Sphere_colors).includes(labels_info[index]) ? Sphere_colors[labels_info[index]] :    // For Sphere graph
            Object.getOwnPropertyNames(Type_colors).includes(labels_info[index]) ? Type_colors[labels_info[index]] :    // For Type graph
            labels_info[0] == 'Core Set' ? Object.values(randomColors(Object.size(labels_info), 0)) : // for Pack graph
            'rgb(255,0,0,.1)';
        },
        borderWidth: ['pie', 'polarArea'].indexOf(type) > -1 ? 2 :
        canvas == 'total' ? 1 : 0,
        borderColor: canvas == 'total' ? "rgb(255,0,0,.5)" : "#fff",
        borderAlign: 'inner',
        order: 2,
    };
    var data2 = {
        labels: name2,
        data: values_info2,
        backgroundColor: function(context) {
            var index = context.dataIndex;
            var value = context.dataset.data[index];
            return Object.getOwnPropertyNames(Sphere_colors).includes(labels_info[index]) ? Sphere_colors[labels_info[index]] :    // For Sphere graph
            Object.getOwnPropertyNames(Type_colors).includes(labels_info[index]) ? Type_colors[labels_info[index]] :    // For Type graph
            labels_info[0] == 'Core Set' ? Object.values(randomColors(Object.size(labels_info), 0)) : // for Type graph
            'rgb(0,0,255,.25)';
        },
        borderWidth: ['pie', 'polarArea'].indexOf(type) > -1 ? 2 : 0,
        borderColor: "#fff",
        borderAlign: 'inner',
        type: type == 'bar' ? 'line' : type,
        fill: type == 'bar' ? false : true,
        showLine: false,
        //canvas == 'total'
        lineTension: 0,
        pointStyle: 'dash',
        pointradius: 0,
        pointHoverRadius: 0,
        pointBorderWidth: 0,
        pointHoverBorderWidth: 0,
        pointBorderColor: "#fff",
        order: 1,
    };
    var data3 = {
        labels: name2,
        data: values_info2,
        type: type,
        backgroundColor: function(context) {
            var index = context.dataIndex;
            var value = context.dataset.data[index];
            return Object.getOwnPropertyNames(Sphere_colors).includes(labels_info[index]) ? Sphere_colors[labels_info[index]] :    // For Sphere graph
            Object.getOwnPropertyNames(Type_colors).includes(labels_info[index]) ? Type_colors[labels_info[index]] :    // For Type graph
            labels_info[0] == 'Core Set' ? Object.values(randomColors(Object.size(labels_info), 0)) : // for Type graph
            canvas == 'total' ? 'rgb(255,0,0,1)' :
            'rgb(255,0,0,.25)';
        },
        borderWidth: ['pie', 'polarArea'].indexOf(type) > -1 ? 2 : 0,
        borderColor: "#fff",
        borderAlign: 'inner',
        fill: type == 'bar' ? true : true,
        showLine: false,
        order: 1,
        datalabels: {
            labels: {
                value: {
                    color: 'white',
                    font: {
                        size: 18,
                    },
                }
            }
        }
    };
    var data4 = {
        labels: name,
        data: values_info,
        backgroundColor: function(context) {
            var index = context.dataIndex;
            var value = context.dataset.data[index];
            return Object.getOwnPropertyNames(Sphere_colors).includes(labels_info[index]) ? Sphere_colors[labels_info[index]] :    // For Sphere graph
            Object.getOwnPropertyNames(Type_colors).includes(labels_info[index]) ? Type_colors[labels_info[index]] :    // For Type graph
            labels_info[0] == 'Core Set' ? Object.values(randomColors(Object.size(labels_info), 0)) : // for Pack graph
            'rgb(255,0,0,.1)';
        },
        borderWidth: ['pie', 'polarArea'].indexOf(type) > -1 ? 2 :
        canvas == 'total' ? 1 : 0,
        borderColor: canvas == 'total' ? "rgb(255,0,0,.5)" : "#fff",
        borderAlign: 'inner',
        order: 2,
        datalabels: {
            color: canvas == 'total' ? 'red' : null,
            font: {
                size: 18,
            },
        }
    };
    var barChartData = {
        labels: labels_info,
        datasets: isNaN(info2) ? [ data1 ] : [ canvas == 'total' ? data4 : data1, canvas == 'total' ? data3 : data2 ],
    };
    var ctx = document.getElementById(canvas).getContext('2d');
    var defaultOptions = {
        responsive: true,
        tooltips: {
            mode: type == 'pie' ? 'dataset' : 'index',
            intersect: true,
            position: type == 'pie' ? 'nearest' : 'average',
            callbacks: {
                title: function(tooltipItem, data) {
                    console.log(data.labels[tooltipItem]);
                    return type == 'pie' ? data.datasets[tooltipItem[0].datasetIndex].label+':' : tooltipItem[0].xLabel+':';
                },
            },
        },
        plugins: {
            title: {
                display: true,
                text: name +' Chart'
            },
            legend: {
                display: false,
                position: 'bottom',
                labels: {
                    usePointStyle: true,
                },
            },
            datalabels: {
                anchor: type == 'pie' ? 'center' : 'end',
                align: 'bottom',
                display: 'auto',
                formatter: (value, ctx) => {
                    let sum = 0;
                    let dataArr = ctx.chart.data.datasets[ctx.datasetIndex].data;
                    dataArr.map(data => {
                        sum += data;
                    });
                    let percentage = type == 'pie' ? labels_info[ctx.dataIndex]+"\n"+(value*100 / sum).toFixed(1)+"%" : value;
                    return percentage;
                },
                color: '#fff',
                labels: {
                    value: {
                        font: {
                            weight: 'bold'
                        },
                    },
                }
            }
        }
    };
    var stackedOptions = {
        responsive: true,
        tooltips: {
            enabled: false,
            mode: type == 'pie' ? 'dataset' : 'index',
            intersect: true,
            position: type == 'pie' ? 'nearest' : 'average',
        },
        scales: {
            x: {
                stacked: true,
                display: false,
            },
            y: {
                stacked: false,
                ticks: {
                    beginAtZero: true,
                    stepSize: 100,
                },
            },
        },
        plugins: {
            title: {
                display: true,
                text: 'Collection (distinct cards)'
            },
            legend: {
                display: false,
                position: 'bottom',
                labels: {
                    usePointStyle: true,
                },
            },
            datalabels: {
                anchor: type == 'pie' ? 'center' : 'end',
                align: 'bottom',
                display: 'auto',
                textAlign: 'center',
                formatter: (value, ctx) => {
                    let sum = 0;
                    let dataArr = ctx.chart.data.datasets[ctx.datasetIndex].data;
                    dataArr.map(data => {
                        sum += data;
                    });
                    let percentage = type == 'pie' ? labels_info[ctx.dataIndex]+"\n"+(value*100 / sum).toFixed(1)+"%" :
                    canvas == 'total' ? value+" cards\n"+(value*100 / Cards_Quantity_unique_total).toFixed(1)+"% of the total" :
                    value;
                    percentage = value == Cards_Quantity_unique_total ? value + ' cards published' : 'You own ' + value + " cards\n" + (value*100 / Cards_Quantity_unique_total).toFixed(1) + "% of the total";
                    return percentage;
                },
                color: '#000',
            }
        },
    };
    var polarOptions = {
        responsive: true,
        tooltips: {
            mode: type == 'pie' ? 'dataset' : 'index',
            intersect: true,
            position: type == 'pie' ? 'nearest' : 'average',
            callbacks: {
                label: function(tooltipItem, data) {
                    var label = data.labels[tooltipItem.index] || '';
                    label += ': ' + Sphere_Quantity_unique[data.labels[tooltipItem.index]] + ' of ' + Sphere_Quantity_unique_total[data.labels[tooltipItem.index]];
                    return label;
                },
            }
        },
        scale: {
            angleLines: {
            },
            ticks: {
                min: 0,
                max: 1,
                stepSize: 0.1,
                callback: function(value, index, values) {
                    return (value*100).toFixed(0)+"%";
                },
            },
        },
        plugins: {
            title: {
                display: true,
                text: 'Percent of '+name+' in the Collection in relation to those published by Sphere'
            },
            legend: {
                display: true,
                position: 'right',
                labels: {
                    usePointStyle: true,
                },
                onClick: null,
            },
            datalabels: {
                anchor: ['pie'].indexOf(type) > -1 ? 'center' :
                ['polarArea'].indexOf(type) > -1 ? 'end' :
                'end',
                align: ['pie'].indexOf(type) > -1 ? 'bottom' :
                ['polarArea'].indexOf(type) > -1 ? 'start' :
                'bottom',
                clamp: true,
                display: 'auto',
                formatter: (value, ctx) => {
                    let sum = 0;
                    let dataArr = ctx.chart.data.datasets[ctx.datasetIndex].data;
                    dataArr.map(data => {
                        sum += data;
                    });
                    let percentage = ['pie'].indexOf(type) > -1 ? labels_info[ctx.dataIndex]+"\n"+(value*100 / sum).toFixed(1)+"%" : // For Pie graph
                    ['polarArea'].indexOf(type) > -1 ? (value*100).toFixed(1)+"%" : // For Polar Area graph
                    value;
                    return percentage;
                },
                color: '#fff',
            }
        },
    };
    var charBar = new Chart(ctx, {
        type: type,
        data: barChartData,
        plugins: [ChartDataLabels],
        options: type == 'polarArea' ? polarOptions :
        canvas == 'total' ? stackedOptions : defaultOptions,
    });
};