Читаю я вчерашний пост простоквашино на Хабре или письмо Дяди Федора. Мысль интересная, но.
Комментарии пугают.
Поясню почему.
Комментарии там условно можно разделить на два вида
- «как много помещается ангелов на конце иглы» «о сортировке дат»,
- «как забивать гвозди электронным микроскопом» «мы напишем что-то такое большое в энтерпрайзненьком стиле».
Душа поэта не выдержала, нашел полчаса, и нарисовал userscript.
Скрипт прост до ужаса — перебирает все комментарии в поисках специального маркера. Если маркер найден — показывает все комментарии с маркером во всплывающем окошке.
Всем желающим поучаствовать в улучшении — добро пожаловать, так сказать откатаем технологию. Если ваш комментарий надо добавить в пост — пишите внутри комментария вот так [also]. А я как инициатор этого безобразия — по мере сил буду ваши пожелания в пост переносить. И соответственно — улучшать скрипт тоже.
А теперь лирику побоку, и рассмотрим что же мы тут такое делаем.
Стандартное начало — объясняем Greasemonkey что это и для чего. А также заводим парочку переменных — для окошечка и для маркера.
// ==UserScript==
// @name Prostokvashino
// @namespace habrahabr.ru
// @description http://ift.tt/NizLm8
// @include http://ift.tt/1f46Lof
// @include http://ift.tt/1f46NfR
var MARKER = '[also]';
var floatingDiv = null;
Теперь рассмотрим основной алгоритм — благо он прост как пять копеек. Для начала найдем где комментарии, подготовим массивчик для результатов (found):
function showMarkedComments () {
var commentsBlock = document.getElementById('comments');
var eltList = commentsBlock.getElementsByTagName('div');
var found = [];
Далее поступим очень просто — переберем все div, с собственно текстами.
for(var j=0; j<eltList.length; ++j) {
var elt = eltList[j];
if(elt.className != 'comment_body' ) continue;
// now elt has two sub-divs - info with author and div with text
// in version 0.1 we're ignoring authors and other technical stuff - just use div with text
for(var k=0; k < elt.childNodes.length; ++k) {
if(elt.childNodes[k].nodeName.toLowerCase() == "div") {
...
}
}
}
Не верх оптимальности, но обойдет всю иерархию с ответами на комментарии, причем в том порядке в котором это описано в странице. Вуаля — о датах думать не придется.
Собственно проверка и запоминание настолько очевидны (и так же неоптимальны), что комментировать тут нечего:
divtxt = elt.childNodes[k].innerHTML;
if(divtxt.indexOf(MARKER) < 0) continue;
found.push(divtxt);
Осталась мелочь — заполнить содержимым всплывающее окошко. Я как старый хардкорщик нарисовал рашпилем из трактора, так что наворачивание плагинов jQuery — самое то тут есть большой простор для улучшения этого безобразия.
// in found, we have list of HTML texts together with marker.
// All we need is to place it in floating div and make it visible
if(found.length < 1) return;
var buf = new StringBuffer();
for(var j = 0; j < found.length; ++j) {
buf.append('<div id="comment_"'+j+' class="comment_item">\n');
buf.append('<div class="comment_body">');
buf.append('<div class="message html_format "><hr/>');
buf.append(found[j]);
buf.append('</div>');
buf.append('</div>\n');
buf.append('</div>\n');
}
buf.append('<hr/>');
floatingDiv.innerHTML = buf.toString();
floatingDiv.style.top = (window.pageYOffset + 10) + 'px';
floatingDiv.style.display = '';
}
Тут в процессе пиления рашпилем я внезапно для самого себя озаботился производительностью, быстренько родив на коленке аналог StringBuffer / StringBuilder. Вот собственно эта запчасть:
// simple string buffer
function StringBuffer() {
this.buffer = [];
}
StringBuffer.prototype.append = function append(string) {
this.buffer.push(string);
return this;
};
StringBuffer.prototype.toString = function toString() {
return this.buffer.join("");
};
Если потребуется ее откомментировать, дайте кто-нибудь знать. По мне так очевидный код, но мало ли. Пятница, вечер, накануне 23 февраля…
Остались мелочи — нарисовать окошечко, повесить события, сходить за соком, и вот он — скрипт.
function makeFloatingDiv() {
if(!floatingDiv) {
floatingDiv = document.createElement('div');
floatingDiv.style.position = "absolute";
//floatingDiv.style.height = '600px';
floatingDiv.style.width = '800px';
floatingDiv.style.backgroundColor = '#f2f2f2';
floatingDiv.style.borderColor = 'red';
floatingDiv.style.borderWidth = '2px';
floatingDiv.style.borderStyle = 'groove';
floatingDiv.style.padding = '2px';
floatingDiv.valign = 'top';
floatingDiv.align = 'left';
floatingDiv.style.display = 'none';
floatingDiv.textAlign = 'left';
floatingDiv.style.overflow = 'auto';
floatingDiv.style.left = '10px';
floatingDiv.style.top = '10px';
document.getElementsByTagName('body')[0].appendChild(floatingDiv);
floatingDiv.style.display = '';
floatingDiv.innerHTML = "...";
}
}
function makeDivWExtraLinks() {
var xdiv = document.createElement('span');
xdiv.appendChild(document.createElement('br'));
xdiv.appendChild(document.createElement('br'));
xdiv.appendChild(mk_Link(1, 'See additions to post'));
return xdiv;
}
function goHide(event) {
if(event.target.style.display != 'none')
event.target.style.display = 'none';
}
Ну и еще парочка таких же функций в том же стиле:
function mk_Link(code, label) {
var newElt = document.createElement('a');
newElt.appendChild(document.createTextNode('[' + label + ']'));
newElt.href= 'javascript:void(-' + code + ')';
return newElt;
}
Однако, внимательный читатель увидит страшную ересь. Мало того что куски HTML склеиваются строчками из кусочков! Там еще и какие-то неочевидные хаки встроились. Вот такие
newElt.href= 'javascript:void(-' + code + ')';
Сам в шоке! Наверное в только что слопанный тортик кто-то подмешал коноплю…
Идея тут в следующем — мы создаем отдельный линк между статьей и комментариями, по нажатию на который и проводится вся работа. Сделано это вот зачем — чтобы можно было пользоваться другими удобными скриптами и кнопками, по обновлению комментариев без перезагрузки страницы. А раз комментарии могут к нам приехать после загрузки и очень даже сильно «после» — то и наш алгоритм должен уметь работать в любое удобное время.
А поскольку это все было нарисовано на коленке, чтобы не стукаться лбом с контекстами выполнения и их принципалами, то ссылку я сформировал так что ни код сайта ни браузер ничего полезного сделать со ссылкой не могут. Но в своем обработчике я легко сориентируюсь что с этим делать. Можно хоть слова матерные написать, лишь бы это не было валидной линкой или валидным кодом.
Однако, остался последний рывок! Никакие происки не помешают… ик! Регистрируем пару обработчиков — на загрузку документа и на обработку ужасного хака:
window.addEventListener("load", goLoad, true);
document.addEventListener('click', goClick, true);
При загрузке странички, между статьей и комментариями добавляем спец-ссылку. По нажатии на нее и будем проводить всю работу:
function goLoad() {
// starting here: extra links plus other stuff
var commentsBlock = document.getElementById('comments');
commentsBlock.parentNode.insertBefore(makeDivWExtraLinks(), commentsBlock);
makeFloatingDiv();
floatingDiv.addEventListener('click', goHide, true);
}
и вот — обработка по нажатию на эту ссылку:
function goClick(event) {
if(event.target.href) {
var j = event.target.href.indexOf('javascript:void(-');
if( j >=0 ) {
// kind is group of operations' prefix: 0..9
var kind = event.target.href.substring(j + 16, j + 18);
if(kind == '-1') showMarkedComments();
}
}
}
Спасибо за внимание.
This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.
Комментариев нет:
Отправить комментарий