GrabDuck

Готовим расширение под Chrome, украшаем Хабр

:

Чтобы сделать расширение достаточно минимальных знаний Javasctipt, HTML и CSS. Давайте добавим на хабр догрузку следующей страницы, а также систематическую проверку новых постов на текущей. Примерно как на twitter.

Полностью готовый экстеншн можно установить и проверить в работе через Chrome Web Store, а здесь разберемся как написать основной код с нуля (полный занимает всего 6 KB).


Для начала необходимо создать отдельную папку нашего экстеншна и положить в нее файл manifest.json с таким содержанием:

{
    "name":"Habrahabr Demo",
    "version":"0.1",
    "description":"Demo plugin",
    "content_scripts":[
        {
            "matches":["http://habrahabr.ru/*"],
            "css":["style.css"],
            "js":["jquery.js", "script.js"]
        }
    ]
}

Экстеншн будет активирован только на habrahabr.ru. Подгрузит из своей папки пока еще отсутствующие style.css, script.js и библиотеку jQuery. Chrome будет ругаться на отсутствие этих файлов, поэтому положим рядом пустые style.css и script.js, а также скачаем самый свежий jquery (который всегда находится по адресу http://code.jquery.com/jquery.js) и тоже положим в папку.

Теперь подключим наш экстеншн. Идем в настройки расширений хрома (chrome://settings/extensions), ставим там галочку «Developer mode» и нажам «Load unpacked extension...» указываем местоположение папки с нашими файлами. Модуль добавлен:

image

Перезагружаем хабр, убеждаемся что всё действительно работает. Точнее ничего, конечно, не изменится. Для проверки же добавим в style.css что-то вроде:

#layout {
     max-width: none !important;
}

А в script.js, скажем:
$(function(){
	var username = $("#header .username").text();
	$("#header .bottom").append(
		'<a href="/users/' + username + '/topics/">топики</a>'+
		'<a href="/users/' + username + '/qa/questions/">вопросы</a>'+
		'<a href="/users/' + username + '/comments/">комментарии</a>'
	);
});

Перезагрузите расширение кликнув на ссылку Reload (ее открывает треугольничек слева от иконки расширения). Затем перезагрузим хабр и убедимся что верстка стала резиновой, а под никнеймом добавились новые ссылки (если вы зарегистрированы).

Приступим к AJAX загрузке. Для начала навесим обработчик клика на ссылку «Туда»:

$(document).on("click", "#next_page", function () {
	var nextPrevBlock = $(".next-prev"); // блок с кнопками Сюда/Туда
	var href = $(this).attr("href"); // ссылка кнопки Туда
	$.ajax({
		url:href,
		success:function (response) {
			$(".posts").append($(response).find(".posts").html()); // добавляем посты в конец списка
			$(".page-nav").html($(".page-nav", response).html()); // и заменяем ссылки туда/сюда/страницы на новые
			stopTrackingScrollToBottom = false; // описание флага будет ниже
		}
	});
	return false; // отменяем переход по ссылке
});

Теперь при клике по ссылке посты с новой страницы будут добавляться к постам на текущей (cнова не забываем перезагрузить экстеншн и хабр). Хорошим тоном считается показывать индикатор загрузки, здесь же опустим, простоты ради.

Далее делаем так чтобы ссылка нажимала сама себя когда мы доскроллим до конца страницы

var stopTrackingScrollToBottom = false;
$(window).scroll(function () {
	if ($(window).scrollTop() + 10 >= ($(document).height() - ($(window).height()))) { // за 10 пикселов до конца страницы
		if (stopTrackingScrollToBottom == false) { // проверяем не инициирован ли уже процесс дозагрузки
			stopTrackingScrollToBottom = true; // отключаем этот уловитель скролла на время дозагрузки
			$("#next_page").click(); // загибаем пальцы за пользователя :)
		}
	}
	if ($(window).scrollTop() == 0) { // юзер доскроллил до верха
		checkForNewPosts();
	}
});

И заодно заложили начало второй части скрипта — вызов checkForNewPosts() при скролле до начала страницы. Функция должна проверять наличие новых постов на текущей странице и сообщать бэйджиком:

function checkForNewPosts(andLoadThem) {
	if ($(".posts").length == 0) {
		// это вообще не страница с постами, уходим
		return;
	}
	$(".posts").prepend('<div class="new-posts-ajax"><img src="http://isbeauty.ru/images/icon_ajax_loader.gif"/></div>');
	$.ajax({
		url:window.location.href,
		success:function (response) {
			$(".new-posts-ajax").remove();
			// Находим все айдишники постов на текущей странице и сравниваем с айишниками в ответе
			var newPosts = getPostIds(response).diff(getPostIds());
			Tinycon.setBubble(newPosts.length);
		}
	});
}

Array.prototype.diff = function (a) {
	return this.filter(function (i) {
		return !(a.indexOf(i) > -1);
	});
};

function getPostIds(where) {
	var postIds = [];
	$(".post", where).each(function () {
		var postId = $(this).attr("id");
		postIds.push(postId);
	});
	return postIds;
}

Пока еще нет результата, потому что Tinycon.setBubble() не существует. Она должна выставлять бэйджик на фавиконке и реализована в библиотеке Tinycon. Чтобы ее подключить скачайте tinycon.js из github, положите в папку расширения и не забудьте подключить tinycon.js в manifest.json.

Чтобы проверить что бэйджик работает — зайдем на http://habrahabr.ru/posts/top/daily/ (вообще лучше тренироваться на страницах топа, они по всей видимости кэшируются и не создают большой нагрузки). Откроем инспектр кода (F12), удалим пару дивов внутри <div class="posts">...</div> и проскроллим на начало страницы. Бэйджик должен показать количество отсутствующих топиков.

Сделаем еще так, чтобы эта проверка происходила регулярно, раз в минуту:

var MAX_CHECKS_FOR_NEW_POSTS = 60; // введем ограничение на количество запросов, на случай если надо выйти за пельменями
var CHECK_FOR_NEW_POSTS_EACH = 60; // через сколько секунд проверять наличие новых

$(function () {

	window.checkNewPostsTimer = setInterval(function () {
		MAX_CHECKS_FOR_NEW_POSTS--;
		if (MAX_CHECKS_FOR_NEW_POSTS <= 0) {
			// self-destroy timer
			clearInterval(window.checkNewPostsTimer);
		} else {
			checkForNewPosts();
		}
	}, CHECK_FOR_NEW_POSTS_EACH * 1000);

});

Не так уж много кода осталось, но на этом закончу. Не охвачена эстетическая сторона вопроса, а также возможность дозагрузить найденные новые посты, отменить автоматическую дозагрузку следующей страницы (по кнопке и по <Esc>). Всё это реализовано в готовом экстеншне, а исходный код доступен после установки где-то в папке:
C:\Users\%USERNAME%\AppData\Local\Google\Chrome\User Data\Default\Extensions\glaccbllkhielccdhfbbpilnlbemgaji
(если там нет, ищите папку glaccbllkhielccdhfbbpilnlbemgaji где-то еще).

Осталось выложить расширение для публичного доступа. Для этого нужно получить приватный файл ключа, он требуется чтобы заливать эту и новые версии вашего экстеншна в дальнейшем. В настройках расширений кликаем кнопку «Pack extension», указываем путь к папке и оставляем путь к ключу пустым:

image

После упаковки хром создаст файл DemoExtension.crx и DemoExtension.pem, последний вам нужно переименовать в key.pem и кинуть в папку экстеншна (не теряйте его, а то не сможете обновлять это расширение). После чего всю папку расширения нужно заархивировать в DemoExtension.zip и залить в вашу панель управления. CRX файл при этом вообще не участвует.

После заливки архива нужно будет заполнить анкетные данные расширения и можно публиковать. Публичным расширение в Chrome Web Store можно сделать после единоразового пожертвования корпорации $5, врочем это необязательно, полученный ранее DemoExtension.crx (не zip) можно выложить хоть на Dropbox, Chrome даст установить его после подтверждения безопасности.

Вот как выглядит залитый в Web Store модуль:

image

Данное расширение не добавляет кнопок на панель, ни диалоговых окон, ни пунктов в контекстное меню, не имеет настроек, но вы сами разберетесь как это всё делать.

Теперь же вперед, делать сайты лучше, удобнее, красивее! :)