Немного поразмыслив я пришел к варианту google chrome extension:
- Crome использует Chromium движок, который является форком WebKit (а это Safari), так же не забываем Blink (а это уже новая (хотя я все еще использую старую с bookmarks’ами) Opera). Таким образом, написав расширения для chrome, мы с минимальными переделками (а то и без них) сможем его портировать на еще 2 браузера
- Нет опыта работы с API Google Chrome
- Google все-таки компания добра 🙂
Когда мысли немного улеглись, первое что я сделал — это ввел в поиске харба "расширение Google Chrome". Увидев обширный вариант статей по данной теме, я со спокойной душой ушел домой полностью уверенный в том, что завтра с утра прочитав их, к концу рабочего дня дело будет ‘в шляпе‘ (как же я тогда ошибался). Прочитав парочку их них я имел общее представление о том как это работает, но этого оказалось мало для воплащения моих идей. Что ж, приступим…
Открываем google chrome, вводим chrome://extensions, ставим галочку Developer mode, нажимаем кнопку Load unpacked extension, выбрав папку нажимаем Ok.
В начале было слово манифест. Ниже можете увидеть содержимое этого файла (manifest.json — это обязательное название файла манифеста)
{ "manifest_version": 2, "name": "My application", // тут надеюсь все понятно "version": "0.9", "icons": { "16": "./16x16.png", "32": "./32x32.png", "48": "./48x48.png", "128": "./128x128.png" }, "permissions": [ "tabs", "http://*/*", "https://*/*" ], "background" : { "page": "background.html" }, "content_scripts":[{ "matches": [ "http://*/*", "https://*/*" ], "js": [ "script_in_content.js" ] }], "browser_action": { "default_title": "Application", "default_icon" : "./16x16.png" // "default_popup": "login.html" // это имя html-страницы расширения, которая будет всплывать при нажатии на иконку, можно с помощью JS устанавливать различные html страницы } }
manifest_version — на данный момент значение 2 обязательное.
version — версия вашего расширения, может содержать только цифры и `.` (те. ‘2.1.12’, ‘0.59’ и тд)
icons — это список всех иконок которые будут отображатся в браузере в различных местах (16 — в адресной строке, 48 — в списке всех расширений и тд.)
permissions — здесь перечислен массив с разрешениями, мне нужно было только tabs.http и https нужен для ajax обмена с любыми сайтами, а также для того что-бы script_in_content.js мог обмениватся данными с фоновой страницей — background.html.
background — это имя фоновой страницы. Фоновая страницы — важный элемент, хотя для некоторых приложений он и не обязателен. Зачем она нужна немного по-позже.
content_scripts — здесь говорится что файл script_in_content.js, будет автоматически загружатся для страницы открытой во вкладке. Страница должны быть открыта с сайтов http://*/* те, всех сайтов с http, но не https, хотя можно было бы указать и их.
browser_action — существует 2 варанта отображения иконки расширения: browser_action и page_action
page_action говорит о том что расширение индивидуально для каждой вкладки, то есть значок будет выводиться в адресной строке. Этот значек можно спрятать/отобразить с помощью JS в зависимости от обстоятельств.
browser_action наоборот не считаются индивидуальными и отображаются не в адресной строке, а в панеле для расширений. Даную иконку никак нельзя скрыть на JS (но можно заблокировать), она отображается постоянно. У browser_action есть одно преимущество по сравнению с page_action, поверх иконки browser_action можно написать пару красивых символов (у меня влазит только 4).
Я выбрал browser_action, как мне необходимо работать не с одним сайтом, а с несколькими. И да, нанесение красивых символов на иконку.
Вот что Google говорит по этому поводу:
Do use page actions for features that make sense for only a few pages.
Don’t use page actions for features that make sense for most pages. Use browser actions instead.
И так, что будет делать наше приложение; скажу сразу, приложение, которое будет далее описано, — это малая часть того что было сделано для клиента. Когда менеджер заходит на сайт hantim.ru для просмотра информации о контракте/вакансии, приложение парсит html код страницы и находит информацию (вакансия, город и тд). При клике на иконку расширения — отображается форма логина, куда менеджер вводит свои данные, а потом может добавить выбранную вакансию/контракт в свой профиль на корпоративном сайте.
Теперь о том как все это работает. Google предоставляет нам такую картину:
1) Inspected window — это то что мы открыли во вкладке, content scripts — это наш script_in_content.js, он имеет полный доступ к DOM страницы.
2) Background page — это сердце приложения, в нашем случае — это background.html.
3) DevTools page — это то, что будет отображатся при клике на иконку расширения (login.html или find.html в нашем случае).
Единственное что меня смущает в данной картинке так это связь DevTools page и Inspected window. Я не нашел решения, что бы передать данные из одной области в другую. Но если выставить Background page как посредника, и через него передавать данный, то все заработает.
И так, настало время кода. Начнем с невидимой стороны.
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <script type="text/javascript" src="lib.js"></script> <script type="text/javascript" src="bg.js"></script> </head> <body></body> </html>
Надеюсь с этим вопросов не должно возникнуть. Одно замечание: «background.html — загружается только один раз за все время работы браузера, а имеено при его запуске». Здесь вы можете увидеть что мы загружаем 2 js файла (lib.js — набор функций, bg.js — ‘голова’ приложения).
/** * OnLoad function * * @return void */ window.onload = function(){ // tmp storage window.bg = new bgObj(); // some variables !!! important window.bg.api_site_host = 'http://katran.by'; // get all graber hosts: !!!once!!! new Ajax({ url: window.bg.api_site_host+'/regexp.php', response: 'json', async: false, onComplete: function(data){ if(data && data.status && (data.status === 'ok')) window.bg.grabber_hosts = data.data; } }).send(); // set handler to tabs chrome.tabs.onActivated.addListener(function(info) { window.bg.onActivated(info); }); // set handler to tabs: need for seng objects chrome.extension.onConnect.addListener(function(port){ port.onMessage.addListener(factory); }); // set handler to extention on icon click chrome.browserAction.onClicked.addListener(function(tab) { window.bg.onClicked(tab); }); // set handler to tabs chrome.tabs.onUpdated.addListener(function(id, info, tab) { // if tab load if (info && info.status && (info.status.toLowerCase() === 'complete')){ // if user open empty tab or ftp protocol and etc. if(!id || !tab || !tab.url || (tab.url.indexOf('http:') == -1)) return 0; // save tab info if need window.bg.push(tab); // connect with new tab, and save object var port = chrome.tabs.connect(id); window.bg.tabs[id].port_info = port; // run function in popup.html chrome.tabs.executeScript(id, {code:"initialization()"}); // send id, hosts and others information into popup.js window.bg.tabs[id].port_info.postMessage({method:'setTabId', data:id}); window.bg.tabs[id].port_info.postMessage({method:'setHosts', data:window.bg.grabber_hosts}); window.bg.tabs[id].port_info.postMessage({method:'run'}); // if user is logged into application set find.html popup if(window.bg.user.id) chrome.browserAction.setPopup({popup: "find.html"}); }; }); window.bg.onAppReady(); }; /** * Functino will be called when popup.js send some data by port interface * * @return void */ function factory(obj){ if(obj && obj.method){ if(obj.data) window.bg[obj.method](obj.data); else window.bg[obj.method](); } } /** * Popup object * * @version 2013-10-11 * @return Object */ window.bgObj = function(){ }; /** * Pablic methods */ window.bgObj.prototype = { /** * some internal params */ tabs: {}, user: {}, popup_dom: {}, active_tab: {}, grabber_hosts: {}, done_urls: [], /** * init() function */ onAppReady: function() { // if user not logged into application set login.html popup chrome.browserAction.setPopup({popup: "login.html"}); }, /** * Function add tab into $tabs object, if need */ push: function(tab) { if(tab.id && (tab.id != 0)){ if(!this.tabs[tab.id]) this.tabs[tab.id] = {tab_obj:tab}; } }, /** * Function will be called from popup.js */ mustParsed: function(data) { if(this.tabs[data.tab_id]){ var id = data.tab_id; this.tabs[id].must_parsed = data.find; // run parser in popup.js, if need if(this.tabs[id].must_parsed && (this.tabs[id].must_parsed === true)) this.tabs[id].port_info.postMessage({method:'parsePage'}); } }, /** * Function will be called from popup.js */ matchesCount: function(data) { if(data.tab_id && this.tabs[data.tab_id]){ var id = data.tab_id; this.tabs[id].matches = data.matches; this.tabs[id].matches_count = this.tabs[id].matches.length+''; if(this.tabs[id].matches_count && this.tabs[id].matches_count != '0'){ chrome.browserAction.setBadgeText({text: this.tabs[id].matches_count}); return 0; } } // show default text chrome.browserAction.setBadgeText({text:''}); }, /** * Function will be called when user change active tab */ onActivated: function(info) { // set active tab this.active_tab = info; var data = {}; data.matches = []; if(info.tabId){ data.tab_id = info.tabId; if(!this.tabs[data.tab_id]) this.tabs[data.tab_id] = {}; if(!this.tabs[data.tab_id].matches) this.tabs[data.tab_id].matches = []; data.matches = this.tabs[data.tab_id].matches; } // set actual count of matches for current tab this.matchesCount(data); // if user is logged into application set find.html popup if(this.user.id) chrome.browserAction.setPopup({popup: "find.html"}); }, /** * Function will be called when user click on extension icon */ onClicked: function(tab) { alert('Произошла ошибка. Обратитесь к разработчикам данного приложения.'); return 0; }, /** * Function will be called from login.js */ loginUser: function(user_data) { var self = this; var json_data = false; // get all graber hosts: !!!once!!! new Ajax({ url: window.bg.api_site_host+'/login.php?user='+encodeURIComponent(JSON.stringify(user_data)), method: 'post', response: 'json', async: false, onComplete: function(data){ if(data && data.status){ // if login - ok if(data.status === 'ok') self.user = data.data; json_data = data; } } }).send(); // return value for login.js return json_data; }, /** * Function will be called from login.js and others places */ setPopup: function(popup_file) { chrome.browserAction.setPopup({tabId: this.active_tab.tabId, popup: popup_file}); }, /** * Function will be called from find.js and others places */ getMatches: function() { // init if need if(!this.tabs[this.active_tab.tabId]) this.tabs[this.active_tab.tabId] = {}; if(!this.tabs[this.active_tab.tabId].matches) this.tabs[this.active_tab.tabId].matches = []; // if user alredy send this url - remove for(var i = 0, cnt = this.tabs[this.active_tab.tabId].matches.length; i < cnt; i++){ for(var j = 0, len = this.done_urls.length; j < len; j++){ if(this.tabs[this.active_tab.tabId].matches[i].url === this.done_urls[j]){ this.tabs[this.active_tab.tabId].matches[i].url = ''; break; } } } return this.tabs[this.active_tab.tabId].matches; }, /** * Function will be called from find.js and others places */ addUrlToGrabber: function(url) { // if $url == '' - already used if(json_data.status && (json_data.status === 'ok')){ var matches = this.tabs[this.active_tab.tabId].matches; for(var i = 0, cnt = matches.length; i < cnt; i++){ if(matches[i].url && (matches[i].url === url)) matches[i].url = ''; this.done_urls.push(url); } } // return value for login.js return json_data; }, /** * Empty method */ empty: function() { } }
Первым делом мы дожидаемся window.onload, потом посылаем запрос на katran.by (получим json данные, с какого сайта и каким RegExp’пом мы дастанем необходимые данные), потом вешаем handler’ы на вкладки браузера (для этого мы и указали в манифесте permissions ~ tabs).
chrome.tabs.onActivated.addListener(function(info) { window.bg.onActivated(info); });
onActivated — происходит тогда, когда пользователь перешел на новую вкладку (по клику или по alt+tab).
chrome.tabs.onUpdated.addListener(function(id, info, tab) { ..... });
onUpdated — происходит тогда, когда страница полностью (загрузился не только DOM, а и все картинки) загрузилась во вкладке.
chrome.browserAction.onClicked.addListener(function(tab) { window.bg.onClicked(tab); });
onClicked — происходит тогда, когда пользователь кликает на иконке приложения. Небольшое замечание, если во время клика default_popup установлено, то обработчик onClicked — не запустится. default_popup это html страница которая будет отображаеться после нажатия на иконку расширения. default_popup можно выставить в манифесте, а так же с помощью chrome.browserAction.setPopup({popup: «find.html»}); или chrome.pageAction.setPopup({popup: «find.html»});
chrome.extension.onConnect.addListener(function(port){ port.onMessage.addListener(factory); });
Эта темная магия конструкция, нужна для приема данных, посланных от script_in_content.js с помощью port.
Обработкой данных занимается factory(obj)
function factory(obj){ if(obj && obj.method){ if(obj.data) window.bg[obj.method](obj.data); else window.bg[obj.method](); } }
Что же происходит когда пользователь загружает вкладку, а происходит следующее:
- Вызывается handler
onUpdated
if (info && info.status && (info.status.toLowerCase() === 'complete'))
если все загружено — продолжаем разбор полетов.if(!id || !tab || !tab.url || (tab.url.indexOf('http:') == -1))
если пользователь открыл не web-сайт (проверку на https — забыл, только сейчас заметил 🙂 ), а к примеру вкладку настройки или ftp и тд., то ничего не делаемwindow.bg.push(tab);
— созраняем информацию об текущей вкладкеchrome.tabs.executeScript(id, {code:"initialization()"});
— сейчас мы приказываем script_in_content.js выполнить функцию initialization()window.bg.tabs[id].port_info.postMessage({method:'setTabId', data:id})
— мы посылаем данные в script_in_content.jschrome.browserAction.setPopup({popup: "find.html"});
— устанавливаем popup страницу, если пользователь авторизовался ранее
Есть 2 способа передать данные от background.html к script_in_content.js:
- сhrome.tabs.executeScript(integer tabId, InjectDetails details, function callback) — одно но, таким спосабом можно передавать данные только в виде строки (не объект, не массив)
- сhrome.tabs.sendMessage(integer tabId, any message, function responseCallback) — так можно передавать что угодно, правдо потребуются дополнитеные настройки
И так, мы послали данные в script_in_content.js, значит настало время рассмотреть его код.
// set handler to tabs: need for seng objects to backgroung.js chrome.extension.onConnect.addListener(function(port){ port.onMessage.addListener(factory); }); /** * Function remove spaces in begin and end of string * * @version 2012-11-05 * @param string str * @return string */ function trim(str) { return String(str).replace(/^\s+|\s+$/g, ''); } /** * Functino will be called from background.js * * @return void */ function initialization(){ window.popup = new popupObj(); } /** * Functino will be called when background.js send some data by port interface * * @return void */ function factory(obj){ if(obj && obj.method){ if(obj.data) window.popup[obj.method](obj.data); else window.popup[obj.method](); } } /** * Popup object * * @version 2013-10-11 * @return Object */ window.popupObj = function(){ }; /** * Pablic methods */ window.popupObj.prototype = { /** * some internal params */ available_hosts: [], total_host: null, matches: [], tab_id: null, port: null, cars: [], /** * Function will be called from bg.js */ setHosts: function(hosts) { this.available_hosts = hosts; }, /** * Function will be called from bg.js */ setTabId: function(id) { this.tab_id = id; }, /** * Function check total host */ run: function() { // get total host if(document.location.host && (document.location.host != '')) this.total_host = document.location.host; else if(document.location.hostname && (document.location.hostname != '')) this.total_host = document.location.hostname; if(!this.total_host || (this.total_host === '')) return 0; var find = false; // if total host in array $available_hosts - parse page for finde cars for (host in this.available_hosts) { if(this.total_host.indexOf(host) != -1){ this.total_host = host; find = true; break; } }; // create connection to backgroung.html and send request this.port = chrome.extension.connect(); this.port.postMessage({method:'mustParsed', data:{tab_id:this.tab_id, find:find}}); }, /** * Function will be called from bg.js * Parse page */ parsePage: function() { // reset variable before parse this.matches = []; if(!this.available_hosts[this.total_host]) return 0; var html = window.document.body.innerHTML; var reg_exp = this.available_hosts[this.total_host]; var matches = {}; var match = []; var find = false; for(var i = 0, len = reg_exp.length; i < len; i++) { var exp = new RegExp(reg_exp[i].reg_exp, reg_exp[i].flag); match = exp.exec(html); if(match && match.length && reg_exp[i].index){ matches[reg_exp[i].field] = trim(match[reg_exp[i].index]); find = true; } else if(match && match.length){ matches[reg_exp[i].field] = match; find = true; } } // this url will be send to site if(find === true){ matches.url = document.location.href; this.matches.push(matches); } // send count of matches this.port.postMessage({method:'matchesCount', data:{tab_id:this.tab_id, matches: this.matches}}); } }
Первое что бросается в глаза — это прием данных от background.html, как можете заметить он такой же как и в bg.js:
chrome.extension.onConnect.addListener(function(port){ port.onMessage.addListener(factory); });
Как помните, ранее в bg.js мы запустили initialization()
, setTabId()
, setHosts()
и run()
. Найбольший интерес представляет window.popup.run(). Там проверяется доменное имя сервера открытой страницы, и если это имя совпадает со списком сайтов которые нам интересны (данные с которых необходимо передать на корпоративный ресурс) — find = true;
и отправляем запрос window.bg.mustParsed(obj)
в bg.js.
/** * Function will be called from script_in_content.js */ mustParsed: function(data) { if(this.tabs[data.tab_id]){ var id = data.tab_id; this.tabs[id].must_parsed = data.find; // run parser in popup.js, if need if(this.tabs[id].must_parsed && (this.tabs[id].must_parsed === true)) this.tabs[id].port_info.postMessage({method:'parsePage'}); } }
Если совпадение домена было найдено, то запускаем парсер страницы parsePage()
в script_in_content.js.
/** * Function will be called from bg.js * Parse page */ parsePage: function() { // reset variable before parse this.matches = []; if(!this.available_hosts[this.total_host]) return 0; var html = window.document.body.innerHTML; var reg_exp = this.available_hosts[this.total_host]; var matches = {}; var match = []; var find = false; for(var i = 0, len = reg_exp.length; i < len; i++) { var exp = new RegExp(reg_exp[i].reg_exp, reg_exp[i].flag); match = exp.exec(html); if(match && match.length && reg_exp[i].index){ matches[reg_exp[i].field] = trim(match[reg_exp[i].index]); find = true; } else if(match && match.length){ matches[reg_exp[i].field] = match; find = true; } } // this url will be send to site if(find === true){ matches.url = document.location.href; this.matches.push(matches); } // send count of matches this.port.postMessage({method:'matchesCount', data:{tab_id:this.tab_id, matches: this.matches}}); }
Если скрипт что-то нашел на странице, то все что он нашел складывает в массивчик, добавляет к нему текущий url страницы и отправляет назад в bg.js, мол: «Смотри что я нашел…». В ответ на это, bg.js анализирует входные данные, и если RegExp’ы нашли что-то — пишет поверх иконки колличество совпадений (1, 2 и тд.) chrome.browserAction.setBadgeText({text: this.tabs[id].matches_count});
.
Это вроде все основные моменты рыботы связки bg.js и script_in_content.js.
Теперь поговорим о popup. Когда пользователь кликает по иконке приложения — отображается форма login.html.
Менеджер вводит свои данные от корпоративного сайта, нажимает Login и тут происходит следующее:
<!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <script type="text/javascript" src="login.js"></script> <link type="text/css" rel="stylesheet" href="login.css"> <title>Grabber popup</title> </head> <body> <div class="body"> <div class="emptyLogin"> <div id="error_message"> </div> <form name="login_form" action="" method="get" id="popup_login_form"> <table> <tbody> <tr> <td align="right">Ваш E-mail:</td> <td><input type="text" name="login" value="" tabindex="1"></td> </tr> <tr> <td align="right">Пароль:</td> <td><input type="password" name="pass" value="" tabindex="2"></td> </tr> <tr> <td colspan="2" align="center"><input type="submit" value="Login" class="button"></td> </tr> </tbody> </table> </form> </div> <div id="loader"><img src="ajax-loader.gif" title="Loding" alt="Loading"></div> </div> </body> </html>
/** * OnLoad function * * @return void */ window.onload = function(){ // set some events handlers document.getElementById('popup_login_form').onsubmit = function(obj){ // fade popup document.getElementById('loader').style.display = 'block'; document.getElementById('error_message').innerHTML = ' '; if(obj.target.elements && obj.target.elements.length && (obj.target.elements.length === 3)){ var data = {}; data.login = obj.target.elements[0].value; data.pass = obj.target.elements[1].value; setTimeout(function(){ var bg_wnd = chrome.extension.getBackgroundPage(); var result = bg_wnd.bg.loginUser(data); if(result && result.status && (result.status === 'error')) document.getElementById('error_message').innerHTML = result.mess; else{ // set new popup html code and close popup window bg_wnd.bg.setPopup('find.html'); window.close(); } // hide fade on popup document.getElementById('loader').style.display = 'none'; }, 500); } return false; }; }
Задача login.js состоит в том, что бы повесить onsubmit
на форму, и отправить логин/пароль в background.html (bg.js),
а делается это с помощью следующей конструкции (как увидите, мы пожем на прямую вызывать методы объекта bg.js):
var bg_wnd = chrome.extension.getBackgroundPage(); var result = bg_wnd.bg.loginUser(data);
bg_wnd.bg.loginUser(data)
отправляет данные на сервер, если все хорошо, то popup login.html сменяет find.html,
а данные о пользователе сохраняются в переменной. Смена popup происходит следующим образом:
/** * Function will be called from login.js and others places */ setPopup: function(popup_file) { chrome.browserAction.setPopup({tabId: this.active_tab.tabId, popup: popup_file}); },
Небольшое замечание, если пользователь открыл popup login.html поставил курсор в поле ‘Ваш E-mail:’ и нажимает TAB (первый раз) в надежде перейти к паролю, то его ожидает разачарование, фокус не сменится. Данный баг все еще актуален.
Так, осталось совсем чуть-чуть.
После того как мы успешно авторизовались, мы поменя popup на find.html.
<!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <script type="text/javascript" src="find.js"></script> <link type="text/css" rel="stylesheet" href="find.css"> <title>Grabber</title> </head> <body> <div class="body"> <div class="carsRows" id="popup_cars_rows"> <h3 style="text-align: center; margin: 5px 0;">Найденные данный странице</h3> <form name="cars_form" action="" method="get" id="popup_cars_form"> <table id="popup_cars_table"> <thead> <tr> <th class="make">Вакансия</th> <th class="info">Город</th> <th class="addBtn"> </th> </tr> </thead> <tbody> </tbody> </table> </form> </div> <div class="carsRows" id="popup_cars_rows_none" style="display: none;"> <h3 style="text-align: center; margin: 5px 0;">Ничего не найдено на странице</h3> </div> <div id="loader"><img src="ajax-loader.gif" title="Loding" alt="Loading"></div> </div> </body> </html>
/** * OnLoad function * * @return void */ window.onload = function(){ // set new popup html code and close popup window window.bg_wnd = chrome.extension.getBackgroundPage(); var rows = window.bg_wnd.bg.getMatches(); // function render popup renderPopup(rows); } /** * Function set cars into html * * @param array $rows * @return void */ function renderPopup(rows) { if(rows.length === 0){ document.getElementById('popup_cars_rows').style.display = 'none'; document.getElementById('popup_cars_rows_none').style.display = 'block'; return 0; } else{ document.getElementById('popup_cars_rows').style.display = 'block'; document.getElementById('popup_cars_rows_none').style.display = 'none'; } for (var i = 0, cnt = rows.length; i < cnt; i++) renderRow(rows[i]); } /** * Function set cars into html * * @param object $row * @return void */ function renderRow(row) { var tbl = document.getElementById('popup_cars_table').children[1]; // add divided row var td = tbl.insertRow(-1).insertCell(-1); td.setAttribute('colspan', '3'); td.innerHTML = '<hr style="border: 1px solid #909090; width: 75%">'; var tr = tbl.insertRow(-1); var td1 = tr.insertCell(-1); var td2 = tr.insertCell(-1); var td3 = tr.insertCell(-1); var vacancy = []; var city = []; var hash = { vacancy: 'вакансия', city: 'город', } var table_row = []; for(key in row){ if(hash[key]){ if(key == 'vacancy') vacancy.push(row[key]); if(key == 'city') city.push(row[key]); } } td1.innerHTML = vacancy.join(' ');; td2.innerHTML = city.join(' '); td3.innerHTML = (row.url === '')?'<b><em>Добавлено</em></b>':'<input type="button" value="Дабавить" name="cars[]" class="button"><input type="hidden" value="'+row.url+'" name="url[]">'; td3.children[0].addEventListener('click', function(){addToGrabber(event)}, false); } function addToGrabber(e) { // hide fade on popup document.getElementById('loader').getElementsByTagName('img')[0].style.marginTop = (window.innerHeight/2-10)+'px'; document.getElementById('loader').style.display = 'block'; if(e && e.srcElement){ var url = e.srcElement.parentNode.children[1].value; setTimeout(function(){ var result = window.bg_wnd.bg.addUrlToGrabber(url); e.srcElement.parentNode.innerHTML = '<b><em>Добавлено</em></b>'; // hide fade on popup document.getElementById('loader').style.display = 'none'; }, 500); } }
Как только find.html загрузился, в работу вступает find.js. Его задача спросить bg.js: ‘Что там у тебя есть на текущую страницу’ — и отобразить то, что отдал bg.js.
/** * OnLoad function * * @return void */ window.onload = function(){ // set new popup html code and close popup window window.bg_wnd = chrome.extension.getBackgroundPage(); var rows = window.bg_wnd.bg.getMatches(); // function render popup renderPopup(rows); }
Так выглядит готовое решение.
C кнопкой ‘Добавить’ я думаю сами разберетесь, как она работает. На последок хочу сказать как все это дело отлаживается.
background.html — что бы посмотреть работу скриптов bg.js и lib.js нужно кликнуть на линку background.html на странице chrome://extensions.
script_in_content.js — он выполняется в контексте страницы, поэтому можете смело инспектировать страницу и смотреть консоль с выводом ошибок в нее.
login.html и find.html — что бы вывести их Developer Tools, нужно кликнуть на иконке приложения и правым кликом мышки выбрать инспекцию страницы.
PS. Весь JavaScript должен находится в js файлах, если вы его вставите в html — chrome будет ругаться.
Также пару ссылок:
на документацию: manifest.json, Chrome’s API
на github.com: исходный код
ссылка на оригинал статьи http://habrahabr.ru/post/198778/
Добавить комментарий