Отмазка Вступление
Иногда очень хочется "подглядеть" за кем-то, просмотрев историю посещения страниц. Но, это не всегда удаётся, ведь историю посещений можно очистить. Да и не только это - в Хроме есть "режим инкогнито", при работе в нём вообще не пишется история.Исправим ситуацию
Решение:- Написать расширение, которое будет мониторить табы, записывая всяческую информацию о посещениях
- Заставить расширение работать также и в incognito mode
- Не спалиться :)
Что ж, приступим..
- В любом месте создаём папку, именуем как угодно
- Создаём manifest.json в ней. Формат - JSON. Манифест - паспорт расширения.
Листинг:
Почитать: спецификация по манифестам- {
- "name": "Mr. Trololo", //имя расширения
- "version": "0.1", //версия
- "description": "History spy.", //описание
- "icons": { //иконки - не обязательно
- "48": "48.png",
- "128": "128.png"
- },
- "background_page": "main.html", //воркер
- "options_page": "main.html", //настройки расширения
- "permissions": [ //разрешения
- "tabs", //доступ к tabs api
- "unlimited_storage" //безлимит места под хранение данных
- ]
- }
- Теперь займёмся нашим воркером.. Он-то и будет делать всю "грязную" работу :)
Что требуется от воркера:- Повесить event listener на событие onUpdated табов
- Обрабатывать каждый вызов события, записывая информацию о посещениях в localStorage
- При вызове настроек расширения (у нас воркер отвечает и за это, для простоты) предоставить интерфейс для просмотра истории посещений, а также её очистки.
Для начала реализуем javascript ядро воркера, которое будет подключаться из main.html
Листинг main.js:
Код отлично комментирован, как мне кажется- /*
- @collection
- Sorry for my bad Javascript ^_^
- */
- m = {
- key: 'history', //key for local storage
- ls: window.localStorage, //local alias
- /* Clear stored data */
- clearData: function() {
- m.ls.removeItem( m.key );
- m.ls.setItem( m.key, "[]" ); //initialize our var' as JSON's array
- },
- /* Get stored data helper */
- get: function() {
- if ( m.ls.getItem( m.key ) == null ) { //var' isn't initialized
- m.ls.setItem( m.key, "[]" );
- }
- /* Local storage provides only string saving. Using JSON for [un]serializing arrays */
- return JSON.parse( m.ls.getItem( m.key ) );
- },
- /* Set stored data helper */
- set: function( v ) {
- m.ls.setItem(
- m.key,
- JSON.stringify( (m.get()).concat( [v] ) ) //concat prev' and present data, JSON it
- );
- },
- /* Callback for event listener */
- callbackTabs: function( id, i, tab ) {
- if ( tab.status == 'complete' ) { //event fires 2 times: "loading" and "complete" states
- m.set( { //store that
- date: m.date(),
- favIcon: tab.favIconUrl || '',
- url: tab.url || '',
- title: tab.title || '',
- incognito: tab.incognito
- } );
- }
- },
- /* Ugly date format helper */
- date: function() {
- d = new Date();
- return '' + (
- ( d.getDate() > 9 ? d.getDate() : '0' + d.getDate() ) + ' ' +
- ['January','February','March','April','May','June','July','August',
- 'September','October','November','December'][d.getMonth()] +
- ' ' + d.getFullYear() + ', ' +
- ( d.getHours() > 9 ? d.getHours() : '0' + d.getHours() ) + ':' +
- ( d.getMinutes() > 9 ? d.getMinutes() : '0' + d.getMinutes() ) + ':' +
- ( d.getSeconds() > 9 ? d.getSeconds() : '0' + d.getSeconds() )
- );
- },
- /* $(id) getter */
- $: function( id ) {
- return document.getElementById( id );
- },
- /* Helper for decomposition of an URL */
- parseURL: function( url ) {
- var a = document.createElement('a');
- a.href = url;
- //some extra parts are commented, we don't need 'em
- return {
- /* source: url,
- protocol: a.protocol.replace(':',''), */
- host: a.hostname,
- /* port: a.port,
- query: a.search,
- params: (function(){
- var ret = {},
- seg = a.search.replace(/^?/,'').split('&'),
- len = seg.length, i = 0, s;
- for (;i<len;i++) {
- if (!seg[i]) { continue; }
- s = seg[i].split('=');
- ret[s[0]] = s[1];
- }
- return ret;
- })(),
- file: (a.pathname.match(//([^/?#]+)$/i) || [,''])[1],
- hash: a.hash.replace('#',''), */
- path: a.pathname.replace(/^([^/])/,'/$1')/* ,
- relative: (a.href.match(/tp://[^/]+(.+)/) || [,''])[1],
- segments: a.pathname.replace(/^//,'').split('/') */
- };
- },
- /* Displaying history helper. History iterator */
- displayData: function() {
- var html = ''; //dirty working with DOM
- (m.get()).forEach( function( v ) {
- p = m.parseURL( v.url );
- html += '<li><span>' + v.date + '</span> ' + ( v.incognito ? '<strong>incognito mode</strong> ' : '')
- + '<a href="' + v.url + '"><img src="'
- + ( v.favIcon ? v.favIcon : ('http://www.google.com/s2/favicons?domain=' + p.host) ) + '"/>'
- + ( v.title ? v.title : ((p.host + p.path ).substr( 0, 80 )) ) + '</a></li>';
- });
- m.$( 'putHere' ).innerHTML = html;
- return false;
- }
- };
- /* Add a tabs listener */
- chrome.tabs.onUpdated.addListener( m.callbackTabs );
- Теперь сам воркер, обычная html страница с подключением скрипта + небольшой интерфейс.
Код:- <script type="text/javascript" src="main.js"></script>
- <style type="text/css">
- #putHere {
- list-style-type: none;
- }
- span {
- color: #ddd;
- -webkit-transition: 1s;
- }
- span:hover {
- color: #000;
- }
- #putHere a, strong {
- margin-left: 20px;
- text-decoration: none;
- }
- img {
- margin-right: 7px;
- position: relative;
- top: 3px;
- }
- </style>
- <ul>
- <li><a href="javascript:m.displayData()">Show</a></li>
- <li><a href="javascript:( m.clearData() || m.displayData() )">Clear</a></li>
- </ul>
- <ul id="putHere"></ul>
- Расширение, в принципе, готово :) Добавим в Хром..
- Ctrl+T, chrome://extensions/
- Включить режим разработчика
- "Загрузить распакованное расширение..", выбираем папку, которую создавали в самом начале
- Если всё прошло без ошибок, то ставим галочку "разрешить работать в автономном режиме" и радуемся. Если нет - действуйте по обстановке, Хром скажет в чём и где ошибка расширения.
P.S. Страница расширения на Google Chrome Extensions
Подскажите, как можно реализовать следующее:
ОтветитьУдалитьслушаю музыку на zvooq.ru, исполнитель - композиция отображаются в title вкладки.
Как заставить сохранять историю изменений заголовка только этой страницы (только с сайта zvooq.ru)
Как сохранять/записывать эти изменения в файл? нужно для дальнейшей обработки (например отображения в CONKY)
вроде бы ваш скрипт подходит, если модернизировать его, пока дилетант в этом, возможно вообще так сделать ?
Наверно все таки не подойдет, zvooq.ru меняет заголовок страницы без перезагрузки самой страницы
ОтветитьУдалитьВ принципе… возможно.
ОтветитьУдалитьТолько концепция приложения другая.
Задать в манифесте scope доступа на zvooq.ru, повесить событие по таймеру и проверять, изменился ли title:
var window.titleWas = window.title;
setInterval(function checkTitle() {
if (window.titleWas != window.title) {
//обработчик изменения
}
}, 1000);
Самая проблема — «сохранять/записывать эти изменения в файл», так как данные возможности отсутствуют в javscript'е.
Выход из ситуации: делать через NPAPI (http://code.google.com/chrome/extensions/npapi.html и ссылка с той страницы на Mozilla Found.), тогда плагин будет иметь полный доступ к системе в контексте пользователя, запустившего процесс. Я не знаком с CONKY, но, как я понял — с ним получится работать.
Если есть опыт прикладного программирования — можете пробовать :)
Либо, dirty workaround: вешаете обработчик (комментарий выше), который при изменении заголовка делает XHR-запрос на локальный HTTP-сервер, на стороне которого (средствами PHP или чего-то ещё) делается та самая запись в файл.
ОтветитьУдалитьРешение лёгкое, но установить ваше расширение до конца сможет только маньяк, помешанный на выводе текущего трека в CONKY :)
Если для себя — вполне сгодится.
Нужно написать не сложное расширение для chrome , у кого есть знания в области написания расширений обращайтесь Skype: vladel1
ОтветитьУдалить