Пишем расширение для google chrome

в 13:11, , рубрики: Google Chrome, google chrome extensions, javascript, Песочница, метки: ,

Написать расширение для google chrome несложно. Но при написании первого раширения могут возникнуть (и возникают) вопросы. Большинство мануалов по написанию первого расширения расчитаны на использования манифеста первой версии, поддержка которого в скором будущем прекратится.

В этой статье будет рассмотрено:

  • Как составлять манифест v.2
  • Как работать с удаленными ресурсами
  • Как работать с cookies
  • Как работать с local storage
  • Как работать с уведомлениями

Введение

К концу статьи у нас будет готово расширение-органайзер, в котором будет поле для добавления новой задачи, а так же список задач на текущий день. Обозначим все требования к органайзеру:

  • Должен иметь поле для добавления события (дата, время, событие)
  • Должен отображать все задачи на текущий день, отсортированные по времени
  • Все прошедшие события должен отображать зачеркнутыми
  • Должен иметь поле для ввода времени, за сколько надо показывать уведомление, а так же чекбокс разрешающий и запрещающий показывать уведомления
  • За указанное время до события должен отображать уведомление о приближающемся событии

Манифест

Начнем создавать расширение с самого начала, то есть с манифеста. Манифест – это тот самый файл, в котром прописываются все параметры расширения. Название, описание, версия, разрешение на доступ к сайтам, разрешение на использование кук, уведомлений, локального хранилища. В общем, манифест – это мозг расширения. Создаем файл manifest.json. Манифест – единственный файл, который должен иметь заранее предопределенное имя, все остальные файлы можно будет называть как угодно. В этом файле есть три обязательных поля:

manifest.json

{
	“name”:  “Organizer extension”,  // Название расширения
	“version”: “1.0”,  // Версия расширения.
	“manifest_version”: 2 // Версия манифеста
}

Тут есть пара правил:

  • Версия манифеста должна быть целочисленной, то есть должна писаться как 2, а не “2”.
  • Версия расширения должна быть строковой, но содержать только числа и точки, то есть “1.0” — хорошо, а 1.0 и “0.9 beta” — плохо.

С обязательными полями – все, перейдем к созданию всплывающего окна расширения. Для того, чтобы по нажатию на пиктограмму, открывалось окно, необходимо добавить в манифест поле “browser_action”

manifest.json

{
…
"browser_action": {
	"default_title": "Open organizer", // Заголовок. Его видно если навести курсор на иконку в браузере
	"default_icon": "icon_small.png", // Путь к иконке расширения
	"default_popup": "popup.html" // Путь к странице с попапом
}
}

Теперь создадим всплывающее окно. Это обычная html страница, которая может быть любого размера и цвета, никаких фокусов. Назовем файл “popup.html”. Создать этот файл мало – его надо указать в манифесте. Так мы и сделали: «default_popup»: «popup.html».

popup.html

<!DOCTYPE html>
<html>
	<head>
		<title></title>
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
	</head>
	<body>
		<div>It works!</div>
	</body>
</html>

Добавление расширения в браузер

Теперь пришло время проверить работоспособность нашего расширния. Для этого загрузим расширение в браузер. Открываем в хроме меню расширений. Ставим птицу на “Developer mode”.
Пишем расширение для google chrome
После этого появятся три кнопки. Нажимаем “Load unpacked extension...”. Выбираем папку с файлами расширения. После этого появится наше расширение. Если все правильно, то по нажатию на иконку – повится окно:
Пишем расширение для google chrome

Подключение скриптов

Теперь можно приступить к интересному. Подключим два javascript файла. Первый – popup.js, второй – jquery. С первым проблем не возникнет, но jquery будем подключать не локальный, а удаленный, взятый по адресу ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js. Проблемы возникнут от того, что по умолчанию расширение не имеет доступа к сторонним ресурсам. Чтобы получить доступ, надо его указать в манифесте. Доступ к чему-либо указывается в поле “permissions”. Так же, для удаленных скриптов и css надо указывать доступные удаленные ресурсы.

manifest.json

{
…
"permissions": [
	"https://ajax.googleapis.com/*"
],
"content_security_policy": "script-src 'self' https://ajax.googleapis.com; object-src 'self'"
}

Теперь подключим эти скрипты в popup.html

popup.html

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script src="popup.js"></script>

Storage

При помощи storage в хроме можно хранить пользовательские данные. И именно в storage наше расширение и будет хранить грядущие события. На то есть две причины. Во-первых, данные, хранищиеся в storage можно синхронизировать, если залогиниться в браузере. А во-вторых, данные можно хранить не только в виде строки, как в cookies, а в любом виде, то есть можно хранить и массивы и объекты. Чтобы это заработало, откроем доступ к storage в манифесте.

manifest.json

{
...
"permissions": [
  …
  "storage"
]
...
}

Теперь переделаем всплавающее окно. Во всплывающем окне будет поле с сегодняшней датой, три инпута для даты, времени и описания нового события, кнопка для добавления нового события, а так же список всех событий на сегодня.

popup.html

<div id="container">
  <div id="today_date">Date</div>
  <table class="table">
    <tr>
      <td>Дата</td>
      <td>Время</td>
      <td>Задача</td>
    </tr>
    <tr>
      <td><input type="text" id="new_date" value="" class="input_date" /></td>
      <td><input type="text" id="new_time" value="" class="input_date" /></td>
      <td><input type="text" id="new_task" value="" class="input_task" /></td>
    </tr>
  </table>
  <ul></ul>
</div>

И сразу же добавим отображение даты в блоке #today_date.

popup.js

$(function(){
  var today = new Date();
  $("#today_date").html(today.getDate()+"."+(parseInt(today.getMonth())+1)+"." + today.getFullYear());
}

Выглядеть должно так:
Пишем расширение для google chrome

Итак, при нажатии на кнопку “+” у нас должно добавляться событие. Вначале файла объявим глобальную переменную storage – объект для работы с storage, а так же глобальный массив tasks для хранения событий.

popup.js

var storage = chrome.storage.sync;
var tasks = new Array();
$(function(){
  …
  $("#add_task").click(function(){
    var new_task = new Object();
    new_task.date = validateField($("#new_date").val(), "date");
    new_task.time = validateField($("#new_time").val(), "time");
    new_task.task = $("#new_task").val();
    if(!new_task.task || !new_task.date || !new_task.task){
       return false;
    }
    tasks[tasks.length] = new_task;
    storage.set({
      tasks:tasks
    });
  });
});

Функция валидации проверяет, что дата записана в формате d.m.yyyy, а время в формате hh:mm, а так же, что в описании события не меньше трех символов.

popup.js

var validateField = function(val, type){
    if(type == "date"){
        var date = val.split(".");
        var year = new Date();
        year = year.getFullYear();
        if(date.length == 3 && parseInt(date[0]) == date[0] && date[0] <= 31 && parseInt(date[1]) == date[1] && date[1] <= 12 && parseInt(date[2]) == date[2] && date[2] >= year){return val;}
    } else if(type == "time"){
        var time = val.split(":");
        if(time.length == 2 && parseInt(time[0]) == time[0] && time[0] < 24 && parseInt(time[1]) == time[1] && time[1] < 60){return val;}
    } else if(type == "task" && type.length >= 3){
        return val;
    }
    return null;
}

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

popup.js

$(function(){
  …
  var now_hours = today.getHours() < 10 ? "0" + today.getHours() :  today.getHours();
    var now_minutes = today.getMinutes() < 10 ? "0" + today.getMinutes() : today.getMinutes();
    var now_time = now_hours + "" + now_minutes;
    storage.get("tasks",function(items){
        if(items.tasks){
            tasks = items.tasks;
            var today_tasks = getTodayTasks(tasks);
            if(today_tasks.length >0){
                for(var i in today_tasks){
                    var this_time = today_tasks[i].time.replace(":", "");
                    var add = this_time > now_time ? "" : ' class="done"';
                    var add_html = '<li'+add+'><strong>'+today_tasks[i].time+'</strong> '+today_tasks[i].task+'</li>';
                    $("ul").append(add_html);
                }
            }
        }
    });
  …
});

Функция getTodayTasks() возвращает из общего списка только события с сегодняшней датой.

popup.js

var getTodayTasks = function(tasks){
    var today_tasks = new Array();
    var today = new Date();
    var today_date = today.getDate()+"."+(today.getMonth() + 1 )+ "." + today.getFullYear();
    for(var i in tasks){
        if(tasks[i].date == today_date){
            today_tasks[today_tasks.length] = tasks[i];
        }
    }
    if(today_tasks.length > 0){
        today_tasks = sortTasks(today_tasks);
    }
    return today_tasks;
}

Функция sortTasks() сортирует события по возрастанию времени.

popup.js

var sortTasks = function(tasks){
    if(tasks.length > 0){
        var swapped = true;
        while (swapped) {
            swapped = false;
            for (var i=0; i < tasks.length-1; i++) {
                var this_time = tasks[i].time.replace(":", "");
                var next_time = tasks[i+1].time.replace(":", "");
                if (this_time > next_time) {
                    var temp = tasks[i];
                    tasks[i] = tasks[i+1];
                    tasks[i+1] = temp;
                    swapped = true;
                }
            }
        }
    }
    return tasks;
}

Уведомления

Пришло время настроить отображение уведомлений на экране. Добавим во всплывающее окно специальный чекбокс. Если этот чекбокс будет отмечен – уведомлениея будут показываться, если не будет отмечен – не будут. Так же добавим текстовый инпут. Цифра в этом инпуте будет показывать, за какое время до событя будет показываться уведомление. То есть если у нас событие назначено на 19:00, в этом текстовом инпуте будет 5, значит в 18:55 появится уведомление. Добавим в popup.html код с этими инпутами

popup.html

<div>
  <input type="checkbox" id="show_notifications" checked="checked" />
  <input type="text" id="when_to_notify" class="input_date" value="" /> 
  Показывать уведомления
</div>

Пишем расширение для google chrome

Теперь давайте разберемся с тем, как это будет работать. При нажатии на чекбокс, будет проверяться его атрибут checked, значение атрибута будет записываться в cookie “show_notifications”. Перейдем к текстовому инпуту. По изменению его значения, новое значение будет валидироваться, если оно целочисленное и не больше 120, записываем новое значение в cookie “when_to_notify”.

Но для того, чтобы у нас это заработало, надо открыть доступ к cookies. Для этого заходим в manifest.json и добавляем в “permissions”

manifest.json

{
...
"permissions": [
	…
	“cookies”
]
...
}

Теперь можно приступать к скрипту. Заходим в popup.js. Для начала установим первоначальные значения в инпутах. По-умолчанию чекбокс не отмечен, то есть уведомления не показываются, а время равно 0. При клике на чекбокс, будет меняться значение cookie “show_notifications”. При изменении значения в тектовом поле, будет меняться значение cookie “when_to_notify”.

popup.js

$(function(){
    setCheckbox();
    setWhenToNotify(getCookie("when_to_notify"));
    ...
    $("#show_notifications").click(function(){
        setCookie("show_notifications", document.getElementById("show_notifications").checked);
    });
    $("#when_to_notify").change(function(){
        setWhenToNotify($(this).val());
    });
});

Рассмотрим подробнее функции. Начнем с функций работы с cookies. В данном случае были взяты готовые функции с w3schools.com.

popup.js

var setCookie = function(c_name,value,exdays){
    /*
     *Взято с http://www.w3schools.com/js/js_cookies.asp
     */
    var exdate=new Date();
    exdate.setDate(exdate.getDate() + exdays);
    var c_value=escape(value) + ((exdays==null) ? "" : "; expires="+exdate.toUTCString());
    document.cookie=c_name + "=" + c_value;
}

var getCookie = function(c_name){Позвонить Васе П.
    /*
     *Взято с http://www.w3schools.com/js/js_cookies.asp
     */
    var i,x,y,ARRcookies=document.cookie.split(";");
    for (i=0;i<ARRcookies.length;i++){
        x=ARRcookies[i].substr(0,ARRcookies[i].indexOf("="));
        y=ARRcookies[i].substr(ARRcookies[i].indexOf("=")+1);
        x=x.replace(/^s+|s+$/g,"");
        if (x==c_name){
            return unescape(y);
        }
    }
}

Разберемся с функцией setCheckbox(). Она получает cookie “show_notifications” и проверяет, если полученное значение равно “true”(текстовое, да), то параметр checked у чекбокса true, иначе false.

popup.js

var setCheckbox = function(){
    var val = getCookie("show_notifications")
    document.getElementById('show_notifications').checked = ((val == "true") ? true : false);
}

Теперь рассмотрим setWhenToNotify(). Она принимает новое значение таймера. Если это значение – целочисленное и не больше 120 минут, то в cookie “when_to_notify” устанавливается новое значение. Если переменная не прошла эту валидацию, то в инпут возвращается предыдущее значение из cookies “when_to_notify”.

popup.js

var setWhenToNotify = function(val){
    var last_val = getCookie("when_to_notify");
    last_val = last_val != "undefined"? last_val: 0;
    val = (parseInt(val)==val && val <=120) ? val : last_val;
    setCookie("when_to_notify", val);
    $("#when_to_notify").val(val);
}

Перейдем к самим уведомлениям. Для этого откроем доступ к уведомлениям и подключим фоновый background.js. Нужно подключить именно фоновый файл, так как если уведомления вызывать из popup.js, то уведомления будут появляться только если открыто всплывающее окно.

manifest.json

{
...
"permissions": [
    …
    "notifications"
    ],
"background": { "scripts": ["background.js"] },
"web_accessible_resources": ["icon_small.png"],
...
}

Последняя строчка дает доступ к удаленному файлу. Дело в том, что картинка, которая отображается в уведомлении обязательно должна быть доступна расширению удаленно. В данном случае файл локальный, но доступ открывать все равно надо. Теперь возьмемся за background.js. Объявим переменную storage и пустой массив tasks. Далее раз в минуту скрипт будет получать список сегодняшних событий и получать из них список задач, которые должны произойти через указанное время. После этого для каждой такой задачи будет показано уведомление.

background.js

var storage = chrome.storage.sync;
var tasks = new Array();
setInterval(function() {
    storage.get("tasks",function(items){
        if(items.tasks && getCookie("show_notifications") == "true"){
            tasks = getTodayTasks(items.tasks);
            if(window.webkitNotifications){
                var texts = getNextTask(tasks);
                for(var i in texts){
                    show(texts[i]);    
                }
            }
        }
    });
}, 60000);

Функции getTodayTasks() и getCookie() взяты из popup.js. Так что начнем разбор с функции getNextTask(). Функция сравнивает текущее время и время события, если оно равно тому значению, которое хранится в cookie “when_to_notify”, то в массив next дописывается строка из времени события и его описания. После проверки всех событий возвращет массив next.

background.js

var getNextTask = function(tasks){
    var now = new Date();
    now = now.getTime();
    var next = new Array();
    for(var i in tasks){
        var date = tasks[i].date.split(".");
        var time = tasks[i].time.split(":");
        var task_date = new Date(parseInt(date[2]), parseInt(date[1]-1), parseInt(date[0]), parseInt(time[0]), parseInt(time[1]), 0, 0);
        task_date = task_date.getTime();
        var delta = Math.round((task_date-now)/60000);
        if(delta == getCookie("when_to_notify")){
            next[next.length] = tasks[i].time + " - " + tasks[i].task;
        }
    }
    return next;
}

Функция show() показывает уведомление с заданным текстом.

background.js

var show = function(text) {
    var notification = window.webkitNotifications.createNotification('icon_small.png','Есть дело', text);
    notification.show();    
}

Результатом работы этого скрипта будет такое вот уведомление:
Пишем расширение для google chrome

Послесловие

Как и обещано, в конце статьи у нас есть готовое расширение-органайзер для Google Chrome.
Теперь добавим расширение в Chrome Web Store. Загружать надо расширение, запакованное в .zip-архив. Для начала заходим в Web Store. Для этого заходим в хроме на вкладку “Приложения” и нажимаем кнопку “Web Strore”
Пишем расширение для google chrome
Теперь заходим в меню. Для этого нажимаем шестиренку и открываем “Developer dashboard”
Пишем расширение для google chrome
Нажимаем большую кнопку “Add new item”. После этого надо будет выбрать zip-архив с расширением нажать “upload”
Пишем расширение для google chrome
Далее надо заполнить небольшую форму с описанием расширения. Теперь есть выбор либо сохранить расширение в черновил либо опубликовать. Просто так опубликовать его не получится так как регистрация в Web Store стоит 5 $. Плату за регистрацию надо платить один раз для аккаунта, а не для каждого расширения. После оплаты появляется позможность опубликовать расширение, но и тут надо быть готовым к тому, что его будут валидировать несколько дней.

Полезные ссылки

Описание полей манифеста: developer.chrome.com/trunk/extensions/manifest.html
Работа с storage: developer.chrome.com/trunk/extensions/storage.html
Информация по уведомлениям: developer.chrome.com/extensions/notifications.html
Исходники расширения: github.com/bozheville/orginaizer_extension

Автор: bozheville

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js