Пролог
Доброго времени суток, уважаемый посетитель ХабраХабра!
В этот очередной в нашей с Вами жизни день знаний (который я, однако, провел исключительно за работой, а не учебой), под влиянием данной статьи, пишу повествование о моем опыте создания расширения для Google Chrome – TabBasket (не знаю, как Вы, но я на названия неказист). Статья оформлена в смешанном стиле – перекликаются элементы урока, а так же пояснения по коду и описание ключевых моментов.
Прежде чем перейти непосредственно к главному, замечу, что мои знания CSS немного сумбурны, опыта было совсем немного, поэтому какое-то мое решение может показаться слегка странным.
Что мы имеем
Для написания расширения нам практически не нужны никакие инструменты, всю работу можно провести хоть в блокноте или gedit. Выбираем редактор на свой вкус (лично я в таких случаях пользуюсь либо Notepad++, либо NetBeans; первое легче, второе функциональнее, выбор как всегда за Вами).
Ближе к делу
Перво-наперво исходники.
Предполагается, что с основами создания расширений читатель уже знаком (опять же из упомянутой выше статьи), поэтому живенько начнем с манифеста:
{
"name" : "TabBasket ",
"version" : "1.5",
"permissions" : [
"tabs", "http://*/*"
],
"background_page" : "back.html",
"browser_action" : {
"default_title": "Closed tabs",
"default_icon": "icon.png",
"popup": "popup.html"
}
}
* This source code was highlighted with Source Code Highlighter.
Два элемента back.html и popup.html будут постепенно рассмотрены (созданы, написаны) далее. Никаких неожиданной в манифесте нет, все достаточно прозрачно. Для большего понимания процесса, кратенько изложу идею. Замечательная страничка back.html работает в тени, не имеет внешнего вида, её код стартует сразу после установки расширения или запуска браузера, если расширение уже было установлено. Ее роль – установить обработчики для событий создания, удаления или обновления вкладок. Popup.html непосредственно формирует список закрытых вкладок, позволяет восстановить какую – либо из них.
Двигаемся дальше. Разбираем файл back.html:
/* Tab constructor function. */
function AnyTab()
{
this.id = 0
this.url = ""
this.name = ""
this.favicon = ""
}
* This source code was highlighted with Source Code Highlighter.
Функция-конструктор для объекта, хранящего информацию о вкладке. Язык позволяет создать инициализированный значениями по умолчанию объект разными методами, я выбрал именно этот. Хранится id вкладки (выдается самим браузером), url(что это о_О?), name (название сайта) и favicon (иконка сайта, с ней отдельная свистопляска). Далее по коду
var active = []
var closedTabs = []
var maxTabCount = 15
* This source code was highlighted with Source Code Highlighter.
Нам понадобятся два массива – активных (открытых) вкладок и закрытых. Максимальное число вкладок я выбрал (по-моему мнению) оптимальное. Вешаем обработчики на события
chrome.tabs.onUpdated.addListener(onTabUpdated)
chrome.tabs.onRemoved.addListener(onTabRemoved)
chrome.tabs.onCreated.addListener(onTabCreated)
* This source code was highlighted with Source Code Highlighter.
В таком случае onTabUpdated, onTabRemoved И onTabCreated – имена функций обработчиков. Теперь немного приоткроем завесу тайны – когда вкладка создается, необходимо запомнить необходимые нам характеристики (ссылку, название и др.). Делается это потому, что в функцию onTabRemoved приходит только id закрытой вкладки, а ее самой уже и в помине нет. Поэтому при закрытии вкладки она ищется по своему id в массиве active(кстати ее там может и не быть – если расширение было установлено в момент, когда в программе уже были открытие вкладки), данные копируются в массив closedTabs и, соответственно, удаляются из массива active. Теперь мы имеем полную информацию о закрытой вкладке и без труда можем ее восставить! Внимательный читатель, однако, обратит внимание на то, что я не упомянул про функцию onTabUpdated. У нее особенная роль, вызванная следующей особенностью (читай проблемой) – в функцию onTabCreated совсем не сразу приходит вся информация о вкладке. Невооруженным глазом видно, как у новоиспеченной вкладки меняется заголовок, прогружается иконка. Эти данные позднее дотекают в функцию onTabUpdated.
Роль рабочей лошадки играет функция updateTab. Если первым параметром передать -1, то просто напросто создастся новая вкладка. В другом случае туда передается id вкладки, информацию о которой обновилась.
function updateTab(index, tab) {
/* Add new tab. index = -1 passed in onTabCreated */
if (index === -1) {
var newTab = new AnyTab()
newTab.id = tab.id
newTab.url = tab.url
newTab.name = tab.title
newTab.favicon = tab.favIconUrl
active.push(newTab)
}
/* Or update tab. Find index first. */
else {
var j, tbCt = active.length
for ( j = 0; j < tbCt; j++ ) {
if ( index == active[j].id )
break
}
if ( j == tbCt ) {
console.log("updateTab not found ID "+index)
return
}
active[j].id = tab.id
active[j].url = tab.url
active[j].name = tab.title
active[j].favicon = tab.favIconUrl || "icon.png"
}
}
* This source code was highlighted with Source Code Highlighter.
Самая сложная часть позади, теперь немного о внешнем виде и popup.html?
Интерфейс данного расширения – всплывающее окошко со списком сайтов, слева иконка для более интуитивного использования. Так же сверху имеется div класса toolArea – это область инструментов, пока там почетно красуется одна единственная кнопочка – очистка списка.
JavaScript’овая начинка данной странички достаточно типична и прозрачна, ключевой момент здесь – доступ к back.html, то бишь ко всем функциям и переменным, объявленным там, через вызов метода chrome.extension.getBackgroundPage().
Скрипт объемный, но если разбить его на логические части, получается очень просто:
- Создание списка
- Задание обработчиков mouseover и mouseout
- Реализация функций клика по элементу списка, а так же наведения курсора
Это уже чистый, простой JavaScript, с небольшими примесями уже использованных нами приемов.
Создание:
var back = chrome.extension.getBackgroundPage()
for ( var k = back.closedTabs.length - 1; k >= 0; k--) {
document.write ("<div id='" + back.closedTabs[k].id + "' onclick='divClick(this)' class='divClass'>")
document.write ("<img src=" + back.closedTabs[k].favicon + " class='imgClass'>")
var txtName = back.closedTabs[k].name
if (txtName.length > 40)
txtName = txtName.substr(0, 35) + "..."
document.write ("<span class='labelClass'>" + txtName + "</span>")
document.write ("</div>")
}
* This source code was highlighted with Source Code Highlighter.
Обработчики:
var divs = document.getElementsByTagName("div");
for (var i = 0; i < divs.length; i++)
{
if (divs[i].id === "toolDiv") {
divs[i].onclick = cleanToolClick
} else {
divs[i].onmouseover = mouseOverEvent
divs[i].onmouseout = mouseOutEvent
}
}
* This source code was highlighted with Source Code Highlighter.
В определенный момент разработки подловил себя на мысли, что при создании новой вкладки в divClick с помощью функции chrome.tabs.create (которая, кстати, принимает объект аргументов, которые легче подсмотреть в документации, чем запоминать из статьи), вкладка автоматически попадает в onTabCreated, так что не нужно беспокоиться о добавлении в массив active вкладок, созданных программно!
ToDo
Пишите предложения по улучшению! Пока только очевидным кажется сделать страницу опций с выбором количества вкладок максимального, но из-за одного такого пунктика страницу не хочется (читай ленюсь) создавать!
Эпилог
В результате проделанной работы получаем достаточно полезное в хозяйстве и быту расширение, реализующее столь необходимую функциональность, как браузерная корзина. В статье рассмотрены основные приемы работы с вкладками, принципы тесного взаимодействия структурных элементов расширения Google Chrome. Создание расширений – очень интересное и увлекательное занятие! Желательным требованием является базовое знание html, css и JavaScript, однако не обязательным – перед живым интересом ничто не устоит!
И принимаю замечания по коду, так как считаю себя новичком в JS!
Удачи Вам в начинаниях, дерзайте, экспериментируйте, создавайте!