Dark Voice

Темный скин golos.io с монитором активности пользователей из blockchain

// ==UserScript==
// @name Dark Voice 
// @namespace golos.io 
// @description Темный скин golos.io с монитором активности пользователей из blockchain
// @description:ru Темный скин golos.io с монитором активности пользователей из blockchain
// @grant none
// @version 0.23
// @include https://golos.io/*
// @require https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js
// ==/UserScript==
// 
var myname = 'vik'; // Поменяйте на свое имя и переместите сокеты выше - будет работать быстрей :)
var socket = new WebSocket('wss://ws.golos.io'),
    account = myname;
startblock = 0;
socket.onopen = function(event) {
    socket.send(JSON.stringify({
        id: 1,
        method: 'get_dynamic_global_properties',
        'params': []
    }));
};
socket.onmessage = function(event) {
    data = JSON.parse(event.data);
    startblock = data.result.last_irreversible_block_num;
};


// Прописываем CSS и HTML
$('.Header__top-steemit').html('<a href="/@vik/">Dark Voice<span class="beta">@vik</span></a>');
var inlinecss = "<style>.Voting__button path,.Voting__button-down a:hover path{fill:#fff}#opclose,.svote a{vertical-align:middle}.Header,body{background-color:#36465d;color:#fff}.VerticalMenu>li>a,.menu a{color:#fff}.VerticalMenu>li>a:hover{color:#101823;line-height:1.5rem}.Header__sub-nav .active a,.Header__sub-nav li:hover a,.PostSummary__header>h2>a,.PostsIndex__topics,.PostsIndex__topics small a,.Topics__title{color:#fff}.Header__sub-nav li>a{color:#fff;border:0;line-height:1.2em;font-size:.9em;background-color:#101823}.Header__sub-nav li{padding:.5rem 0;position:relative;overflow:hidden}.menu .buttons{display:none}.Header__sub-nav li>a:after{content:'';width:0;height:2px;background:#fff;z-index:1;position:absolute;bottom:0;left:0;transition:.6s all ease}.Header__sub-nav li:hover>a:after{content:'';width:100%}.Header .menu,.Header__sub-nav,.Header__top{background-color:#101823;border-bottom:1px solid #0b1119}#content{background:#36465d}.PostsIndex__topics{border-radius:3px;padding:5px;transition:.4s all ease;height:35px;overflow:hidden;position:fixed;top:55px;border:0;right:-160px;background:#184e86;box-shadow:0 0 35px -10px #000;z-index:700}ul.Topics li a{background:#fff;padding:0 3px;border-radius:3px;margin:3px}.PostsIndex__topics:hover{box-shadow:0 0 75px -10px #000;height:450px;overflow:auto;right:-30px}img.PostSummary__image{width:100%;max-width:640px;height:auto;border:0;padding:0;float:none;display:block}.PostSummary{position:relative;margin:0 2% 50px 0;border-radius:3px;box-shadow:0 15px 50px -15px #000;max-width:580px;background:#101823}.PostSummary.with-image .PostSummary__content,.PostSummary.with-image .PostSummary__reblogged_by,.PostSummary__content{margin:.5rem}.PostSummary__collapse{position:absolute;right:5px;top:5px}.PostSummary__body.entry-content{white-space:normal;color:#a6acb5;margin:0 0 30px}.PostSummary__footer{color:#fff}.Voting__inner{border:0}.Voting__button circle{stroke:#fff}.Voting__button-up>a:hover circle,a.confirm_weight:hover circle{fill:#4ba2f2;stroke:#4ba2f2}.Voting__button-up .Icon{box-shadow:0 0 15px rgba(0,0,0,.7);border-radius:100%;transform:scale3d(1,1,1) rotate(0);transition:all 1s cubic-bezier(.4,0,0,1.67)}.PostSummary:hover span.usersg{position:relative;transition:1s all ease}.PostSummary:hover .Voting__button-up .Icon{box-shadow:0 0 25px rgba(0,0,0,.7);transform:scale3d(2.2,2.1,2.6) rotate(720deg)}.dropdown-pane{background:#101823;border:0;box-shadow:0 0 20px #000;transition:1s all ease;color:#fff}.vcard>strong a,span.Author{font-weight:200;text-shadow:none;border-radius:10px;color:#fff}#closeop,.ReplyEditor{box-shadow:0 0 30px -10px #000}.Voting__adjust_weight .weight-display,.Voting__adjust_weight_down .weight-display{margin-left:20px;color:#fff;font-size:1.5rem}.Voting__adjust_weight,.Voting__adjust_weight_down{padding:10px 5px;margin-right:0;width:400px;overflow:hidden}.vcard>strong a{background:#4ba2f2;padding:0 7px 1px}span.Author{position:relative;background:#484848;padding:1px 6px 2px 12px}.Reputation{font-size:.8rem;border:0;border-radius:5px;background:#ffae00;color:#fff;padding:0 6px 0 3px}.vcard>strong a:before{content:'#';font-size:.6rem;margin-right:3px;color:#fff}.PostSummary__time_author_category{font-size:.7rem;border:0}.VotesAndComments{float:right}.PostsIndex.row{max-width:100%}.Voting__inner{margin-right:0}.grid-item{float:left;transition:.4s all ease}.grid-item,.grid-sizer{width:22%}.gutter-sizer{width:4%}#showanswer{position:fixed;right:26px;top:118px;background:#063465;color:#fff;font-size:14px;border-radius:3px;padding:1px 15px;z-index:400}span.usersg{transition:all .4s ease;background:#009c64;padding:1px 5px 2px}#drkloader{width:auto;display:block;height:100%;overflow:auto;overflow-x:hidden}#drkwidget,.answerholder{position:fixed;width:50%}.ReplyEditor{background:#fff;padding:20px 0;border-radius:5px;color:#000}ul#drkstream{margin:0;padding:0 15px;background:rgba(1,14,31,.9);height:90%;overflow-x:auto;overflow-y:auto}#drkstream li{display:none;list-style-type:none;font-size:.8rem}.svote a{background:#5974ff;color:#fff;padding:1px 5px;border-radius:10px;line-height:0;overflow:hidden;font-size:11px;font-weight:700}.scomment i{background:green;font-style:normal;padding:0 4px;border-radius:15px}.Voting span{color:#1dd492;text-shadow:0 0 3px #19943e}.drklog p{line-height:1;margin:0;background:#184e86;padding:10px}.insertsila strong{font-size:20px;background:url(https://golos.io/images/golospower-badge.jpg) no-repeat #fff;background-size:contain;padding:0 15px 0 35px;color:#1b5d9d;border-radius:30px}.drklog{text-align:center}#drkwidget{z-index:100;display:none;background:url(https://s28.postimg.org/bvu4yda25/endless_3d_cube_gif.gif) no-repeat #184e86;background-size:cover;top:110px;right:15px;height:600px;height:calc(100vh - 180px);border-radius:3px;box-shadow:0 0 10px -1px #000;overflow:hidden}ul#drkstream li b{background:rgba(75,162,242,.14);padding:1px 8px;border-radius:10px;font-weight:200}ul#drkstream li strong{background:rgba(114,255,98,.51);padding:1px 8px;border-radius:10px}.scomment div,.svote div{font-size:.8rem;height:.8rem;overflow:hidden;vertical-align:middle;line-height:.8rem;max-width:200px;display:inline-block;color:#3b4a5a}#closeop,#drkmenu,#drkstory,.answertarget .Voting{display:none}#drkmenu{text-align:center}#post_overlay{color:#000}.answerholder{z-index:500;background:#101823;display:none;top:150px;padding:15px;right:14px}#closeop,#opclose{background:#063465;border-radius:3px;font-size:14px}.answertarget{overflow:auto;height:500px;width:740px}#opclose{position:absolute;left:20px;top:10px;padding:3px 15px 5px;line-height:14px}#closeop{z-index:400;position:fixed;top:150px;right:26px;padding:5px 10px}.beforeload{transition:1s all}#drkstream::-webkit-scrollbar,body::-webkit-scrollbar{width:7px;height:9px}#drkstream::-webkit-scrollbar-track,body::-webkit-scrollbar-track{background:#053363}#drkstream::-webkit-scrollbar-thumb,body::-webkit-scrollbar-thumb{background:rgba(255,255,255,.4)}#drkstory{overflow:auto;height:100%;opacity:.98}#drkstory tbody,.UserWallet tbody{border:0!important;background:#101823}#drkstory table tbody tr:nth-child(even),.UserWallet table tbody tr:nth-child(even){background-color:#063465}.UserWallet__balance.row{background:#fff;padding:15px;color:#000;font-weight:700}.reveal.fade.in{background:#20242a;color:#fff;border:0;box-shadow:0 0 50px #000}a.go-top{position:fixed;background:#334258;bottom:10px;left:10px;color:#fff;padding:10px;box-shadow:0 0 20px #000;border-radius:3px}.answertarget .PostSummary{position:relative;margin:10px 0;border-radius:3px;box-shadow:0 15px 50px -15px #000;max-width:660px;background:#101823;color:#fff}.answertarget .PostSummary__body.entry-content{color:#fff;margin:0}.Comment .highlighted { background: #415879; padding: 5px; border-radius: 10px; }.Post a { color: #9cd0ff; }</style>";
var somehtml = '<a href="#0" class="go-top">Вверх</a><div id="drkwidget"><div class="drklog"><p id="silag"><span class="insertsila"><strong>Сила Голоса...</strong></span></p><span id="opclose">Закрыть</span><span id="currblock">Загружаем текущий блок...</span><span id="dwitness"></span></div><div id="drkmenu">Моя история</div><div id="drkstory"></div><ul id="drkstream"><li class="beforeload"><center>Подключаемся к ноде... Для корректной работы используйте только одну вкладку Голоса</center></li><li class="beforeload2"><center>В этой ноде идет трансляция действий пользователей: комментарии, голосование, упоминания, фолловинг и другое. При низком качестве подключения возможны потери событий из трансляции. Для минимизации потерь не открывайте несколько вкладок Голоса одновременно!</center></li></ul></div><div id="closeop">Мониторинг</div>';
$(inlinecss).appendTo("head"); // Устанавливаем CSS стили
$(somehtml).appendTo("body"); // Вставляем необходимые html элементы

// Меняем в постах маленькие фото на большие: 256x128 > 640x480 
function imgtoggle() {
    var tinyimg = new RegExp('/256x128/'), // Берем ссылки на маленькие превью
        bigimg = new RegExp('640x480'); // Меняем в ссылка значение разрешения фотографии, обращаясь к проксификатору по ссылке https://imgp.golos.io/640x480/
    $('img.PostSummary__image').each(function() {
        var image = $(this);
        image.attr('src', image.attr('src').replace(tinyimg, bigimg)); // Тут происходит ранее описанная замена 256x128 на 640x480
    });
}
imgtoggle();
setTimeout(function() {
    $('#drkwidget').slideDown();
}, 2000);
$('#opclose').on('click', function() {
    $('#drkwidget').slideUp();   $('.PostsIndex.row' ).css('max-width',' 80%');
    $('#closeop').slideDown();
});
$('#closeop').on('click', function() {
    $('#drkwidget').slideDown();
    $(this).slideUp(); $('.PostsIndex.row' ).css('max-width',' 100%');
});




function drkwait() {
    socket = new WebSocket('wss://ws.golos.io'); account = myname;
    var movementTotal, silaTotal;
    socket.onopen = function(event) {
        socket.send(JSON.stringify({
            id: 1,
            method: 'get_dynamic_global_properties',
            'params': []
        }));
    };
   socket.onmessage = function(event) {
        var silaGolosa, powerMovement, data = JSON.parse(event.data),
            stream = document.getElementById('drkstream');
        lastblock = startblock;
        merkle = data.result.transaction_merkle_root;
        if (merkle !== 0 && startblock > 100) {
            if (data.id === 1) {
                movementTotal = data.result.total_vesting_shares.split(' ')[0];
                silaTotal = data.result.total_vesting_fund_steem.split(' ')[0];

                socket.send(JSON.stringify({
                    id: 2,
                    method: 'get_accounts',
                    params: [
                        [account]
                    ]
                }));
                socket.send(JSON.stringify({
                    id: 3,
                    method: 'get_block',
                    'params': [lastblock]
                }));
                //  socket.send(JSON.stringify({ id: 4, method:'get_account_history', params: [ [account] ] })); История аккаунта
                document.getElementById('currblock').innerHTML = '<span class="insertblock"><strong>' + lastblock + '</strong> Последний блок. </span>';
            }
            if (data.id === 2) {
                powerMovement = data.result[0].vesting_shares.split(' ')[0];
                silaGolosa = silaTotal * (powerMovement / movementTotal);
                document.getElementById('silag').innerHTML = '<span class="insertsila"><strong>' + silaGolosa + '</strong></span>';
            }

            if (data.id === 3) {
                tx = data.result.transactions[0].operations[0][1];
                wtnss = data.result.witness;
                drkvoter = JSON.stringify(tx.voter);
                drkpower = JSON.stringify(tx.weight);
                drkauthor = JSON.stringify(tx.author);
                drklink = JSON.stringify(tx.permlink);
                drkcomm = JSON.stringify(tx.parent_author);
                drkbodycomm = JSON.stringify(tx.body);
                drknewbie = JSON.stringify(tx.new_account_name);
                drkfl = tx.json; // Фолловинг (подписки)
                mention = JSON.stringify(tx.to); // Mention упоминания
                mentmemo = JSON.stringify(tx.memo);
                document.getElementById('dwitness').innerHTML = '<span class="inserwtnss"> В ноде <strong>' + wtnss + '</strong></span>';
                if (typeof mentmemo !== "undefined") {
                    memocut = mentmemo.slice(26);
                    stream.insertAdjacentHTML('afterbegin', '<li class="sfollow"><b>' + mention + '</b> упомянул ' + memocut + '</li>');
                }
                if (typeof drknewbie !== "undefined") {

                    stream.insertAdjacentHTML('afterbegin', '<li class="snewbie">Новый пользователь > <strong>' + drknewbie + '</strong>!</li>');
                }
                if (typeof drkcomm !== "undefined" && typeof drkauthor !== "undefined") {
                    stream.insertAdjacentHTML('afterbegin', '<li class="scomment"><b>' + drkauthor + '</b> комментирует пост <strong>' + drkcomm + '</strong> <div>' + drklink + '</div></li>');
                }
                if (typeof drkvoter !== "undefined" && typeof drkauthor !== "undefined") {
                    stream.insertAdjacentHTML('afterbegin', '<li id=' + drkauthor + ' class="svote"><b>' + drkvoter + '</b>  <a title=' + drklink + ' href=' + drklink + '>></a>  голосует с силой ' + drkpower + ' за <strong>' + drkauthor + '</strong>    <div>' + drklink + '</div></li>');
                }
                if (typeof drkfl !== "undefined") {
                    df = JSON.parse(drkfl);
                    dfollower = df[1].follower;
                    dfollowin = df[1].following;
                    stream.insertAdjacentHTML('afterbegin', '<li class="sfollow"><b>' + dfollower + '</b> подписка на <strong>' + dfollowin + '</strong></li>');

                }
            }

        }



    };
}




// Получаем параметры, который пользователь может добавить в конец своей ссылки на аватар.  Синтаксис //domain.tld/img.jpg?css=[css стили] 
// Так же получаем юзернэйм для которого нужно показывать блок ответов
function waitfor() { // Внутри функции будем ждать полной загрузки страницы и проверять когда появятся необходимые нам ссылки

    var Upic = $('.Userpic');
    var userpicUrl = $('.Userpic img').attr('src'); // Проверяем ссылку на аватар
    var usernameUrl = Upic.parent().attr('href'); // Проеверяем ссылку на юзернэйм. Нам нужно получить из нее ник текущего пользователя, что бы ответы в виджет загружались именно для вас. 
    // Если ссылки уже доступны, выдираем только нужные нам параметры:  
    // Фильтруем и получаем параметры, который пользователь добавил в своем аккаунте в конец ссылки на аватар  например //вк.ком/рукалицо.jpg?css=[#content{background:black;}] сделает фон контента черным

    function getParameterByName(name, url) {
        if (!url) {
            url = userpicUrl;
        }
        name = name.replace(/[\[\]]/g, "\\$&");
        var regex = new RegExp("[?&]" + name + "(=([^&]*)|&||$)"),
            results = regex.exec(url);
        if (!results) return null;
        if (!results[2]) return '';
        return decodeURIComponent(results[2].replace(/\+/g, " "));
    }
    // Функция с помощью который мы загрузим ответы в будущем.
    function answerupdate() {
        $('.answertarget').load(usernameUrl + '/recent-replies/ #posts_list');
    }
if (Upic.length) {
  
  myname = usernameUrl.slice(2);
 var isVisible = function(obj) {
            return obj.offsetWidth > 0 && obj.offsetHeight > 0;
        };
        var curry = function(uncurried) {
            var parameters = Array.prototype.slice.call(arguments, 1);
            return function() {
                return uncurried.apply(this, parameters.concat(Array.prototype.slice.call(arguments, 0)));
            };
        };
        var getAndAddToCallbacks = function(socket, callbacks, method, params, callback) {
            callbacks.push(callback);
            var data = {
                id: callbacks.length - 1,
                method: method,
                params: params
            };
            socket.send(JSON.stringify(data));
        };
        var runFun = function(funs, id, data) {
            funs[id](data);
        };
        var socket = new WebSocket('wss://ws.golos.io'),
            callbacks = [];
        var getResult = curry(getAndAddToCallbacks, socket, callbacks),
            runCallback = curry(runFun, callbacks);
        socket.onopen = function(event) {
            getResult('get_dynamic_global_properties', [], function(data) {
                var totalVestingShares = data.result.total_vesting_shares.split(' ')[0],
                    totalVestingFundSteem = data.result.total_vesting_fund_steem.split(' ')[0];
                var nodes = [].slice.call(document.querySelectorAll('[itemprop=author] strong')).reduce(function(nodes, node) {
                    if (!isVisible(node) || node.ssp__hasSp) {
                        return nodes;
                    }
                    nodes.push(node);
                    return nodes;
                }, []);
                var accounts = nodes.map(function(node) {
                    return node.innerHTML;
                });
                getResult('get_accounts', [accounts], function(data) {
                    data.result.forEach(function(account, i) {
                        var vestingShares = account.vesting_shares.split(' ')[0];
                        var steemPower = totalVestingFundSteem * (vestingShares / totalVestingShares);

                        var parent = nodes[i].parentNode;

                        console.log(account.name + ' ' + steemPower);
                        nodes[i].outerHTML = '' + account.name + ' <span class="usersg">' + Math.round(steemPower) + '</span>';
                        nodes[i].ssp__hasSp = true;


                    });
                    
                });
            });




        };
       socket.onmessage = function(event) {
            var data = JSON.parse(event.data);
            runCallback(data.id, data);
        };
       setInterval(function() {
            startblock++;
            drkwait();

            imgtoggle();
            $('#drkstream li,#drkmenu').slideDown();


        }, 5000); // Проверяем новые блоки. 



        $('.Header__top-steemit').html('<a href="/@vik/">Dark Voice<span class="beta">@vik</span></a>');
        $('.beforeload').css('opacity', '0');

       
      
        




        // Даем возможность обновлять ответы нажатием кнопки "Обновить ответы"
        $('<div class="answerholder"><p id="answerupdate">Обновить ответы</p><div class="answertarget">Загрузка...</div></div><div id="showanswer">Мои ответы</div>').appendTo("body");

        $('#answerupdate').on('click', function() {
            answerupdate();
        });

        // Даем возможность открывать/закрывать блок ответов
        var isActive = true;
        $('#showanswer').on('click', function() {
            if (isActive) {
                answerupdate();


                $('.answerholder').slideDown();
            } else {
                $('.answerholder').slideUp();
            }
            isActive = !isActive;

        });


        // моя история

        $('#answerupdate').on('click', function() {
            answerupdate();
        });

        // Даем возможность открывать/закрывать блок ответов
      
        $('#drkmenu').on('click', function() {
            if (isActive) {
                $('#drkstory').load(usernameUrl + '/transfers/ table'); // загрузка истории
                $('#drkstory').slideDown();
            } else {
                $('#drkstory').slideUp();
            }
            isActive = !isActive;

        });



        // Получаем то, что после ?css= и перед &  


        // Убираем квадратные скобки по бокам из переменной
        var cleaning = getParameterByName('css');

        if (cleaning !== null) {
            var stringcss = cleaning.substring(1, cleaning.length - 1);
            // Оборачиваем в теги стиля    

            // Если параметр существует 
            var cleanCSS = $('<style></style>').append(stringcss);
            // Подключаем наш кастом стиль. На данный момент он применяется только после полной загрузки страницы, но в следующей версии скрипта я это исправлю.
            cleanCSS.appendTo("head");
        }
        //C остальными пармаетрами проще. Можно получить параметр после каждого & в формате //вк.ком/рукалицо.jpg?option1=ON&option2=OFF&option3=BIG
        // Пока опции есть как пример и демо функционала, осталось придумать какие опции могуть быть нужны. Например фиксировать ли кнопки редактора или нет. Или отключить большинсво ненужных элементов. 
        // В качестве примера отключим топики - метки в сайдбаре 
        var notopics = getParameterByName('topics');
        if (notopics == 'OFF') {
            $('.PostsIndex__topics').slideUp(); // Топики улетают
            // Добавляем кнопку, по нажатию которой топики выпадут вниз
            $('<div id="slidetopics">Топики</div>').appendTo("body");

            $('#slidetopics').on('click', function() {
                if (isActive) {

                    $('.PostsIndex__topics').slideDown();
                } else {
                    $('.PostsIndex__topics').slideUp();
                }
                isActive = !isActive;
            });

        }
        // Для отключения топиков вставьте в ссылку аватара параметр &notopics=OFF 
        // В следующей версии скина будет больше параметров и настроек  


    }
    // Так как мы все еще внутри условия "Если аватарка уже загрузилась" , то если она не загрузилась - мы будем рекурсивно проверять и ждать ее каждую секунду   
    else {
        setTimeout(waitfor, 1000);
    }
}
waitfor();

// Добавляем кнопку-стрелку возвращения в начало страницы
$(document).ready(function() {
    // Show or hide the sticky footer button
    $(window).scroll(function() {
        if ($(this).scrollTop() > 200) {
            $('.go-top').fadeIn(200);

        } else {
            $('.go-top').fadeOut(200);
        }
    });

    // Animate the scroll to top
    $('.go-top').click(function(event) {
        event.preventDefault();


        $('html, body').animate({
            scrollTop: 0
        }, 300);
    });
});


// Сообщение в консоли браузера.
console.log('Вы установили userscript от golos.io/@vik и сейчас вы смотрите консоль :) ВНИМАНИЕ! Перед установкой этого или похожего скрипта убедитесь, что вы нашли его в надежном источнике! Если вы не уверены, что скрипт оригинальный - обратитесь к опытным пользователям и попросите проверить его на предмет внедрения вредного кода. Вы можете обратиться ко мне в голосе golos.io/@vik/ ');