// ==UserScript==
// @id habrActivity
// @name habrActivity
// @version 5.2014.12.31
// @author spmbt0
// @description view user activity in Habr comments for Fx-Opera-Chrome
// @include http://habrahabr.ru/*
// @include http://geektimes.ru/*
// @exclude http://habrahabr.ru/api/*
// @update 4 win.console; remove panel from Geektimes articles;
// @icon data:image/gif;base64,R0lGODlhIAAgAMMBAG6Wyv///2+NtIucstfY2b/FzpSmvY+QkM3Nzunp6fLy8qGwweDg4MbFxa2trrm6uiwAAAAAIAAgAAAE/xDISau9OM/AOe2edoHBBwqiRZodmrKhRLqXYFfrdmLCQBQGWk62swgOiERAQQgChs9iRZBMKDgEFGnbMi4YDMU1gNBytzSJDcGwXhUD4lmqZofFioZrPqMIDARtYksIAzZ8dAINgngJVgkLUH1qBmBuCgmBYA6SUgKBl0wICA6lk1FdAAIFjngKDAgEpKYgWXIcKH8EDQ0EVwmjsrycIA4FZl2rDwcHDgivow8ODwzEHca3ASgDpMylsrEOzdUkDk59AtOl07wIDcwNkDbzCy7z8xIDD8Ps3Q5hCQqscxBHgw0DbEY1WIbEkRtHZV6oMsAq0wNqrcQ4KihR1Z9YjzUeKjjWcYqABUoaJeBY0k8bAm5ItqxgANjFBnBmTgnTQNw0nVOSNBjQLA1QXdEMATVioGnJCAA7
// @namespace https://greasyfork.org/users/2323
// ==/UserScript==
(function(win, noConsole, css, hActHelp){
var d = document
,dBody = d.body || d.documentElement
,$q = function(q, f){return (f||d).querySelector(q)}
,uHead = $q('.user_header')
,uName2a = $q('.username a', uHead)
,uName2 = uName2a && uName2a.innerHTML
,comms = $q('.comments_list');
if(uName2 && comms && $q('.user_comments')){ //=====(всё работает только на странице комментариев некоторого пользователя)=====
try{ //для оповещения об ошибках в Fx
var NOWdate = new Date()
,lh = location.href
,HRU =location.protocol +'//'+ location.host
,lStorRoot ='habrAct_'
,setLocStor = function(name, hh){ if(!win.localStorage) return;
localStorage[lStorRoot + name] = JSON.stringify({h: hh});
}
,getLocStor = function(name){
return (JSON.parse(localStorage && localStorage[lStorRoot + name] ||'{"h":{}}')).h;
}
,removeLocStor = function(name){localStorage.removeItem(lStorRoot + name);}
,$qA = function(q, f){return (f||d).querySelectorAll(q)}
,$pd = function(ev){ev.preventDefault();}
,$sp = function(ev){ev.stopPropagation();}
,$x = function(el, h){ //===extend===
if(h)
for(var i in h)
el[i] = h[i];
return el;
}
,$e = function(g){ //===создать или использовать имеющийся элемент===
//g={el|clone,IF+ifA,q|[q,el],cl|(clAdd,clRemove),ht,cs,at,on,revent,apT,prT,bef,aft,f+fA}
if(g.ht || g.at){
var at = g.at ||{}; if(g.ht) at.innerHTML = g.ht;}
if(typeof g.IF =='function')
g.IF = g.IF.apply(g, g.ifA ||[]);
g.el = g.el || g.clone || g.IF && g.IF.attributes && g.IF ||'DIV';
var o = g.clone && g.clone.cloneNode(!0)
|| (typeof g.el =='string' ? d.createElement(g.el) : g.el);
if(o && (g.IF===undefined || g.IF) && (!g.q || g.q && (g.dQ = g.q instanceof Array ? $q(g.q[0], g.q[1]) : $q(g.q)) ) ){ //выполнять, если существует; g.dQ - результат селектора для функций IF,f
if(g.cl)
o.className = g.cl;
else{
if(g.clAdd)
o.classList.add(g.clAdd);
if(g.clRemove)
o.classList.remove(g.clRemove);
}
if(g.cs)
$x(o.style, g.cs);
if(at)
for(var i in at){
if(i=='innerHTML') o[i] = at[i];
else o.setAttribute(i, at[i]);}
if(g.on)
for(var i in g.on)
o.addEventListener(i, g.on[i],!1);
if(g.revent)
for(var i in g.revent)
o.removeEventListener(i, g.revent[i],!1);
g.apT && g.apT.appendChild(o); //ставится по ориентации, если новый
g.prT && (g.prT.firstChild
? g.prT.insertBefore(o, g.prT.firstChild)
: g.prT.appendChild(o) );
g.bef && g.bef.parentNode.insertBefore(o, g.bef);
g.aft && (g.aft.nextSibling
? g.aft.parentNode.insertBefore(o, g.aft.nextSibling)
: g.aft.parentNode.appendChild(o) );
if(typeof g.f =='function')
g.f.apply(g, g.fA ||[]); //this - это g
}
return o;
}
,parents = function(cl, elem){
for(var el = elem; el!=null && !RegExp(cl).test(el.className); el = el.parentNode);
return el;
}
,prev = function(cl, elem){
for(var el = elem; el!=null && !RegExp(cl).test(el.className); el = el.previousSibling);
return el;
}
,next = function(cl, elem){
for(var el = elem; el!=null && !RegExp(cl).test(el.className); el = el.nextSibling);
return el;
}
,toTime = function(dat){
var yestDate = new Date(+NOWdate - 86400000)
,datMonth ={"января":0,"февраля":1,"марта":2,"апреля":3,"мая":4,"июня":5,
"июля":6,"августа":7,"сентября":8,"октября":9,"ноября":10,"декабря":11}
,datFull = dat.innerHTML
,datYest = /вчера/.test(datFull)
,dateText = datFull.replace(/ в /,' ').replace(/(сегодня |вчера )/,'')
,datArr = dateText.match(/(\d+)\s+([а-яё]+)\s+(\d{4})?/i);
if(!datArr)
datArr =[0,(datYest ? yestDate : NOWdate).getDate(),(datYest ? yestDate : NOWdate).getMonth(),(datYest ? yestDate : NOWdate).getFullYear()];
var altArr = dateText.match(/(\d+)\:(\d+),([а-яё]+)\s*(\d+)\s*([а-яё]+)\s*(\d{4})?/i); //ччммддЧЧММГГГГ?
if(altArr)
datArr =[0,altArr[4],altArr[5],altArr[6]];
//'altArr'.wcl(altArr)
var mon = datArr && datMonth[datArr[2]] || datMonth[datArr[2]] !=0 && datArr[2] || datMonth[datArr[2]];
//'datArr'.wcl(datArr, mon)
if(datArr && !datArr[3])
datArr[3] = NOWdate.getFullYear();
var ret2 = new Date(datArr[3], mon, datArr[1], dateText.replace(/(.*?)(\d{1,2}):(\d\d)(.*)/,'$2'), dateText.replace(/(.*?)(\d{1,2}):(\d\d)(.*)/,'$3') ).getTime();
if(+NOWdate < ret2)
ret2 = new Date(datArr[3] -1, mon, datArr[1], dateText.replace(/(.*?)(\d{1,2}):(\d\d)(.*)/,'$2'), dateText.replace(/(.*?)(\d{1,2}):(\d\d)(.*)/,'$3') ).getTime();
return ret2;
}
,getYearWeekDay = function(dd){
var d = new Date(dd), jan1 = new Date(d.getFullYear(),0,1);
return [d.getFullYear(), Math.ceil(((dd - jan1) /86400000 + jan1.getDay() -1)/7)
, d.getDay() + 7*(d.getDay()==0), d.getDate(), d.getMonth()];
}
,wcl = function(a){ a = a ||''; //консоль как метод строки или функция, с отключением по noConsole
if(win.console && (!noConsole || this =='ER_global:'))
win.console.log.apply(win.console, this instanceof String
? ["'=="+ this +"'"].concat([].slice.call(arguments)) : arguments);
};
String.prototype.wcl = wcl;
var hActUsersTmpl ={ //шаблон основной (индексной) записи в хранилище
dataLen: 0 //число записей в хранилище вида habrActDataNNNNN, где NNNNN - число
,dataCount: 25 //огранчитель цикла чтения, страниц
,dateStart: 1000 //ограничитель периода чтения, до 365 дней, сравнивает дату начала цикла и посл.
,userS: [] //массив имён пользователей, для которых имеются данные
}
,readAutoIntervalMax = 365
,hActDataTmpl ={ //шаблон записи данных в хранилище; запись имеет вид habrActData[число], от 1 до dataLen
user:'' //пользователь, для которого со страницы комментариев сняли данные
,data: [] //массив дат (или сложнее)
};
(function(css){ var u = 'undefined'; //addRules
if(typeof GM_addStyle !=u){ GM_addStyle(css);
}else if(typeof addStyle !=u){ addStyle(css);
}else{
var node = d.createElement('style');
node.type ='text/css';
node.appendChild(d.createTextNode(css));
(d.getElementsByTagName('head')[0] || dBody).appendChild(node);}
})(css);
var readPage
,ww =0
,startButt = $e({el:'button', ht:'Старт'
,on:{click: readPage = function(ev){ //читать статистику со страницы и переходить к следующей
if(ev){
var handStart =1;
hActUsers = getLocStor('users');
hActUsers.userS = hActUsers.userS ||[];
hActUsers.dataLen = hActUsers.dataLen ||0;
}
var infoS = $qA('.info', comms)
,datC =[];
clearTimeout(ww);
if(startButt.innerHTML =='Стоп'){
startButt.innerHTML ='Старт';
hActUsers.dataCount =0;
setLocStor('users', hActUsers);
return;
}
hActUsers.dataLen++;
startButt.innerHTML ='Стоп';
if(handStart)
hActUsers.dataCount = hActUsersTmpl.dataCount;
for(var i in infoS){ var iI = infoS[i]; if(iI.attributes){
var sco = $q('.voting .score',iI)
,apm = sco && sco.title.match(/\d+/g)
,text = $q('.message', iI.parentNode);
datC.push(//{date:
toTime($q('time',iI))/1000
//,plus: apm && apm[1]
//,minus: apm && apm[2]
//,textLen: text && text.innerHTML.replace(/\t/g,'').length}
);
}}
var pageA = lh.match(/^.+?page(\d+).*/);
'datC'.wcl(datC, pageA, ( + NOWdate/1000 - datC[datC.length -1]) /86400)
$e({el: $q('.msg', startButt.parentNode) ||'span'
,cl:'msg'
,ht:'<br> До '+(0|((NOWdate/1000 - datC[datC.length -1]) /86400))
+' дней от сегодня; интервал - '+ (0|((datC[0] - datC[datC.length -1]) /86400))+' дней'
,bef: $q('.clear',uHead)
});
if(handStart)
hActUsers.dateStart = datC[0];
if((hActUsers.dateStart - datC[datC.length -1]) /86400 < readAutoIntervalMax)
ww = setTimeout(function(){
location.href = pageA
? lh.replace(/page(\d+)/,'page'+ (+pageA[1] +1))
: lh +'page2/';
}, 3000);
else{
startButt.innerHTML ='Старт';
hActUsers.dataCount =0;
}
if(!handStart)
hActUsers.dataCount--;
var noNewUser =0;
for(var i in hActUsers.userS){ var uI = hActUsers.userS[i];
if(uI == uName2){
noNewUser =1; break;}
}
if(!noNewUser)
hActUsers.userS.push(uName2);
setLocStor('users', hActUsers); //накапливать статистику в хранилище
wcl('2nd', hActUsers.dataLen);
setLocStor('data'+ hActUsers.dataLen, {user: uName2, data: datC});
}}
,bef: $q('.clear', uHead)
}),
hActUsers = getLocStor('users');
hActUsers.userS = hActUsers.userS ||[];
hActUsers.dataLen = hActUsers.dataLen ||0;
if(hActUsers.dataCount >0){ //автозапуск анализа страницы
wcl('1st');readPage();}
var helpHAct,
helpButt = $e({el:'button', ht:'Подробности'
,on:{click: function(ev){ if(!helpHAct || helpHAct.style.display=='none'){
if(!helpHAct){
helpHAct = $e({cl:'helpHAct'
,ht: hActHelp
,on:{click: function(){this.style.display ='none';}}
,apT: dBody
});
$q('.in', helpHAct).addEventListener('click', function(ev){$sp(ev);},!1);
}else
helpHAct.style.display ='block';
}else helpHAct.style.display ='none'; } }
,aft: startButt
}),
eraseButt = $e({el:'button', ht:'Стереть'
,on:{click: function(ev){
var hActUsers = getLocStor('users');
if(confirm('Удалить всю статистику комментариев пользователей ('+ hActUsers.dataLen +' записей)?')){
if(hActUsers.dataLen){
for(var i = +hActUsers.dataLen; i >=1; i--)
removeLocStor('data'+ i);
removeLocStor('users');
}
$q('.diag', selUser) && $q('.diag', selUser).classList.add('empty');
}
}}
,aft: startButt
}),
dataButt = $e({el:'button', ht:'Данные'
,on:{click: function(ev){
hActUsers = getLocStor('users');
selectUser(showData);
this.blur();
}}
,aft: startButt
}),
selUser,
selectUser = function(f){ //предложить выбор пользователей
if(!selUser || selUser.style.display=='none'){
if(!selUser){
selUser = $e({cl:'selUser'
,ht: '<div class=under></div><div class=in><h2><input class="inUser" title="перейти -видео на комментарии пользователя; Ctrl-Enter - в новом окне"><span class="titl">Выбрать пользователя</span></h2><div class="diag empty"></div></div>'
,on:{click: function(){this.style.display ='none';}}
,apT: dBody
});
$q('.in', selUser).addEventListener('click', function(ev){$sp(ev);},!1);
$q('.inUser',selUser).addEventListener('keyup',function(ev){ if(ev.keyCode ==13){
var lnk = HRU +'/users/'+ this.value +'/comments/';
if(!ev.ctrlKey)
location.href = lnk;
else
window.open(lnk,"_blank")
}},!1);
}else{
selUser.style.display ='block';
var sC = $q('.in', selUser).childNodes;
for(var i = sC.length -1; i >=0; i--){ var sI = sC[i];
'sI'.wcl(sI.tagName, sI.className)
if(dI.attributes &&(sI.tagName =='BUTTON'|| sI.className =='hADel') )
selUser.removeChild(sI);
}
}
for(var i in hActUsers.userS){ var uI = hActUsers.userS[i];
$e({el:'button'
,ht: uI
,on:{click: function(){
var bS = $qA('button', this.parentNode);
for(var j in bS) if(bS[j].attributes)
bS[j].classList.remove('hAActive');
this.classList.add('hAActive');
f(this.innerHTML);
this.blur();
}}
,bef: $q('.diag', selUser)
});
$e({el:'button',cl:'hADel'
,ht:'X'
,at:{title:'Удалить из хранилища','data-user': uI}
,on:{click: function(){
var t = this, u;
del1user(u = this.getAttribute('data-user'));
t.previousSibling.parentNode.removeChild(t.previousSibling);
setTimeout(function(){t.parentNode.removeChild(t);},1);
for(var i in hActUsers.userS){ var uI = hActUsers.userS[i];
if(u == uI){
hActUsers.userS.splice(i, 1); break;
}}
setLocStor('users', hActUsers);
}}
,bef: $q('.diag', selUser)
});
}
var hU = hActUsers.userS;
'hU'.wcl(hU,uName2)
if(hU && hU.length ==1)
f(hU[0]),$q('button',selUser).classList.add('hAActive');
else if(hU && uName2){
var bS = $qA('button', hU[0].parentNode);
for(var i in bS) if(bS[i].attributes){
bS[i].classList.remove('hAActive');
if(bS[i].innerHTML == uName2)
f(uName2),bS[i].classList.add('hAActive');
}
}
}else selUser.style.display ='none';
},
showData = function(user){ //показать диаграмму активности
var dA =[]
,diag = $q('.diag', selUser)
,dC = diag.childNodes;
for(var i = dC.length -1; i >=0; i--){ var dI = dC[i]; if(dI.attributes)
diag.removeChild(dI);}
for(var i =1; i <= +hActUsers.dataLen; i++){ //всё ранее прочитанное по юзеру
if(!RegExp(' '+ i +' ').test(hActUsers.removed ||'') ){
var dat = getLocStor('data'+ i);
if(user == dat.user)
dA = dA.concat(dat.data);
}}
diag.style.height ='100px';
dA.sort(function(a, b){return a - b});
var ymd0 = getYearWeekDay(dA[0]*1000), ymdE =[] //год, неделя, день, число, месяц, кол-во комм.
,diagLeftMargin =7
,diagTopMargin =7
,stripeN =1 //число полос для диаграммы
,stripePeriod = 110
,nComm =0
,diagInnerWid = diag.offsetWidth -diagLeftMargin *2 -10 //ограничения ширины 1 полосы
,wasDoubles =0, dAClean =[]; //слежение за дублями
for(var i in dA){
var ymd = getYearWeekDay(dA[i]*1000);
ymd[5] = ymdE[0]==ymd[0] && ymdE[1]==ymd[1] && ymdE[2]==ymd[2] ? ymdE[5]+1 : 1;
if(dA[i] == ymdE[6]){ //игнор дублей чтения
wasDoubles =1; continue;}
dAClean.push(dA[i]);
nComm++;
ymdE = [ymd[0], ymd[1], ymd[2], ymd[3], ymd[4], ymd[5], dA[i]];
var leftFirstShift = ((ymd[0]-ymd0[0])*52 + (ymd[1]-ymd0[1]) )*10
,iStripe = Math.floor(leftFirstShift / diagInnerWid);
stripeN = Math.max(stripeN, iStripe +1);
$e({cl:'comm'
,cs:{left: (leftFirstShift % diagInnerWid) + diagLeftMargin +'px'
,top: iStripe * stripePeriod + ymd[2] *10 + diagTopMargin +'px'}
,at:{title: ymd[5] +' / '+ ymd[3]+'янвфевмарапрмайиюниюлавгсеноктноядек'.match(/.../g)[ymd[4]]+(ymd[0] +'').substr(2,2) }
,apT: diag
});
}
if(wasDoubles){ //удалить юзера и записать его же из очищенного от дублей массива dAClean
del1user(user);
wcl('delU')
setLocStor('data'+ ++hActUsers.dataLen, {user: uName2, data: dAClean});
setLocStor('users', hActUsers); //сохранить .removed
}
if(stripeN >1)
diag.style.height = 110 * stripeN +'px';
diag.classList.remove('empty');
var hActTitle = '%N комментари%W <span title="%T">с %D0 по %D1</span>', days;
if(ymd)
$q('h2 .titl', selUser).innerHTML = hActTitle
.replace(/%N/,nComm).replace(/%W/, nComm % 10 >0 && nComm % 10 <5 && Math.floor(nComm % 100 / 10) !=1 ? (nComm % 10 ==1 ?'й':'я'):'ев')
.replace(/%D0/,ymd0[3] +'.'+ (ymd0[4]+1) +'.'+ (ymd0[0] +'').substr(2,2))
.replace(/%D1/,ymd[3] +'.'+ (ymd[4]+1) +'.'+ (ymd[0] +'').substr(2,2))
.replace('%T', Math.ceil(days = (dA[dA.length-1] - dA[0]) /86400) +' дней, '+ (nComm/days).toFixed(2) +' комм./д.' );
},
del1user = function(user){ //удалить данные 1 пользователя
for(var i =1; i <= +hActUsers.dataLen; i++){ //всё ранее прочитанное по юзеру
if(!RegExp(' '+ i +' ').test(hActUsers.removed ||'') ){
var dat = getLocStor('data'+ i);
if(user == dat.user){
var dat = removeLocStor('data'+ i);
hActUsers.removed = (hActUsers.removed ||' ')+ i +' ';
}}}
};
}catch(er){
'ER_global:'.wcl(er +' (line '+(er.lineNumber||'')+')')}; //для оповещения об ошибках в Fx
}} //=====/(конец основных операций)=====
)(typeof unsafeWindow !='undefined'? unsafeWindow: (function(){return this})(), 'noConsole',
/*===== css =====*/
'.helpHAct,.selUser{position: absolute; z-index: 1201; top: 0; width: 100%; height: 100%;}'
+'.helpHAct .under,.selUser .under{position: fixed; z-index: 1200; width: 100%; height: 100%; opacity:0.1; background:#777}'
+'.helpHAct .in, .selUser .in{position: relative; z-index: 1201;max-width: 48em; margin: 120px auto 280px; padding: 12px; border: 1px solid #bbb; box-shadow: 0 0 7px 4px #a4b39D/*#afb6af*/; background: #fff;}'
+'.selUser .in .inUser{float: right; padding: 1px 1px 3px; border: 1px solid #ccc;}'
+'.selUser .in h2{margin-bottom: 6px;}'
+'.selUser .diag{position: relative; height: 100px; margin-top: 12px; background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABuAgMAAACsF16hAAAADFBMVEXu7u7x8fHz8/MAAACwIVgQAAAABHRSTlP///8AQCqp9AAAAAFiS0dEAIgFHUgAAAA9SURBVHjaY2AYBYMahCKBsDQkgCITtQoBVuKUWTUqMyozKjMqMyozKjMMZJABoyACCIw2G6gO/uMCI0cGAF7MVC7yox7wAAAAAElFTkSuQmCC) 6px 0;}'
+'.selUser .comm{position: absolute; width: 8px; height: 8px; opacity: 0.2; background: #8b4;}'
+'.selUser .hAActive, .user_header .hAActive{background-color: #f6f8e0/*#fff2d8*/}'
+'.selUser button, .user_header button{height: 1.6em; margin: 10px 0 0 1px; padding: 0 4px 1px; line-height: 1.3em;'
+'box-shadow: 0 0 2px rgba(255, 255, 255, 0.4) inset, 0 0 2px rgba(0, 0, 0, 0.2); transition-duration: 0.2s; text-shadow: 0 1px 0 rgba(255, 255, 255, 0.8); border-radius: 3px; border: 1px solid #e9d8e8; border-color: #e9DeD8 #bccbbb #9eae9e; background-color: #c8ead0/*#cfe8c4*/;}'
+'.selUser button{margin-top: 5px}.user_header .rating{margin-right: 14px;}'
+'.selUser button:hover,.user_header button:hover{background-color: #f2fddb/*#fcfcfc*/;}'
+'.selUser .diag.empty{background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAMAAAC6V+0/AAAASFBMVEXu7u708uv7+PP7+fP28+z38+z08ev7+PLy8Oz69/H6+PP7+PHx8Oz69/L49Ovs7+749Oz38+v28uz18uz49O328+v49O78+fSGxIqmAAAAAWJLR0QAiAUdSAAAAE9JREFUeNqVzjEOwCAMA0BLWbqEASn0/z8F2lpKIaDW4w22AcaEyb/t0MDku2nUp6vdtDUEVhCYYLIsRPP24NtuHOzC0TpO1tDcF6Kzk1gBDdAFF3BuHgsAAAAASUVORK5CYII=)}'
+'.selUser .hADel{position: relative; height: 14px; line-height: 1px; border-radius: 6px; margin:-0.6em; top:-0.9em; left: -3px; padding:0 0 1px; border: 1px solid #d99; font-size:10px; background-color: #ea7852; opacity: 0.15;}.selUser .hADel:hover{background-color: #E65E30; color: #fbb; opacity: 0.8}'
+'.selUser button:hover +.hADel{opacity: 0.5}',
/*===== help string =====*/
'<div class=in><h2>Просмотр активности комментариев пользователей</h2><br><br>После установки юзерскрипта <b>habrActivity</b> на страницах комментариев пользователей (и только на них) появляются 4 кнопки: "Старт", "Данные", "Стереть", "Подробности".<br><br>'
+'При нажатии на "<b>Старт</b>" запускается цикл чтения данных с перебором страниц комментариев для данного пользователя, а кнопка меняет название на "Стоп". Прочитывается не более 25 страниц в автоматическом режиме и читается интервал комментариев за не более 365 дней. В любой момент цикл автоматического чтения страниц останавливается вручную кнопкой "<b>Стоп</b>". Если нужно читать больше 365 дней, чтение возобновляется по кнопке "Старт".<br><br>'
+'С каждой страницы собирается информация о датах создания комментариев и, возможно, впоследствии более сложная, и запоминается в хранилище. Таким образом, накапливается информация о комментариях разных пользователей в разные периоды времени. Хранятся только массивы чисел - например, даты написания комментариев. В последующих версиях возможно расширить статистику на сбор оценок, объёма текстов, количество ссылок и картинок и подобное.<br><br>'
+'Чтобы просмотреть накопленные данные, нажимают кнопку "<b>Данные</b>". Если в браузере на данный момент хранится информация о более 1 пользователе, появится список кнопок с именами пользователей. Нажав одну из кнопок, переходим на просмотр статистики.<br><br>'
+'Просмотр статистики по датам комментариев организован аналогично инфографике Гитхаба - показ активности комментариев пользователя по дням года и дням недели. Отображается не более 420 последних дней (60 недель). Данные подготовлены для "музыкальной иллюстрации активности" http://habrahabr.ru/post/173085/ .<br><br>'
+'В результате, получаются 3 полезные вещи: смотрим активность любого пользователя в комментариях, видим наглядный график, прослушиваем в задумчивости озвучивание его. Делаем выводы.<br><br>'
+'Чтобы стереть все данные пользователя, нажимается кнопка "<b>Стереть</b>". Память браузера очищается от накопленных данных.<br><br></div>')