SB Ledger Summary

SB ledger summary for quick view rather than just ledger view

// ==UserScript==
// @name         SB Ledger Summary
// @namespace    http://tampermonkey.net/
// @version      0.1.2
// @description  SB ledger summary for quick view rather than just ledger view
// @author       Anonymous coder
// @run-at       document-start
// @match        http://www.swagbucks.com/account/summary
// @grant        none
// ==/UserScript==
/* Changelog
 *
 * 20190101 - Fixed substitution of ' for " for JSON parsing to work on descriptions with ' in the string
 * 20170825 - Added localStorage item to keep track if the checkbox is enabled and if so, re-click it on page refresh
 * 20170822 - Initial release
 */

// Create a few global variables for use later
var summary_data = [];
var sb_Categories = [
    "Searching",
    "Referrals",
    "Shop",
    "Trade-In",
    "Rewards Store",
    "Swag Codes",
    "Other",
    "Swagstakes",
    "Answer",
    "Discover",
    "Play",
    "Bonus SB",
    "Watch",
    "Tasks",
    "Mobile Watch",
    "Social Games",
    "nCrave",
    "Swagbucks Visa Card",
    "Swagbucks Local",
    "Uncategorized"
];
var my_sheet;

// Parse the data loaded
function parse_ledger(ledger_data) {

    var last_day;   
    // Slice out the beginning data which includes the lifetime SB
    // We don't care about this since it's already displayed on the website anyway
    if (ledger_data.substr(0, 2) == "1|") {
        var idxL = ledger_data.indexOf("@");
        lifeSB = ledger_data.slice(2, idxL);
        // Change all ' to " so we can just use the standard JSON parse function rather than re-invent the wheel
        var temp_data = ledger_data.substr(idxL + 3).replace(/,'/g, ',"').replace(/',/g, '",').replace(/']/g, '"]').replace(/\\/g,'');
        var parsed_data = JSON.parse(temp_data);
        // Sort the data by the category number
        if (parsed_data.length > 0) {
            last_day = parsed_data[0][1];
            parsed_data.sort(function(a,b) {
                if (a[0] == b[0]) {
                    return (a[5] === b[5] ? 0 : (a[5] < b[5] ? -1 : 1));
                } else
                    return a[0] - b[0];
            });
        }
        // Iterate through the parsed data and break it into the activity types defined by Swagbucks
        for (i = 0 ; i < parsed_data.length ; i++) {
            if (last_day == parsed_data[i][1]) {
                var switch_val = 0;
                switch(parsed_data[i][0]) {
                        // Searching
                    case 1: //"Searching the Web",
                        switch_val = 0;
                        break;
                        // Referrals
                    case 2: //"Referral SB from",
                        switch_val = 1;
                        break;
                        // Shop
                    case 3: //"Shop",
                        switch_val = 2;
                        break;
                        // Trade-Im
                    case 4: //"Trade-In",
                        switch_val = 3;
                        break;
                        // Rewards Store
                    case 5: //"Rewards Store Refund",
                    case 6: //"Rewards Store Purchase",
                        switch_val = 4;
                        break;
                        // Swag Codes
                    case 7: //"Swag Code",
                        switch_val = 5;
                        break;
                        // Other
                    case 0: //"Registration",
                    case 9: //"Other",
                    case 21: //"Accelerator Bonus",
                    case 98: //"Correction",
                    case 99: //"Adjustment"
                        switch_val = 6;
                        break;
                        // Swagstakes
                    case 11: //"Swagstakes Entry",
                        switch_val = 7;
                        break;
                        // Answer
                    case 12: //"Answer",
                        switch_val = 8;
                        break;
                        // Discover
                    case 13: //"Discover",
                    case 20: //"Swagbucks Rewards",
                    case 24: //"Mobile Offers",
                        switch_val = 9;
                        break;
                        // Play
                    case 14: //"Play",
                        switch_val = 10;
                        break;
                        //Bonus SB
                    case 15: //"Bonus SB",
                        switch_val = 11;
                        break;
                        // Watch
                    case 16: //"Watch",
                        switch_val = 12;
                        break;
                        // Tasks
                    case 17: //"Tasks",
                        switch_val = 13;
                        break;
                        // Mobile Watch
                    case 18: //"Mobile Watch",
                        switch_val = 14;
                        break;
                        // Social Games
                    case 19: //"Social Games",
                        switch_val = 15;
                        break;
                        // nCrave
                    case 22: //"nCrave",
                        switch_val = 16;
                        break;
                        // Swagbucks Visa Card
                    case 23: //"Swagbucks Visa Card",
                        switch_val = 17;
                        break;
                        // Swagbucks Local
                    case 25: //"Swagbucks Local",
                        switch_val = 18;
                        break;
                        // Not categorized according to Swagbucks website
                    case 8: //"Submission",
                    case 10: //"Flipswap",
                    default:
                        switch_val = 19;
                }
                // Searching and Play categories have blank name fields, so we need to supply it manually rather than the parsed data
                var entry_name;
                if (parsed_data[i][5] === "" && parsed_data[i][0] == 1)
                    entry_name = "Searching";
                else if (parsed_data[i][5] === "" && parsed_data[i][0] == 14)
                    entry_name = "Play";
                else
                    entry_name = parsed_data[i][5];
                // Add parsed data to our array for rendering later
                if (summary_data[switch_val] === undefined) { // Initial data, array element doesn't exist
                    summary_data[switch_val] = {};
                    summary_data[switch_val][entry_name] = [parsed_data[i][3] , 1 ];
                    summary_data[switch_val].LedgerTotal = [parsed_data[i][3] , 1 ];
                }
                else { // Array element exists, but object doesn't, so initialize
                    if (summary_data[switch_val][entry_name] === undefined) {
                        summary_data[switch_val][entry_name] = [parsed_data[i][3] , 1 ];
                        summary_data[switch_val].LedgerTotal[0] += parsed_data[i][3];
                        summary_data[switch_val].LedgerTotal[1] += 1;
                    }
                    else { // Array and object exists, so increment values
                        summary_data[switch_val][entry_name][0] += parsed_data[i][3];
                        summary_data[switch_val][entry_name][1] += 1;
                        summary_data[switch_val].LedgerTotal[0] += parsed_data[i][3];
                        summary_data[switch_val].LedgerTotal[1] += 1;                 
                    }
                }
            }
        }
    }
}

// We need this function in order to override the XMLHttpRequest Swagbucks sends so we can see a copy of the account activity without calling it ourselves
(function(open) {
    XMLHttpRequest.prototype.open = function() {
        this.addEventListener("readystatechange", function() {
            if(this.readyState == 4 && this.status == 200 && this.responseURL.includes("cmd=sb-acct-ledger")) {
                parse_ledger(this.responseText);
            }
        }, false);
        open.apply(this, arguments);
    };
})(XMLHttpRequest.prototype.open);

// Render the ledger data as a summary table
function render_ledger_data() {
    // Add checkbox to display summary or not
    var summary_checkbox_section = document.createElement("section");
    summary_checkbox_section.setAttribute("id", "sbsummary");
    var summary_checkbox_label = document.createElement("label");
    summary_checkbox_label.setAttribute("class", "periodLabel");
    var summary_checkbox = document.createElement("input");
    summary_checkbox.setAttribute("id", "sbsummarycheck");
    summary_checkbox.setAttribute("type", "checkbox");
    summary_checkbox.addEventListener('click', function () {
        if (document.getElementById("sbsummarycheck").checked) {
            localStorage.setItem('Enabled', 'true');
            document.getElementById("tableContainerL").style.display = "none"; 
            document.getElementById("sbsummary_section").style.display = "block"; 
        } else {
            localStorage.setItem('Enabled', 'false');
            document.getElementById("tableContainerL").style.display = "block"; 
            document.getElementById("sbsummary_section").style.display = "none"; 
        }
    }, false);
    summary_checkbox_label.appendChild(summary_checkbox);
    summary_checkbox_label.appendChild(document.createTextNode("SB Summary"));
    summary_checkbox_section.appendChild(summary_checkbox_label);
    document.getElementById("tableParameters").appendChild(summary_checkbox_section);
    // Create the summary table
    var summary_table = document.createElement("table");
    summary_table.setAttribute("class", "sbsummarytable");
    var summary_table_header_row = document.createElement("tr");
    summary_table_header_row.setAttribute("class", "sbsummaryheader");
    var summary_table_header1 = document.createElement("th");
    summary_table_header1.setAttribute("class", "sbsummaryheader_cell");
    summary_table_header1.appendChild(document.createTextNode("Activity"));
    var summary_table_header2 = document.createElement("th");
    summary_table_header2.setAttribute("class", "sbsummaryheader_cell");
    summary_table_header2.appendChild(document.createTextNode("Total SB"));
    var summary_table_header3 = document.createElement("th");
    summary_table_header3.setAttribute("class", "sbsummaryheader_cell");
    summary_table_header3.appendChild(document.createTextNode("Entries"));
    summary_table_header_row.appendChild(summary_table_header1);
    summary_table_header_row.appendChild(summary_table_header2);
    summary_table_header_row.appendChild(summary_table_header3);
    summary_table.appendChild(summary_table_header_row);
    // Now that the header was created, let's populate the table rows, first with the activity summary rows
    var summary_table_data_row, summary_table_data_cell1, summary_table_data_cell2, summary_table_data_cell3;
    var summary_data_key;
    for (i = 0; i < summary_data.length; i++) {
        if (summary_data[i] !== undefined) {
            summary_table_data_row = document.createElement("tr");
            summary_table_data_row.setAttribute("class", "sbsummarydata");

            summary_table_data_cell1 = document.createElement("td");
            summary_table_data_cell1.setAttribute("class", "sbsummarydata_category " + i);
            summary_table_data_cell1.appendChild(document.createTextNode(sb_Categories[i]));
            summary_table_data_cell2 = document.createElement("td");
            if (summary_data[i].LedgerTotal[0] < 0) {
                summary_table_data_cell2.setAttribute("class", "sbsummarydata_amount red");
            } else {
                summary_table_data_cell2.setAttribute("class", "sbsummarydata_amount");
            }
            summary_table_data_cell2.appendChild(document.createTextNode(summary_data[i].LedgerTotal[0]));
            summary_table_data_cell3 = document.createElement("td");
            summary_table_data_cell3.setAttribute("class", "sbsummarydata_entries");
            summary_table_data_cell3.appendChild(document.createTextNode(summary_data[i].LedgerTotal[1]));
            summary_table_data_row.appendChild(summary_table_data_cell1);
            summary_table_data_row.appendChild(summary_table_data_cell2);
            summary_table_data_row.appendChild(summary_table_data_cell3);
            summary_table_data_row.addEventListener('click', function() {
                var changerule = my_sheet.cssRules;
                if (this.className.includes("active")) {
                    // Hide detailed rows
                    for (i = 0; i < changerule.length; i++) {
                        if (changerule[i].selectorText == ("._" + this.childNodes[0].className.split(" ")[1] + ".sbsummarydetaildata")) {
                            changerule[i].style.display = "none";
                            this.className = this.className.replace(" active", "");
                            break;
                        }
                    }
                } else {
                    // Show detailed rows
                    for (i = 0; i < changerule.length; i++) {
                        if (changerule[i].selectorText == ("._" + this.childNodes[0].className.split(" ")[1] + ".sbsummarydetaildata")) {
                            changerule[i].style.display = "table-row";
                            this.className = this.className.concat(" active");
                            break;
                        }
                    }
                }
            });
            // Now we add the table into the html file to be drawn
            summary_table.appendChild(summary_table_data_row);
            // Now we add the detailed rows below the activity
            if ( i == 14 ) { // This is a mobile watch activity, so let's display the regular data first
                for (summary_data_key in summary_data[i]) {
                    if (summary_data_key != "LedgerTotal" && !summary_data_key.includes("Bonus")) {
                        summary_table_data_row = document.createElement("tr");
                        summary_table_data_row.setAttribute("class", "sbsummarydetaildata _" + i);
                        summary_table_data_cell1 = document.createElement("td");
                        summary_table_data_cell1.setAttribute("class", "sbsummarydetaildata_category");
                        summary_table_data_cell1.appendChild(document.createTextNode(summary_data_key));
                        summary_table_data_cell2 = document.createElement("td");
                        if (summary_data[i][summary_data_key][0] < 0) {
                            summary_table_data_cell2.setAttribute("class", "sbsummarydetaildata_amount red");
                        } else {
                            summary_table_data_cell2.setAttribute("class", "sbsummarydetaildata_amount");
                        }
                        summary_table_data_cell2.appendChild(document.createTextNode(summary_data[i][summary_data_key][0]));
                        summary_table_data_cell3 = document.createElement("td");
                        summary_table_data_cell3.setAttribute("class", "sbsummarydetaildata_entries");
                        summary_table_data_cell3.appendChild(document.createTextNode(summary_data[i][summary_data_key][1]));
                        summary_table_data_row.appendChild(summary_table_data_cell1);
                        summary_table_data_row.appendChild(summary_table_data_cell2);
                        summary_table_data_row.appendChild(summary_table_data_cell3);
                        summary_table.appendChild(summary_table_data_row);
                    }
                }
                for (summary_data_key in summary_data[i]) { // Now if there were any bonus rounds, we will display them next
                    if (summary_data_key != "LedgerTotal" && summary_data_key.includes("Bonus")) {
                        summary_table_data_row = document.createElement("tr");
                        summary_table_data_row.setAttribute("class", "sbsummarydetaildata _" + i);
                        summary_table_data_cell1 = document.createElement("td");
                        summary_table_data_cell1.setAttribute("class", "sbsummarydetaildata_category");
                        summary_table_data_cell1.appendChild(document.createTextNode(summary_data_key));
                        summary_table_data_cell2 = document.createElement("td");
                        if (summary_data[i][summary_data_key][0] < 0) {
                            summary_table_data_cell2.setAttribute("class", "sbsummarydetaildata_amount red");
                        } else {
                            summary_table_data_cell2.setAttribute("class", "sbsummarydetaildata_amount");
                        }
                        summary_table_data_cell2.appendChild(document.createTextNode(summary_data[i][summary_data_key][0]));
                        summary_table_data_cell3 = document.createElement("td");
                        summary_table_data_cell3.setAttribute("class", "sbsummarydetaildata_entries");
                        summary_table_data_cell3.appendChild(document.createTextNode(summary_data[i][summary_data_key][1]));
                        summary_table_data_row.appendChild(summary_table_data_cell1);
                        summary_table_data_row.appendChild(summary_table_data_cell2);
                        summary_table_data_row.appendChild(summary_table_data_cell3);
                        summary_table.appendChild(summary_table_data_row);
                    }
                }
            } else { // Display detailed data (non-mobile watch entries)
                for (summary_data_key in summary_data[i]) {
                    if (summary_data_key != "LedgerTotal") {
                        summary_table_data_row = document.createElement("tr");
                        summary_table_data_row.setAttribute("class", "sbsummarydetaildata _" + i);
                        summary_table_data_cell1 = document.createElement("td");
                        summary_table_data_cell1.setAttribute("class", "sbsummarydetaildata_category");
                        summary_table_data_cell1.appendChild(document.createTextNode(summary_data_key));
                        summary_table_data_cell2 = document.createElement("td");
                        if (summary_data[i][summary_data_key][0] < 0) {
                            summary_table_data_cell2.setAttribute("class", "sbsummarydetaildata_amount red");
                        } else {
                            summary_table_data_cell2.setAttribute("class", "sbsummarydetaildata_amount");
                        }
                        summary_table_data_cell2.appendChild(document.createTextNode(summary_data[i][summary_data_key][0]));
                        summary_table_data_cell3 = document.createElement("td");
                        summary_table_data_cell3.setAttribute("class", "sbsummarydetaildata_entries");
                        summary_table_data_cell3.appendChild(document.createTextNode(summary_data[i][summary_data_key][1]));
                        summary_table_data_row.appendChild(summary_table_data_cell1);
                        summary_table_data_row.appendChild(summary_table_data_cell2);
                        summary_table_data_row.appendChild(summary_table_data_cell3);
                        summary_table.appendChild(summary_table_data_row);
                    }
                }
            }
        }
    }
    // Now that the table was generated, let's add the section right after Swagbuck's ledger table
    var summary_section = document.createElement("section");
    summary_section.setAttribute("id", "sbsummary_section");
    summary_section.style.display = "none";
    summary_section.appendChild(summary_table);
    document.getElementById("tableView").appendChild(summary_section);
    if (localStorage.getItem('Enabled') == 'true') {
        document.getElementById('sbsummarycheck').click();
    }
}

// Add some CSS style information so our table doesn't look too different
function render_css() {
    // Create a style
    var style = document.createElement("style");
    // WebKit hack :(
    style.appendChild(document.createTextNode(""));
    // Add the <style> element to the page
    document.head.appendChild(style);

    // Now we add our rules for everything we generated earlier
    // Overall table
    style.sheet.insertRule(".sbsummarytable { cursor: pointer; text-align: left; width: 100%; position: relative; border: solid #ccc; border-collapse: collapse; font-weight: normal; border-width: .1rem; padding: .7rem 0 .7rem 1rem }", style.sheet.length);
    // Table header row (<th>)
    style.sheet.insertRule(".sbsummaryheader { cursor: pointer; font-size: 1.5em; text-align: left; position: relative; border: solid #ccc; font-weight: normal; border-width: .1rem; padding: .7rem 0 .7rem 1rem }", style.sheet.length);
    style.sheet.insertRule(".sbsummaryheader_cell { top: 0; left: 0; width: 100%; position: relative; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; padding: 1rem 0 1rem 1rem; font-weight: bold }", style.sheet.length);    
    // Activity summary row
    style.sheet.insertRule(".sbsummarydata { cursor: pointer; font-size: 1.4em; text-align: left; position: relative; border: solid #ccc; font-weight: normal; border-width: .1rem; padding: .7rem 0 .7rem 1rem; font-weight: bold }", style.sheet.length);
    style.sheet.insertRule(".sbsummarydata_category { color: #69b8d6; width: 100%; position: relative; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; padding: 1rem 0 1rem 1rem; font-weight: bold }", style.sheet.length);
    style.sheet.insertRule(".sbsummarydata_amount { color: #69b8d6; text-align: right; padding: 1rem 0 1rem 1rem; font-weight: bold }", style.sheet.length);
    style.sheet.insertRule(".red.sbsummarydata_amount { color: #e98780 }", style.sheet.length);
    style.sheet.insertRule(".sbsummarydata_entries { top: 0; left: 0; width: 100%; position: relative; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; padding: 1rem 0 1rem 1rem; font-weight: bold}", style.sheet.length);
    // Detailed data row
    //    style.sheet.insertRule(".sbsummarydetaildata { cursor: pointer; font-size: 1.4em; text-align: left; position: relative; border: solid #ccc; font-weight: normal; border-width: .1rem; padding: .7rem 0 .7rem 1rem }", style.sheet.length);
    style.sheet.insertRule(".sbsummarydetaildata_category { top: 0; left: 0; width: 100%; position: relative; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; padding: 1rem 0 1rem 1rem }", style.sheet.length);
    style.sheet.insertRule(".sbsummarydetaildata_amount { color: #69b8d6; text-align: right; padding: 1rem 0 1rem 1rem; font-weight: bold }", style.sheet.length);
    style.sheet.insertRule(".red.sbsummarydetaildata_amount { color: #e98780 }", style.sheet.length);
    style.sheet.insertRule(".sbsummarydetaildata_entries { top: 0; left: 0; width: 100%; position: relative; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; padding: 1rem 0 1rem 1rem; }", style.sheet.length);
    // Generate individual styles for each activity type's detailed rows so they can be show or hidden when clicked
    for (i = 0; i < 20; i++) {
        style.sheet.insertRule("._" + i + ".sbsummarydetaildata { cursor: pointer; width: 100%; font-size: 1.4em; text-align: left; position: relative; border: solid #ccc; font-weight: normal; border-width: .1rem; padding: .1rem 0 .1rem 1rem; display: none }", style.sheet.length);
    }
    return style.sheet;
}

// Push the HTML code changes once the page is ready to go
document.onreadystatechange = function () {
    if (document.readyState === "complete") {
        // Push CSS rules for the table we create
        my_sheet = render_css();
        // Render ledger data
        render_ledger_data();
    }
};