[Из песочницы] Скованные одной цепью, или добавим комфорта комментариям Вконтакте
Кому неинтересен сюжет, могут сразу ознакомиться с результатом — ссылка на исходники. Она будет продублирована и в конце статьи.
Прочь, мелочи вроде написания манифеста! Приступлю сразу к интересному.
Встраивание скрипта в код страницы
Цель — выводить цепочку комментариев по клику на иконку у комментария. Для этого нужно это иконку отрисовать и повесить на неё событие.
Так эта «иконка» выглядит в итоге
Сразу стало ясно, что код должен работать «изнутри». Встроит его в страницу обычный для расширений контент-скрипт. Вот так он с этим справится:
var s = document.createElement("script");
s.src = chrome.extension.getURL('/js/inject.js');
(document.head || document.documentElement).appendChild(s);
Ещё встраиваемому коду потребуются некие статичные данные, вроде урлов фоновых изображений и id самого расширения. Их я пробрасываю через самописное событие, вызываемое в контент-скрипте после того, как встраиваемый скрипт подгрузился. Контент-скрипт целиком теперь выглядит следующим образом:
var s = document.createElement("script");
s.src = chrome.extension.getURL('/js/inject.js');
(document.head || document.documentElement).appendChild(s);
s.onload = function() {
var evt = document.createEvent("CustomEvent");
var url = chrome.extension.getURL('/img/planet.gif');
app_uid = chrome.runtime.id;
evt.initCustomEvent("BindDefData", true, true, { 'url': url, 'app_uid': app_uid });
document.dispatchEvent(evt);
};
Это финальный его вид. А «слушается» событие на стороне встраиваемого скрипта. Это довольно банально.
Общение между встраиваемым и фоновым скриптами
Получение комментариев, само собой, происходит через api Вконтакте (а именно через метод wall.getComments). Процесс их получения вовсе не требует, чтобы код был написан во встраиваемом в страницу скрипте. Он будет написан в так называемом «бэкграунд-скрипте». Напомню структуру. У нас есть:
- контент-скрипт — пробрасывающий непосредственно на страницу встраиваемый код и статичную информацию
- встраиваемый скрипт — код, отвечающий за логику отображения
- бэкграунд-скрипт — код, работающий в фоне и ждущий команды от встраиваемого. Ходит по api к Вконтакту в гости за списком не отображённых комментариев
Во время прогулки до api рисуется прелоадер. Задержки обычно нет, но порой что-то сомневается в недрах Вконтакта и приходится ждать пару секунд
Сам запрос к api выполняется простенькой функцией, реализующей xhr:
var send_api_request = function(data, url, sync) {
var req = new XMLHttpRequest();
req.open('GET', url + dict_to_uri(data), sync);
req.send();
return req.responseText;
}
Гораздо интереснее бэкграунд принимает команды от встраиваемого скрипта. Для этого был написан «слушатель»:
chrome.extension.onMessageExternal.addListener(function(request, sender, call_back) {
chain = create_chaine(request.ids_list); //построение цепочки комментариев
call_back({ 'chain': chain[0], 'pid': request.ids_list[1], 'persons': chain[1] }); //возвращение полученных данных на сторону "клиента"
})
Обратите внимание, что используется onMessageExternal, так как событие вызывается из встроенного скрипта. Другими словами для браузера эти события вызываются на внешнем для него сайте, а не внутри собственного расширения.
Со стороны клиента событие вызывается так:
chrome.runtime.sendMessage(app_uid, { 'ids_list': ids_list }, function(result) { ... })
В нём мы передаём список необходимых id-шников (id оратора, поста, комментария) и затем обрабатываем полученную цепочку комментариев. Процесс построения самого диалога банален, с ним можно ознакомиться в исходниках.
Отрисовка
Пришло время «видимой» части расширения — отрисовки всплывающего окна.
Так выглядит короткий диалог сейчас
Я не стал изобретать велосипед и после минутного реверс-инжиниринга воспользовался классом MessageBox, который использует сам Вконтакт:
var draw_box = function(html) {
var box = new MessageBox({
title: false,
width: 670,
onHide: false,
onDestroy: false,
bodyStyle: 'background-color: rgba(79, 113, 152, 0.3); border-radius: 6px',
hideButtons: true,
grey: true,
progress: true,
hideOnBGClick: false
});
box.content(html);
box.show();
return box;
}
Html-шаблон, который вкладывается, был «содран» и немного переделан с первого попавшегося хипстерского сайтика. Он слишком неуклюжий, чтобы цитировать. А наполняется он с помощью чуть-чуть оттюнингованной вконтактовой функции rs:
var rs_t = function(html, repl) {
each(repl, function(k, v) {
if (k == 'text') {
v = (typeof v === 'undefined' ? '' : v);
v = v.replace(/(\r|\n)/g, '
'); // make newlines
v = v.replace(/((http)?s?(\:\/\/)?((www)?\.?[a-zA-Z0-9]+\.[a-zA-Z]+\/?\S+))/g, '$4'); //make links
};
html = html.replace(new RegExp('%' + k + '%', 'g'), (typeof v === 'undefined' ? '' : v).toString().replace(/\$/g, '$'));
});
html = html.replace(/\[(id[0-9]+)\|([^\]+]+)\]/, '$2'); // [id|name] -> name
return html;
}
Т.е. в шаблоне подменяется конструкция вида %data% на значение переменной data. Этим занималась оригинальная функция. Я добавил ещё пару вещиц: генерацию ссылок на профиль из записей вида [id000000|Иван Иванов], генерацию ссылок из урлов в тексте сообщения и обработку символов конца строки (переноса).
Обрезанный скрин реального обсуждения
В целом, всё уже работает, но нет основного элемента — ручки, дёрнув за которую, мы увидим все эти чудеса. И тут опять не обошлось без толики реверс-инжиниринга. У Вконтакта есть класс Wall и метод класса _repliesLoaded. Он вызывается, когда комментарии подгрузились, о чём нам недвусмысленно намекает название. Я подменяю этот метод своим:
var cloned_function = Wall._repliesLoaded;
Wall._repliesLoaded = function(post, hl, replies, names, data) {
cloned_function(post, hl, replies, names, data);
wall_overloaded = true;
setTimeout(replace_html, 300);
};
Т.е. технически вызываю его же, но сразу после запускаю функцию, которая отрисовывает в нужных свежеподгруженных комментариях иконку, кликнув по которой мы и увидим всплывающее окно с цепочкой комментариев.
Что в итоге?
Расширение работает. Умеет показывать цепочки комментариев с изображениями и ссылками. Умеет следить за появившимся контентом и добавлять кнопку цепочки, куда нужно.
Скрин цепочки комментариев со ссылками, изображениями и переносами строк.
Не умеет строить параллельные цепочки для случаев, когда обсуждение разветвляется и затем вновь «схлапывается» к итоговому комментарию. Скоро научится. В процессе подбора структуры данных.
Фреймворками не обмазано. Для кого-то оно плюс, для кого-то минус. Очень бы хотелось высоко вскинуть голову и гордо заявить: «И не нужны мне они вовсе!». Но нет. Всё прозаичней — почти не знаю местных фреймворков и батареек. JS — неосновной язык.
Ссылка на репозиторий с исходниками. Буду рад форкам и пулл-реквестам.
P.S.: Я не стал оплачивать себе аккаунт разработчика в гугле. Мне он ни к чему. Если кто желает, может смело выставлять расширение в chrome web store. Если ещё и упомянет автора, то будет отлично.
Комментарии (1)
16 сентября 2016 в 11:16
0↑
↓
Оффтопик: Вконтакт, Фейсбуки и прочее современное социальное говно не предназначены для «обсуждений» в принципе. Нужен или форум, или более приспособленный для этого инструмент с древовидной структурой комментариев, типа disqus для встравивания, или ЖЖ, как платформа.