BvS Village Page Revamp

Improvement for the Village Page

// ==UserScript==
// @name          BvS Village Page Revamp
// @namespace     North
// @description   Improvement for the Village Page
// @include       http*://*animecubed.com/billy/bvs/village.html
// @include       http*://*animecubedgaming.com/billy/bvs/village.html
// @version       1.5
// @history       1.5 New domain - animecubedgaming.com - Channel28
// @history       1.4 Now https compatible (Updated by Channel28)
// @history       1.3 Moved Candyween to Areas column
// @history       1.2 A couple of changes based on feedback
// @history       1.1 Interchanged columns for the Village and the Admin tabs
// @history       1.0 Initial release
// @grant         none
// ==/UserScript==

function addStyle(css) {
    var head = document.getElementsByTagName('head')[0];
    var style = document.createElement('style');
    style.type = 'text/css';
    style.textContent = css;
    head.appendChild(style);
}
function hasClass(element, className) {
    return element.className.match(new RegExp('(\\s|^)' + className + '(\\s|$)'));
}
function addClass(element, className) {
    if (!hasClass(element, className)) {
        element.className += " " + className;
    }
}
function removeClass(element, className) {
    if (hasClass(element, className)) {
        var reg = new RegExp('(\\s|^)' + className + '(\\s|$)');
        element.className = element.className.replace(reg, ' ');
    }
}

function tabsPanel(tabsData) {
    var tabChangeCallbacks = [];
    var tabsContainer = document.createElement('div');
    var contentContainer = document.createElement('div');
    var container = document.createElement('div');
    container.appendChild(tabsContainer);
    container.appendChild(contentContainer);
    addClass(tabsContainer, 'tab-panel');

    var selectTab = function(tabData) {
        for (var i = 0; i < tabsData.length; i++) {
            removeClass(tabsData[i].tab, 'active-tab');
            removeClass(tabsData[i].tab, 'hover-inactive-tab');
            tabsData[i].content.style.display = 'none';
            tabsData[i].hide && tabsData[i].hide();
        }
        addClass(tabData.tab, 'active-tab');
        tabData.content.style.display = '';
        tabData.show && tabData.show();

        for (i = 0; i < tabChangeCallbacks.length; i++) {
            tabChangeCallbacks[i](tabData.index);
        }
    };
    var createTab = function(tabData) {
        var tab = document.createElement('div');
        addClass(tab, 'inactive-tab');
        tab.appendChild(document.createTextNode(tabData.text));
        tabsContainer.appendChild(tab);
        tabData.tab = tab;

        if (tabData.content) {
            contentContainer.appendChild(tabData.content);
            tabData.content.style.display = 'none';
        }
        tabData.hide && tabData.hide();

        var onTabClick = function() {
            selectTab(tabData);
        };
        var onTabMouseEnter = function() {
            if (!hasClass(tab, 'active-tab')) {
                addClass(tab, 'hover-inactive-tab');
            }
        };
        var onTabMouseLeave = function() {
            removeClass(tab, 'hover-inactive-tab');
        };
        tab.addEventListener('click', onTabClick, false);
        tab.addEventListener('mouseenter', onTabMouseEnter, false);
        tab.addEventListener('mouseleave', onTabMouseLeave, false);
    };

    for (var i = 0; i < tabsData.length; i++) {
        tabsData[i].index = i;
        createTab(tabsData[i]);
    }

    addStyle([
        ".tab-panel {height: 24px; border-bottom: 4px solid #2647a0;}",
        ".tab-panel div {float: left; font-size: 12px; cursor: pointer; position: relative; padding-left: 5px; padding-right: 5px; line-height: 22px; border-top-left-radius: 1ex; border-top-right-radius: 1ex; border: 1px solid #a3a3a3; border-bottom-width: 0; bottom: 1px;}",
        ".tab-panel .inactive-tab {background: #d8d8d8 url(data:image/gif;base64,R0lGODlhAQAoALMAAOzs7fv7++Xm5vb29+/w8Pz8/Pn5+eHi49zd3/T19fr6+vHy8ujq6v7+/v///9jY2iH5BAAAAAAALAAAAAABACgAAAQS0EnXWgnKjLQIYMKBPGRpnmgEADs=) top left repeat-x; margin-top: 2px;}",
        ".tab-panel .active-tab {background: #2647a0 url(data:image/gif;base64,R0lGODlhAQAoAMQAAEBr1CtOqyhMpjljyDlhxEBt1jBWtTtlyy5SsEBs1Tdfwj1nzj9q0TNbvEFu10Jv2SZHoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAAAAAAALAAAAAABACgAAAUV4CM6TpEADLMcA6E0jYEEAmTfeI6HADs=) top left repeat-x; border: 1px solid #243356; border-bottom-width: 0; line-height: 24px; color: white; cursor: default;}",
        ".tab-panel .hover-inactive-tab {background: #bfdaff url(data:image/gif;base64,R0lGODlhAQAoAMQAAObx/+fx/9zs/9Xn/+fy/8ff/+Lu/+Pw/9fo/+Xw/9Hm/8vi/8/j/8Td/9/t/9rp/+Dt/7/a/+jy/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAAAAAAALAAAAAABACgAAAUWoCRKRAAAyWFAjvAgg8IsRRPdeK7jIQA7) top left repeat-x; margin-top: 2px;}"
    ].join("\n"));

    return {
        container: container,
        selectTab: function(tabIndex) {
            if (tabsData.length > tabIndex) {
                selectTab(tabsData[tabIndex]);
            }
        },
        onTabChange: function(callback) {
            tabChangeCallbacks.push(callback);
        }
    };
}

function getBvsDate() {
    var bvsOffset = -5;
    var d = new Date();

    var utc = d.getTime() + (d.getTimezoneOffset() * 60000);
    return new Date(utc + (3600000 * bvsOffset));
}
function getLastBvsDayrollDate() {
    var bvsDate = getBvsDate();
    var hours = bvsDate.getHours();
    var hoursOffsetToDayroll = hours < 5 ? (19 + hours) : (hours - 5);
    bvsDate.setMinutes(10);
    return new Date(bvsDate.getTime() - hoursOffsetToDayroll * 3600000);
}
function markTodayElement(element) {
    var header = element.querySelector('b');
    var result = /(\d{1,2})\/(\d{1,2}) \(\w{3} - (\d{1,2}):(\d{1,2})/.exec(header.textContent);
    if (result && result.length === 5) {
        var date = new Date();
        var month = result[1] - 1;
        var day = result[2];
        var hour = result[3];
        var minute = result[4];
        var year = date.getFullYear();
        if (date.getMonth() === 0 && month === 11) {
            year--;
        }

        date = new Date(year, month, day, hour, minute);
        var dayroll = getLastBvsDayrollDate();

        if (date > dayroll) {
            addClass(element, 'today');
        }
    }
}

var cleanClonedNode = (function() {
    var counter = 0;
    return function(node) {
        var nodesWithId = document.evaluate(".//*[@id]", node, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
        for (var i = 0; i < nodesWithId.snapshotLength; i++) {
            var item = nodesWithId.snapshotItem(0);
            item.id = '';
        }

        var formNodes = document.evaluate(".//form", node, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
        var formNames = [];
        for (var i = 0; i < formNodes.snapshotLength; i++) {
            var form = formNodes.snapshotItem(i);
            formNames.push(form.name);
            form.name += counter;
        }

        var linkNodes = document.evaluate(".//a[contains(@href, 'submit()')]", node, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
        for (var i = 0; i < linkNodes.snapshotLength; i++) {
            var link = linkNodes.snapshotItem(i);
            for (var j = 0; j < formNames.length; j++) {
                link.href = link.href.replace('.' + formNames[j] + '.submit', '.' + formNames[j] + counter + '.submit');
            }
        }
        counter++;
    };
})();

function getClonedPageComponent(header) {
    var container = document.createElement('div');
    var title = document.createElement('b');
    title.appendChild(document.createTextNode(header));
    container.appendChild(title);

    var xpath = '//td/font/b[text()="' + header + '"]/ancestor::table[1]/following-sibling::table[1]';
    var component = document.evaluate(xpath, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
    if (component.snapshotLength) {
        var clonedComponent = component.snapshotItem(0).cloneNode(true);
        cleanClonedNode(clonedComponent);
        container.appendChild(clonedComponent);
    }

    return container;
}

function getSpyReportLink() {
    var container = document.createElement('div');
    var header = document.createElement('b');
    header.appendChild(document.createTextNode('Spy Report:'));

    container.appendChild(header);

    container.appendChild(document.forms['spyreportshow'].cloneNode(true));
    cleanClonedNode(container);

    return container;
}

function getActionComponent(formName) {
    var element = document.forms[formName];
    while (element && element.tagName.toLowerCase() !== 'table') {
        element = element.parentNode;
    }
    element = element.cloneNode();
    cleanClonedNode(element);
    return element;
}

function createAnnouncemntTab() {
    var container = document.createElement('div');

    var newAnnouncement = document.getElementById('annul').cloneNode(true);
    newAnnouncement.style.height = '';
    newAnnouncement.id = '';
    container.appendChild(newAnnouncement);

    var changeLink = document.querySelector('form[name=cann] p');
    if (changeLink) {
        container.appendChild(changeLink.cloneNode(true));
    }

    return container;
}

function createMessagesTab(selector) {
    var container = document.createElement('div');

    var form = document.createElement('form');
    form.style.marginBottom = '0';
    form.name = 'emess' + selector.length;
    form.action = document.forms['emess'].action;
    form.method = document.forms['emess'].method;
    form.appendChild(document.getElementsByName('player')[0].cloneNode(true));
    form.appendChild(document.getElementsByName('pwd')[0].cloneNode(true));

    var ul = document.createElement('ul');
    ul.className = 'checklist cl3';
    ul.style.height = 'auto';
    ul.style.margin = '0';
    form.appendChild(ul);

    var replaceClass = function(element, classToReplace, newClass) {
        if (hasClass(element, classToReplace)) {
            removeClass(element, classToReplace);
            addClass(element, newClass);
        }
    };
    var messages = document.querySelectorAll(selector);
    for (var i = 0; i < messages.length; i++) {
        var clonedMessage = messages[i].cloneNode(true);
        if (i % 2 === 0) {
            replaceClass(clonedMessage, 'alt', 'alt2');
            replaceClass(clonedMessage, 'alt3', 'alt4');
            replaceClass(clonedMessage, 'alt5', 'alt6');
        } else {
            replaceClass(clonedMessage, 'alt2', 'alt');
            replaceClass(clonedMessage, 'alt4', 'alt3');
            replaceClass(clonedMessage, 'alt6', 'alt5');
        }
        markTodayElement(clonedMessage);
        ul.appendChild(clonedMessage);
    }

    var cloneForm = function(name) {
        var newForm = document.forms[name];
        var formName = name + selector.length;

        newForm = newForm.cloneNode(true);
        newForm.name = formName;

        var links = newForm.querySelectorAll('a');
        for (var i = 0; i < links.length; i++) {
            links[i].href = links[i].href.replace(name, formName);
        }
        return newForm;
    };

    var table = document.createElement('table');
    var tr = document.createElement('tr');
    var tdLeft = document.createElement('td');
    var tdRight = document.createElement('td');
    table.style.width = '100%';
    tdLeft.style.textAlign = 'left';
    tdLeft.style.width = '50%';
    tdRight.style.textAlign = 'right';
    tdRight.style.verticalAlign = 'top';
    tr.appendChild(tdLeft);
    tr.appendChild(tdRight);
    table.appendChild(tr);
    container.appendChild(table);

    var eraseMessagesInput = document.getElementsByName('messageerase')[0];
    if (eraseMessagesInput) {
        form.appendChild(eraseMessagesInput.cloneNode(true));
        var eraseLink = eraseMessagesInput.nextElementSibling.querySelector('a').cloneNode(true);
        eraseLink.href = eraseLink.href.replace('emess', form.name);
        tdLeft.appendChild(eraseLink);
    }
    if (document.forms['refreshmes']) {
        tdRight.appendChild(cloneForm('refreshmes'));
    }
    if (document.forms['emessall']) {
        tdLeft.appendChild(cloneForm('emessall'));
    }
    if (document.forms['leavem']) {
        container.appendChild(cloneForm('leavem'));
    }

    container.appendChild(form);
    addStyle("li.today label > b {background-color: #9AFFF2;}");
    return container;
}

function createActionsTab() {
    var categories = [{
            title: 'Areas',
            formNames: ['kat', 'phases', 'pizzamenu', 'fieldmenu', 'robofighto', 'concenter', 'tvtime', 'eldiablo', 'zat', 'festival', 'candyween']
        }, {
            title: 'Agression',
            formNames: ['spy', 'vattack', 'bbook', 'obscure', 'irreport', 'spyreport']
        }, {
            title: 'Bonuses',
            formNames: ['lemonaid', 'dicetime', 'brotime', 'blackstones', 'pandtime', 'ramen', 'ninjabeach']
        }, {
            title: 'Actions',
            formNames: ['snowday', 'patrol', 'rescol', 'paperwork', 'zatrs', 'repcol', 'sgrs', 'ingredienthunt']
        }, {
            title: 'Admin',
            formNames: ['setperm', 'science']
        }];
    var other = [];

    var cellNumber = 0;
    var table = document.createElement('table');
    var tr;
    table.className='village-actions';

    var createTd = function(title) {
        if (cellNumber % 2 === 0) {
            tr = document.createElement('tr');
            table.appendChild(tr);
        }
        var td = document.createElement('td');
        td.style.verticalAlign = 'top';
        tr.appendChild(td);

        cellNumber++;

        td.appendChild(document.createTextNode(title));
        return td;
    };

    var actions = document.evaluate("//table[@width='255']//form/ancestor::table[1]", document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
    for (var i = 0; i < actions.snapshotLength; i++) {
        var action = actions.snapshotItem(i).cloneNode(true);
        var form = action.querySelector('form');

        var added = false;
        for (var j = 0; j < categories.length; j++) {
            var category = categories[j];
            for (var k = 0; k < category.formNames.length; k++) {
                if (form.name === category.formNames[k]) {
                    category.actions = category.actions || [];
                    category.actions.push(action);
                    added = true;
                }
            }
        }
        if (!added) {
            other.push(action);
        }

        cleanClonedNode(action);
    }
    for (i = 0; i < categories.length; i++) {
        category = categories[i];
        var td = createTd(category.title);
        for (j = 0; category.actions && j < category.actions.length; j++) {
            td.appendChild(category.actions[j]);
        }
    }
    if (other.length) {
        td = createTd('Other');
        for (i = 0; i < other.length; i++) {
            td.appendChild(other[i]);
        }
    }

    addStyle("table.village-actions table {margin-bottom: 3px;}");
    return table;
}

function createVillageTab() {
    var container = document.createElement('div');
    var table = document.createElement('table');
    var tr = document.createElement('tr');
    var rightTd = document.createElement('td');
    var leftTd = document.createElement('td');
    rightTd.style.verticalAlign = 'top';
    leftTd.style.verticalAlign = 'top';
    table.style.width = '100%';
    leftTd.style.width = '50%';
    tr.appendChild(leftTd);
    tr.appendChild(rightTd);
    table.appendChild(tr);
    container.appendChild(table);

    var members = getClonedPageComponent('Members:');
    var permissions = getClonedPageComponent('Permissions:');
    var resources = getClonedPageComponent('Resources:');
    var spyingOn = getClonedPageComponent('Spying On:');
    var villageContracts = getClonedPageComponent('Village Contracts:');
    var villageBank = getClonedPageComponent('Village Bank:');
    var leaveVillage = getClonedPageComponent('Leave Village:');

    rightTd.appendChild(members);
    leftTd.appendChild(resources);
    leftTd.appendChild(permissions);
    leftTd.appendChild(spyingOn);
    leftTd.appendChild(villageContracts);
    leftTd.appendChild(villageBank);
    leftTd.appendChild(leaveVillage);

    return container;
}

function createAdminTab() {
    var container = document.createElement('div');
    var table = document.createElement('table');
    var tr = document.createElement('tr');
    var rightTd = document.createElement('td');
    var leftTd = document.createElement('td');
    rightTd.style.verticalAlign = 'top';
    leftTd.style.verticalAlign = 'top';
    table.style.width = '100%';
    leftTd.style.width = '50%';
    tr.appendChild(leftTd);
    tr.appendChild(rightTd);
    table.appendChild(tr);
    container.appendChild(table);

    var villageOptions = getClonedPageComponent('Village Options:');
    var resourcePoints = getClonedPageComponent('Resource Points:');
    var upgrades = getClonedPageComponent('Upgrades:');
    var invasionDefense = getClonedPageComponent('Invasion Defense:');
    var applications = getClonedPageComponent('Applications / Members:');
    var yesterdayReport =  getClonedPageComponent("Yesterday's Report:");
    var newLeader = getClonedPageComponent('Name new Leader:');
    var spyReportLink = getSpyReportLink();

    var temp = yesterdayReport.querySelectorAll('td > div');
    for (var i = 0; i < temp.length; i++) {
        temp[i].style.height = 'auto';
    }

    rightTd.appendChild(villageOptions);
    leftTd.appendChild(spyReportLink);
    leftTd.appendChild(resourcePoints);
    leftTd.appendChild(upgrades);
    leftTd.appendChild(invasionDefense);
    leftTd.appendChild(applications);
    leftTd.appendChild(yesterdayReport);
    leftTd.appendChild(newLeader);

    return container;
}

(function() {
    var announcement = document.getElementById('annul');

    var container = document.createElement('div');
    container.style.className = 'village-tabs';
    announcement.parentNode.insertBefore(container, announcement.previousElementSibling);

    var elementsToHide = [];
    var element = announcement.previousElementSibling;
    while (element) {
        elementsToHide.push(element);
        element = element.nextElementSibling;
    }
    elementsToHide.push(document.forms['emess']);

    var playerName = document.getElementsByName('player')[0].value;
    var onTabChange = function(index) {
        localStorage.setItem('village-revamp-' + playerName, index);
    };
    var tabs = tabsPanel([{
            text: 'Chat',
            content: createMessagesTab('#messageul li.alt3, #messageul li.alt4, #messageul li.alt5, #messageul li.alt6')
        }, {
            text: 'Log',
            content: createMessagesTab('#messageul li.alt, #messageul li.alt2')
        }, {
            text: 'Actions',
            content: createActionsTab()
        }, {
            text: 'Village',
            content: createVillageTab()
        }, {
            text: 'Admin',
            content: createAdminTab()
        }, {
            text: 'Announcement',
            content: createAnnouncemntTab()
        }, {
            text: 'Old',
            content: document.createElement('div'),
            show: function() {
                for (var i = 0; i < elementsToHide.length; i++) {
                    elementsToHide[i].style.display = '';
                }
            },
            hide: function() {
                for (var i = 0; i < elementsToHide.length; i++) {
                    elementsToHide[i].style.display = 'none';
                }
            }
        }]);
    tabs.onTabChange(onTabChange);

    var tabIndex = localStorage.getItem('village-revamp-' + playerName) || 0;
    tabIndex = parseInt(tabIndex);
    tabs.selectTab(tabIndex);

    container.appendChild(tabs.container);
    addStyle([
        ".village-tabs {width: 100%; text-align: left;}"
    ].join("\n"));
})();