А таки давайте напишем инструмент для написания писем Дяди Федора!

от автора

image
Читаю я вчерашний пост простоквашино на Хабре или письмо Дяди Федора. Мысль интересная, но.

Комментарии пугают.

Поясню почему.

Комментарии там условно можно разделить на два вида

  • «как много помещается ангелов на конце иглы» «о сортировке дат»,
  • «как забивать гвозди электронным микроскопом» «мы напишем что-то такое большое в энтерпрайзненьком стиле».

Душа поэта не выдержала, нашел полчаса, и нарисовал userscript.

Скрипт прост до ужаса — перебирает все комментарии в поисках специального маркера. Если маркер найден — показывает все комментарии с маркером во всплывающем окошке.

Всем желающим поучаствовать в улучшении — добро пожаловать, так сказать откатаем технологию. Если ваш комментарий надо добавить в пост — пишите внутри комментария вот так [also]. А я как инициатор этого безобразия — по мере сил буду ваши пожелания в пост переносить. И соответственно — улучшать скрипт тоже.

А теперь лирику побоку, и рассмотрим что же мы тут такое делаем.

Стандартное начало — объясняем Greasemonkey что это и для чего. А также заводим парочку переменных — для окошечка и для маркера.

// ==UserScript== // @name           Prostokvashino // @namespace      habrahabr.ru // @description    habrahabr.ru/post/213263 // @include        http://*.habrahabr.ru/post/* // @include        http://habrahabr.ru/post/*  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 февраля…

Остались мелочи — нарисовать окошечко, повесить события, сходить за соком, и вот он — скрипт.

всякий утиль

Не менее хардкорно, при помощи того же рашпиля, я нарисовал всплывающее окошечко. Да, я знаю — так пишут только старперы, помнящие особенности жавашкрипа в нетшкафе 3.0.

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();         }     } } 

Спасибо за внимание.

ссылка на оригинал статьи http://habrahabr.ru/post/213481/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *