Pull to refresh

Создание и публикация расширения

Reading time 5 min
Views 3.7K
image

В своём предыдущем топике Bexf — Фрэймворк для создания расширений я пообещал, что напишу продолжение. Моё расширение, написанное на Bexf, опубликовали и в честь этого я решил продолжить тему расширений для Оперы.

В статье: архитектура расширения, интересные моменты в процессе написания Habra Meter(далее HM) на Bexf, публикация расширения.

Архитектура


Изучая тему расширений я просмотрел с десяток исходников и в каждом, как водится, разная архитектура. Архитектура, безусловно, дело каждого, ниже я привожу мой вариант (файловая архитектура HM):
/
    images/       // Все изображения, включая иконки
        font.png
        icon-18.png
        icon-64.png
    includes/     // Стандартное название. Скрипты, которые будут подключаться Оперой автоматически 
    vendors/      // Любые внешние библиотеки
        Bexf.js
        jquery-1.4.4.min.js
        jquery.flot-0.6.min.js
    css/          // Все CSS
        style.css
    js/           // все JS
        options.js
        popup.js
        Widget.js
    config.xml    // Стандартное название. Обязательно. Настройки расширения
    index.html    // Стандартное название. Обязательно. Индексный файл
    options.html  // Стандартное название. Опции
    popup.html    // Всплывающее окно

Стандартные файлы и папки

Папка includes/ включает в себя .js/.css файлы, которые подключаются к определенным страницам Оперой и могут взаимодействовать с расширениями. Каждый скрипт представляет из себя UserScript формата Greasemonkey
// ==UserScript==
// @name --
// @namespace --
// @description --
// @author --
// @include mediafire.com*
// @include http://*.mediafire.com/*
// ==/UserScript==

// Код скрипта
HM не имеет подключаемых скриптов.

config.xml — настройки расширения. На описании формата я не буду останавливаться все и так хорошо изложено тут www.opera.com/docs/apis/extensions/configxmlresourceguide
index.html — индексный файл, инициализирующий расширение (название можно сменить)
options.html — опции, при наличии его Опера активирует Настройки расширения. Опции это html страница любого содержания, как и popup.

Написание расширения


Дальше я не буду описывать каждую мелочь, только основные моменты. Код открыт, так что вы все сможете глянуть сами.

Index

Этот файл не имеет вида, так что нет смысла вставлять что-либо в body и подключать CSS. В моём случае скрипт подключат Bexf.js и Widget.js (основной скрипт HM). Widget инициализирует расширение через метод init.
    init: function () {
            var self = this;

            this.font = new global.Image();
            this.font.src = "images/font.png";
            this.icon = new global.Image();
            this.icon.src = "images/icon-18.png";

            this.icon.onload = function () {
                self.button = $.createButton(self._buttonInitOptions)
                               .addToPanel();
                self.requestUpdates();
                self.initTimer();
            };
        }

Метод подключает необходимые ресурсы: спрайтовый шрифт(font.png) и шаблон кнопки(icon-18.png) о них дальше. Инициализирует кнопку, сразу же её обновляет, запускает таймер обновлений.

В проекте мне понадобилось динамически менять картинку на кнопке для отображения кармы и рейтинга, притом картинка должна была создаваться на canvas. Тут была проблема: картинка максимум 18х18 и поэтому любой стандартный шрифт был бы совершенно нечитаем(впринципе то, что есть сейчас тоже особо не блещет) — создал спрайтовый шрифт 3х5: 0-9, A-F и запятая/точка все параметры букв расписал в fontDemensions. Хоть index.html не имеет вида, но зато имеет document, поэтому оказалось возможными динамически генерировать иконку через canvas и получать её данные через canvas.toDataURL(). Ниже 2 метода, отвечающие за отрисовку:
        _drawText: function (ctx, x, y, text) {
            text = text.split('');
            for (var i = 0, c = text.length, g; i < c; i+= 1) {
                g = this.fontDemensions[text[i]];
                ctx.drawImage(this.font, g.x, g.y, g.w, g.h, x, y, g.w, g.h);
                x += g.w;
            }
        },
        drawIcon: function (karma, rating) {
            karma += '';
            rating += '';
            var canvas = document.createElement('canvas');
             // Create an empty canvas element
            canvas.setAttribute('width', '18px');
            canvas.setAttribute('height', '18px');
            // Copy the image contents to the canvas
            var ctx = canvas.getContext("2d"), offset;
            ctx.drawImage(this.icon, 0, 0);
            // Draw karma
            offset = 8 - (2 * karma.length) + (karma.indexOf('.') >= 0 ? 2 : 0) + (karma.indexOf(',') >= 0 ? 2 : 0);
            this._drawText(ctx, offset, 2, karma);
            // Draw rating
            offset = 8 - (2 * rating.length) + (rating.indexOf('.') >= 0 ? 2 : 0) + (rating.indexOf(',') >= 0 ? 2 : 0);
            this._drawText(ctx, offset, 11, rating);
            return canvas.toDataURL();
        }
После закрытия окна опций, обновляем кнопку (мб сменился пользователь)
    $.disconnect(function () {
        Widget.requestUpdates.call(Widget);
        Widget.initTimer.call(Widget);
    });

Больше ничего интересного в Widget.js нет. Остался лишь парсинг страницы хабропользователя(кроссдоменный XHR), сбор статистики за месяц и прочие утилитарные методы.

Опции

options.html — обычный html файл любого содержания, предполагается, что он будет управлять widget.preferences (Bexf.opt) — личные данные расширения.
Файл имеет дефалтную структуру(размечены все поля). А логика его проста: при инициализации загружает значения в поля формы, при нажатии кнопки «сохранить», возвращает данные в widget.preferences.

Всплывающее окно

popup.html — обычный html файл любого содержания(но ограниченного размера) может иметь любое название, попапами управляет кнопка. Количество попапов не ограничено. Попап инициализируется каждый раз (исполняет весь код) когда его открывают. В случае HM в попапе находится график изменения параметров пользователя за последние 30 дней. График рисуется на canvas с помощью jQuery Flot (в первой версии рисовался Google Chart API, больше я к GC API ни ногой). Скрипт попапа парсит данные статистики(т.к. widget.preferences это DOMStorage объект, то его значения не могут быть объектом), сохраненные в Bexf.opt('stat') и переделывает их в формат, который понимает jQuery Flot. Больше ничего интересного.

Тестирование


Разработчики Opera подумали о нас, поэтому тестировать расширения очень просто: кидаем config.xml в браузер и расширение подключается в режиме отладки, его можно Перезагрузить(обновить код)/Отключить/Удалить в менеджере расширений Оперы как и обычное расширение. Как писал lugansk «Dragonfly пока не умеет с ними (Errors) работать» но метод opera.postError работает и его результат можно посмотреть в Error Console (Ctrl + Shift + O) Dragonfly пока не умеет работать с opera.postError из расширений.

Публикация


1. Вам необходимо протестировать расширение
2. Создать аккаунт Оперы (он один на все сервисы) — my.opera.com/community/signup
3. Придумать назание, краткое описание, подробное описание
4. Сделать минимум 1 скриншот
5. Нарисовать иконки 64x64(обязательно) 18x18(если есть кнопки)
6. Придумать версию расширения, например 1.0
7. Упаковать расширение в .oex — переименованный .zip
Как только все готово, логинимся и загружаем расширение, следуя инструкциям мастера загрузки addons.opera.com/developer/extensions/upload
Как только вы загрузили расширение оно попадет на модерацию (1-2 дня) и после этого будет доступно для скачивания. Обновленное расширение также проходит модерацию (1-2дня). И есть мнение, что про очень частых сменах версий модераторы понижают приоритет вашего расширения.
Подробнее о публикации addons.opera.com/developer/guidelines

Почитать


Общее темы по расширениям www.opera.com/addons/extensions/develop
Формат config.xml www.opera.com/docs/apis/extensions/configxmlresourceguide
Режим разработки dev.opera.com/articles/view/opera-extensions-developer-workflow
API расширений www.opera.com/docs/apis/extensions
Bexf — фрэймворк, который использовался при написании HM habrahabr.ru/blogs/opera/111461
Ещё один пример создания расширения dev.opera.com/articles/view/hands-on-building-an-opera-extension
Переводы доков Оперы и примеры от хабрапользователя SonicGD: Обмен сообщениями, Окна, UserJS, Ваше первое расширение для Opera, Кнопки, бэджи и всплывающие окна

Официальную версию(прошедшую модерацию) HM можно найти среди всех расширений оперы addons.opera.com/addons/extensions/details/habra-meter
Последнюю версию, архитектура который представлена в данной статье, можно скачать тут: browser-extensions-framework.googlecode.com/files/habra-meter-1.1.1.oex (это переименованный .zip)

Буду рад ответить на ваши вопросы. Теперь, думаю, вопрос о расширениях Оперы раскрыт на хабре полностью.

PS Не ставьте Habra Meter — постоянное наблюдений кармы приводит к психическим заболеваниям. Это расширение proof of concept моего фрэймворка Bexf
Tags:
Hubs:
+24
Comments 21
Comments Comments 21

Articles