LWM_ProgressBars

Insert level & skill progress bars to home page & player info page.

Ajankohdalta 8.11.2017. Katso uusin versio.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name LWM_ProgressBars
// @name:ru Прогресс персонажа
// @description	Insert level & skill progress bars to home page & player info page.
// @description:ru Добавляет шкалу отображающую процент получения следующего уровня
// @namespace saturn_hwm
// @author saturn573 
// @homepage http://greasyfork.org/scripts/2892-lwm-progressbars
// @include http://178.248.235.15/home.php
// @include http://*.lordswm.com/home.php
// @include http://*.heroeswm.ru/home.php
// @include http://178.248.235.15/pl_info.php?id=*
// @include http://*.lordswm.com/pl_info.php?id=*
// @include	http://*.heroeswm.ru/pl_info.php?id=*
// @version 0.28
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_deleteValue
// ==/UserScript==

var Scales = [[0, 1500, 4500, 15E3, 32E3, 9E4, 19E4, 4E5, 86E4, 165E4, 3E6, 5E6, 85E5, 145E5, 25E6, 43E6, 7E7, 108E6, 16E7, 23E7, 325E6, 5E8], [20, 50, 90, 160, 280, 500, 900, 1600, 2900, 5300, 9600, 17300, 35E3], [16, 60, 180, 400, 700, 1200, 2E3, 3E3, 4300, 6E3, 8E3, 10500], [90, 180, 360, 720, 1500, 3E3, 5E3, 8E3, 12E3, 17E3, 23E3, 3E4, 38E3, 47E3, 57E3, 7E4], [10, 30, 60, 100, 150, 210, 280, 360, 450, 550, 660, 800, 1E3, 1300, 2E3], [50, 120, 240, 400, 600, 840, 1200, 2E3, 3E3, 4300, 6E3, 8E3, 10800, 14E3, 17600, 21600, 26E3], [100, 240, 480, 800, 1200, 1680, 2400, 4E3, 6E3, 8600, 12E3], [50, 120, 300, 600, 1E3, 1500,
2200, 3E3, 4E3, 5500, 7800, 11E3, 14500, 18200, 22200], [150, 350, 750, 1400, 2200, 3200, 4300, 5600, 7E3, 8500], [60, 200, 450, 850, 1500, 2700, 4500], [1600, 3600], [30, 80, 165, 310, 555, 970, 1680, 2885, 5770], [104, 588, 2200, 7E3, 1E4], [8, 29, 71, 155, 295, 505, 799, 1191, 1695, 6E3, 12E3]];

var Styles = '.pb{ display:inline-block; position: relative; width:135px; background:white; border:2px solid; border-radius: 7px/3px; cursor: pointer; }\
	.pb .scale { display: inline; position: absolute; top: 0px; left: 0px; height: 100%; border-radius: 2px/1px; background:#af9f39; background: linear-gradient(to top, #af9f39, #fffbca); }\
	.pb:hover .pb-side-text { display: inline; }\
	.pb:hover .pb-front-text { display: none; }\
	.pb-text { position: relative; width: 100%; height: 100%; color: darkgreen; text-align: center;  font-weight: bold; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; }\
	.pb-side-text { display:none; }\
	.pb-front-text { display: inline; }\
	.left { font-size: smaller; }\
	.levelpb { display: inline-flex; margin-left: 10px; width: 180px; } \
	.skill-table { width: 100%; margin-bottom: 10px; }\
	.skill-table caption { text-align: left; }\
	.skill-table caption span { float: right; font-size: small; margin-right: 10; }\
    .key-column { text-align: left; width: 100%; font-variant: small-caps; }\
	.skill-value { font-size: 15px; }\
	.skill-value-complete { color: #af9f39; font-size: 20px; font-weight: bold; font-family: "Comic Sans MS", cursive, sans-serif;  }\
	.skill-value-none { font-weight: normal; }\
	.skill-value-low { color: blue; }\
 	.progress-column { text-align: center; width: 135px; }\
	.expander { margin-left: 5px; border-radius: 8px; height: 16px; width: 16px; font-size: 8px; font-weight:bold; background: gold; background: linear-gradient(to top, #af9f39, #fffbca); border: #af9f39; border-style: outset; border-width: 1px; position: relative; top: -3px; }';

var expandCaption = '>';
var collapseCaption = '<';
var levelStringRU = '\u0411\u043E\u0435\u0432\u043E\u0439 \u0443\u0440\u043E\u0432\u0435\u043D\u044C';
var factionsGroupTitleRU = '\u0424\u0440\u0430\u043A\u0446\u0438\u044F';
var guildsGroupTitleRU = '\u0413\u0438\u043B\u044C\u0434\u0438\u044F';
var allRu = '\u043E\u0431\u0449.';
var hoursRu = 'часов';
var daysRu = 'дней';
var weeksRu = 'недель';
var pointsRu = 'очков за';
var pphRu  = 'очков в час';
var averageSpeedRu = 'Средняя скорость';
var tttnlRu = 'Время до следующего уровня';


var pl_id = /pl_id=(\d+)/.exec(document.cookie)[1];
if (~location.pathname.indexOf('pl_info.php')) {
	pl_id = /id=(\d+)/.exec(location.search)[1];
}

function getTrackingState(){
	let result = GM_getValue('pl_' + pl_id);
	if (result) {
		result = JSON.parse(result);
	}
	else {
		result = undefined;
	}
	return result;
}

function startTracking() {	
	GM_setValue('pl_' + pl_id, JSON.stringify(currentState));
	alert((isEn() ? 'The tracking was started. Click again to cancel it.'
		: 'Отслеживание начато. Нажмите еще раз чтобы выключить его'));
	location.reload();	
}

function stopTracking() {
	if (confirm((isEn() 
			? 'The tracking will be stopped immediately as you press the OK button.'
			: 'Отслеживание будет выключено сразу после того как вы нажмете кнопку ОК'))) {
		GM_deleteValue("pl_" + pl_id);
		location.reload();
	}
}

var currentState = { time: new Date().getTime() };
var trackingState = getTrackingState();

function getTimeLabel(hours) {
	if (hours < 24) {
		return hours + ' ' + (isEn() ? 'hours' : hoursRu);
	}
	else {
		var days = parseInt(hours / 24);		
		if (days < 7) {
			return days + ' ' + (isEn() ? 'days' : daysRu)
				+ ' ' + (hours % 24) + ' ' + (isEn() ? 'hours' : hoursRu);
		}
		else {
			return '~' + parseInt(days / 7) + ' ' + (isEn() ? 'weeks' : weeksRu);
		}
	}
}

function getTrackingInfoString(caption, left) {
	let delta = (currentState[caption] - trackingState[caption]);		
	let elapsedHours =  Math.floor((currentState.time - trackingState.time) / 36E5);
	let speed = delta / elapsedHours;
	return delta.toFixed(2)
	+ ' ' + (isEn() ? 'points in' : pointsRu) + ' '
	+ getTimeLabel(elapsedHours)
	+ ((elapsedHours > 0 && delta > 0)
		? ' \r\n' 
			+ (isEn() ? 'Average speed' : averageSpeedRu)
			+ ': ' + speed.toFixed(2) + ' '
			+ (isEn() ? 'points per hour' : pphRu)
			+ '\r\n'
			+ (isEn() ? 'Time to the next level' : tttnlRu)
			+ ': ' + getTimeLabel(Math.floor(left / speed))
		: '');
}


function round(value, precision) {
    if (precision > 0) {
        var b = precision * 10;
        return Math.round(value * b) / b;
    }
    else {
        return Math.floor(value);
    }
}

function parseSourceCode(source) {
    var captions = [];
    var match;
    var cr = /((?:[a-z'\u0430-\u044F\u0451]+\s)?[a-z\u0430-\u044F\u0451]+):\s/gi;
    while ((match = cr.exec(source)) != null) {
        captions.push({ Index: cr.lastIndex, Value: match[0].toString() });
    }
    var getCaption = function (index) {
        for (var ii = captions.length - 1; ii >= 0; ii--) {
            if (captions[ii].Index < index) {
                return captions[ii].Value;
            }
        }
        return null;
    }

    var result = [];
    var sr = /(?:\s|>)(\d+)(<\/b>|<\/a>)?\s\((\d+(?:\.\d+)?)\)/g;
    while ((match = sr.exec(source)) != null) {
		let item = {
            Caption: getCaption(sr.lastIndex),
            Level: +match[1].toString(),
            Score: +match[3].toString(),
            Sign: match[2]
        };
		currentState[item.Caption] = item.Score;
        result.push(item);
    }
    return result;
}

function addStyles() {
    var style = document.createElement('style');
    style.type = 'text/css';
    style.appendChild(document.createTextNode(Styles));
    document.head.appendChild(style);
}

function checkScale(scale) {
    if (scale && scale.length > 0) {
        if (scale.length > 1) {
            for (var ii = 1; ii < scale.length; ii++) {
                if (scale[ii] <= scale[ii - 1]) {
                    return false;
                }
            }
        }
        return true;
    }
}

function createProgressBar(points, left, percentage, title) {
    var percentageString = Math.floor(percentage) + '%';

    var border = document.createElement('div');
    border.className = 'pb';	
	
    var scale = document.createElement('div');
    scale.className = 'scale';
    scale.style.width = percentageString;
    border.appendChild(scale);

    var textBox = document.createElement('div');
    textBox.className = 'pb-text';

    var frontText = document.createElement('span');
    frontText.className = 'pb-front-text';
    frontText.appendChild(document.createTextNode(percentageString));
    textBox.appendChild(frontText);

    var sideText = document.createElement('span');
    sideText.className = 'pb-side-text';
    sideText.appendChild(document.createTextNode(points));

    var l = document.createElement('span');
    l.className = 'left';
    l.appendChild(document.createTextNode('+' + left));
    sideText.appendChild(l);
    textBox.appendChild(sideText);

    border.appendChild(textBox);
	
	if (title) {		
		if (trackingState) {
			border.title =  getTrackingInfoString(title, left);
			border.addEventListener('click', stopTracking);
		}
		else {
			border.title = isEn()
				? 'Click to start tracking'
				: 'Нажмите чтобы начать отслеживание';
			border.addEventListener('click', startTracking);
		}
	}
    return border;
}

function getScoreRange(score, scale) {
    if (!checkScale(scale)) {
        return;
    }

    var initialValue = 0;
    var finalValue = score;
    var level = 0;
    for (var ii = 0; ii < scale.length; ii++) {
        if (score >= scale[ii]) {
            initialValue = scale[ii];
        }
        else {
            finalValue = scale[ii];
            level = ii;
            break;
        }
    }
    return { Initial: initialValue, Final: finalValue, Level: level };
}

function getProgressPercentage(range, score) {
    if (range) {
        return (score - range.Initial) * 100 / (range.Final - range.Initial);
    }
    return 0;
}

function getScale(index) {
    return Scales[index];
}

function getCaption(value, exclude) {
    if (exclude) {
        var r = new RegExp(exclude, 'i');
        return value.Caption.replace(r, '');
    }
    return value.Caption;
}

function createRow(value, scaleIndex, excludeCaption) {
    var scale = getScale(scaleIndex);
    var range = getScoreRange(value.Score, scale);
    if (!range) {
        alert(JSON.stringify(value) + ' : ' + scaleIndex + ' - ' + excludeCaption);
    }

    var row = document.createElement('tr');
    var c1 = document.createElement('td');
    c1.className = 'key-column';
    if (scaleIndex == 1 && value.Sign) {
        c1.style = 'font-weight: bold; text-decoration:underline; ';
    }
    c1.appendChild(document.createTextNode(getCaption(value, excludeCaption)));
    var lb = document.createElement('b');
    lb.className = 'skill-value';
    lb.appendChild(document.createTextNode(value.Level));
    if (value.Score >= range.Final) {
        lb.className = 'skill-value-complete';
    }
    else if (!value.Score) {
        lb.className = 'skill-value-none';
    }
    c1.appendChild(lb);
    row.appendChild(c1);
    var c2 = document.createElement('td');
    c2.className = 'progress-column';
    if (value.Score == 0) {
        c2.appendChild(document.createTextNode('-'));
    }
    else if (value.Score >= range.Final) {
        c2.appendChild(document.createTextNode(value.Score));
    }
    else {
        var percentage = getProgressPercentage(range, value.Score);

        var points = value.Score;
        var left = round(range.Final - value.Score, 1);

        if (range.Level > value.Level) {
            percentage = 100;
            left = '0.1';
            var rl = document.createElement('small');
            rl.innerHTML = -(range.Level - value.Level);
            lb.className = 'skill-value-low';
            c1.appendChild(rl);
        }
        var pb = createProgressBar(points, left, percentage, value.Caption);
        c2.appendChild(pb);
    }
    row.appendChild(c2);
    return row;
}

function createExpanderButton() {
    var cb = document.createElement('input');
    cb.type = 'button';
    cb.value = expandCaption;
    cb.className = 'expander';
    cb.collapsed = true;
    cb.onclick = function (event) {
        var r = this.parentNode.parentNode;
        var display = this.collapsed ? '' : 'none';
        r.nextSibling.style.display = display;
        r.nextSibling.nextSibling.style.display = display;
        r.nextSibling.nextSibling.nextSibling.style.display = display;
        this.collapsed = !this.collapsed;
        this.value = this.collapsed ? expandCaption : collapseCaption;
    };
    return cb;
}

function insertExpander(table) {
    var row = table.lastChild;
    for (var ii = 0; ii < 3; ii++) {
        row.firstChild.style = 'padding-left: 15px;';
        row.style.display = 'none';
        row = row.previousSibling;
    }
    row.firstChild.appendChild(createExpanderButton());
}

function isEn() {
    return /^www\.lordswm\.com/.test(location.host);
}

function replaceSkills() {
    var home = document.getElementById('home_2');
    if (home) {
        var createSkillTable = function(caption) {
            var result = document.createElement('table');
            result.className = 'skill-table';
            var cpt = document.createElement('caption');
            cpt.appendChild(document.createTextNode(caption));
            result.appendChild(cpt);
            return result;
        }

        var mainNode = home.parentNode;
        var items = parseSourceCode(mainNode.innerHTML.toString());

        var range = document.createRange();
        range.selectNodeContents(mainNode);
        range.deleteContents();

        var t = createSkillTable(isEn() ? 'Factions' : factionsGroupTitleRU);
        var excludeCaption;
        var ii = 0;
        var gi = 0;
        var faction = true;
        var factionPoints = 0;
        do {
            if (faction && (~items[ii].Caption.indexOf(isEn() ? 'guild' : '\u0413\u0438\u043B\u044C\u0434\u0438\u044F'))) {
                faction = false;
                gi = ii;
                var sum = document.createElement('span');
                sum.appendChild(document.createTextNode(' \u03A3'));
                var sub = document.createElement('sub');
                sub.appendChild(document.createTextNode(isEn() ? 'all' : allRu));
                sum.appendChild(sub);
                sum.appendChild(document.createTextNode('= ' + round(factionPoints, 2)));
                t.firstChild.appendChild(sum);
                mainNode.appendChild(t);
                t = createSkillTable(isEn() ? 'Guilds' : guildsGroupTitleRU);
                excludeCaption = "('\\sguild)|(" + guildsGroupTitleRU + ')';
                mainNode.appendChild(t);
            }
            if (faction) {
                factionPoints += items[ii].Score;
            }
            t.appendChild(createRow(items[ii], (faction ? 1 : Math.min(Scales.length - 1, (ii - gi) + 2)), excludeCaption));
            ii++;            
        }
        while (ii < items.length - 2);
        insertExpander(t);
        mainNode.appendChild(t);
    }
}

function getLevelPoints(points) {
    var r = /\(([\d\,]+)\)(?:\s\+(-?[\d\,]+))?/;
    var m = r.exec(points)
    if (m) {
        return { Points: m[1].replace(/,/g, ''), Left: (m[2] ? m[2].replace(/,/g, '') : 0) }
    }
}

function insertLevelUpProgressBar() {
    var bs = document.getElementsByTagName('b');
    var lbReg = new RegExp('(?:Combat\\slevel|' + levelStringRU + '):\\s(\\d+)');
    for (var ii = 0; ii < bs.length; ii++) {
        var b = bs[ii];
        var m = lbReg.exec(b.innerHTML);
        if (m) {
            var start = b.nextSibling;
            var end = start;
            do {
                end = end.nextSibling;
            }
            while (end && (!end.tagName || end.tagName.toLowerCase() != 'br'));
            var next = end.nextSibling;
            if (end) {
                var pointsText;
                var range = document.createRange();
                range.setStart(start, 0);
                range.setEnd(end, 0);
                pointsText = range.toString();
                var lvl = getLevelPoints(range.toString());
                if (lvl) {
                    var r = getScoreRange(lvl.Points, getScale(0));
                    if (r && r.Final > lvl.Points) {
                        var percentage = getProgressPercentage(r, lvl.Points);
                        if (r.Level > m[1]) {
                            b.appendChild(document.createTextNode('+'));
                            percentage = 100;
                        }
						currentState.level = lvl.Points;
                        var pb = createProgressBar(lvl.Points, lvl.Left, percentage, 'level');
                        pb.className += ' levelpb';
                        b.parentNode.insertBefore(pb, b.nextSibling);
                        range.deleteContents();
                    }
                }
                range.detach();
                break;
            }
        }
    }
}

addStyles(); insertLevelUpProgressBar(); replaceSkills();