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

    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
    Поделиться публикацией
    Похожие публикации
    Реклама помогает поддерживать и развивать наши сервисы

    Подробнее
    Реклама
    Комментарии 21
    • НЛО прилетело и опубликовало эту надпись здесь
    • 0
      Отлично, как раз хотел узнать про публикацию
      • –13
        А какова сейчас доля Оперы?
      • +1
        Спасибо за статью! Ой как мне ее не хватало недели две назад, когда пытался разобраться с написанием расширений.
        • +1
          >> так, что ошибки расширений не отлавливаются в Dragonfly (у всех так?) мне пришлось извращаться с try catch'ем и записью текста ошибки в widget.preferences

          Dragonfly пока не умеет с ними работать, пробуйте Error Console (Ctrl + Shift + O), для отладочных сообщений в консоли ошибок можно ещё пользоваться opera.postError.
          • 0
            opera.postError из расширений и не работает, чего только не переключал.
            • 0
              Странно, статья, на которую я дал ссылку именно о расширениях. И скриншот результатов работы кода там прилагается.
              Странно, может в последних версиях проблемы появились, надо проверить.
              • 0
                В Dragonfly ошибки нет, а в Error Console есть. Спасибо за наводку забыл про старую консоль ошибок Оперы.
              • 0
                Юзайте

                window.opera.postError(«Ошибка»);
                Иногда приходится дописывать пространство имен windows.
                • 0
                  Это не поможет, ибо window есть global.window (window.window === window === this === this.window) т.е. ссылка на самого себя. Когда вы делаете такое обращение, вы на самом деле не явно указываете на window (как ожидаете), а выполняете трюк, показанный выше.
                  • 0
                    Если честно, механизма, описанного вами не знаю, просто поделился тем, что у меня работает. А вот почему «не поможет» не понял. Вы имели ввиду что это костыль?
                    • 0
                      Я имею ввиду, что window.opera.postError("Ошибка"); это тоже самое, что и opera.postError("Ошибка"); как ни крути
                      • +1
                        В BackgroundProcess'ах (user_js) расширений пространство имен по-умолчанию вовсе не window. Поэтому если нужна работа с документом, приходится уточнять… т.е. document.body -> window.document.body
            • +2
              «Теперь, думаю, вопрос о расширениях Оперы раскрыт на хабре полностью» — Эта фраза должна звучать уже после статей с практической работой со вкладками, с сообщениями фоновым процессам и подобные штуки, просто добавить кнопку и показать выпадающую штуку не сложно. Это детально расписано на сайте оперы.
              • 0
                А планируется ли сделать фреймворк кроссбраузерным?
                • 0
                  Планировал, но пересмотрел планы, а проект на гуглокоде так и остался browser-extensions-framework. Слишком много зависимостей от каждого браузера XUL/HTML разный нейминг методов, разный доступ к модификации интерфейса. Максимум, что можно выжать, особо не парясь, это Opera+Chrome (т.е. любые браузеры поддерживающие или хоть как-то похожи на W3C Widgets), но пока нет времени этим заняться.

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