Tom Adler’s blog

opera + prism: desktop web-applications

несколько лет назад опера научилась запускать в отдельных системных окнах маленькие приложения-виджеты, вроде тех, что сейчас часто можно видеть на стартовых экранах смартфонов. Конечно, так можно было запускать и приложения покрупнее, но этим мало кто занимался. Тем более, что эти приложения работали только до тех пор, пока работала опера. А использовать отдельный процесс оперы с отдельным профилем — хлопотно.

но вот недавно виджеты стали стандартом W3C, а опера кардинально улучшила их поддержку: теперь они работают как независимые программы, и так же независимо их можно устанавливать и удалять. Немедленно множество людей сообразило, что в таком виджете можно запускать уже существующие веб-приложения, достаточно одного ифрейма. Правда, с первых попыток у меня это не заработало, но я оставил себе заметку на будущее.

будущее наступило : ) И теперь достаточно нескольких кликов, чтобы превратить веб-приложение в «настольное»! Зайдите оперой на страничку Opera Prism: desktop web-applications, введите урл и нажмите Install. Или скачайте его на диск, чтобы поредактировать. Или отправьте конечную ссылку другу. Update: лучше всего использовать Widgetize.

конечно, я не буду утверждать, что опера была первее всех — это очевидно даже из названия заметки. До неё таким трюкам научились Mozilla Prism и Хром. Однако у оперы есть преимущество: она ест заметно меньше памяти, и эти приложения более похожи системные (вплоть до автопакетирования в deb в убунте).

а иконок нет пока только из-за моей лени ; ) Вернее, я не знаю ещё нормального способа добывать их автоматически или вручную.

,

созрели новые стандарты!

конец года выдался урожайным на приятные новости. Несколько модулей CSS3 наконец стали реальными стандартами, выйдя из стадии, где каждый браузер реализует тот предварительный вариант, который ему больше нравится (а их фанаты поплёвывают на оперу, ждущую хоть какой-то определённости).

итак, самый крутой модуль — CSS3 Selectors — теперь является Proposed Recommendation, и ему остался последний шаг до W3C Recommendation.

следующий в списке CSS3 Backgrounds and Borders, который сейчас стал Candidate Recommendation. Это этап, на котором разработчики спеки официально заявляют: «пришло время воплощать идеи в коде».

ну и последний модуль — CSS Multi-column Layout, который теперь тоже Candidate Recommendation.

подозреваю, что кто-то в опере уже включил флажок в мейкфайле, чтобы в Рождественском релизе Оперы оказался давно готовый у них код для реализации обоих этих модулей-кандидатов.

p.s.: если кто не слышал, Еврокомиссия в очередной раз признала Микрософт нарушителями закона и заставила добавить в винду экран выбора браузера. Приятно, что в Евросоюзе такие законы. Приятно, что кто-то может добиться их выполнения. Даже если этот кто-то — скромная норвежская компания.

как добавить pubsubhubbub в блог на django

наверное, вначале стоит пояснить, что такое PubSubHubbub. Эта технология была придумана недавно как замена веб-пингам, позволяющим оповестить всех заинтересованных, что у вас в блоге появилась новая запись. Для этого при добавлении записи нужно было отправить http-запрос на один или несколько специализированных серверов, которые дальше сами рассылали уведомления всем подписавшимся. PubSubHubbub развивает эту идею дальше, добавляя в этот запрос само содержимое поста, так что заинтересованные сайты не ломятся все сразу в ваш блог, чтобы скачать фид и вынуть из него новый текст. Однако для большего нашего удобства работает и старый механизм: достаточно «пингануть» подходящий сервер, и он сам скачает фид, извлечёт из него пост, и разошлёт его подписчикам

кратко процесс можно описать так:

  1. вы добавляете запись
  2. ваш блог отправляет серверу сообщение «в таком-то фиде новое содержимое»
  3. сервер скачивает фид и находит в нём новый пост
  4. сервер отправляет всем подписанным на обновления этого фида новый пост

итак, что нужно сделать для подключения вашего блога на django к этой радости? Во-первых, выбрать PubSubHubbub-сервер. Я сам пока знаю только один: http://pubsubhubbub.appspot.com/. Пропишем его в настройках (settings.py):

PUSH_URL = u'http://pubsubhubbub.appspot.com/'

затем нужно скачать и установить питоновский модуль для публикации в PuSH. Установка после распаковки делается обычным sudo python setup.py install

после этого нужно в методе сохранения записи (тут я не могу давать конкретику, потому что не знаю внутренностей вашего блога) добавить собственно отправку оповещения:

from pubsubhubbub_publish import publish
publish(settings.PUSH_URL, u'http://your.blog/feed.url')

но это самый простой вариант, мне хочется оповещать ещё и об обновлениях в фидах тегов:

from pubsubhubbub_publish import publish
from django.core.urlresolvers import reverse as reverse
params = [settings.PUSH_URL, u'http://your.blog/feed.url']
params.extend(map(lambda tag: 'http://your.blog%s' % (reverse('tag_feed', args=[tag])), tags))
publish(*params)

теперь остался последний шаг: нужно добавить в сам фид информацию о том, за ним можно следить при помощи PuSH. Вообще-то это должно делаться очень просто — добавлением в него вот такой строчки:

<link href="http://pubsubhubbub.appspot.com/" rel="hub" />

но в django для фидов используется навороченный Syndication Framework, поэтому придётся унаследоваться от стандартного класса отрисовки фида Atom1Feed и добавить строчку вот таким образом:

class PubSubHubbubEnabledFeed(Atom1Feed):
    def add_root_elements(self, handler):
        super(PubSubHubbubEnabledFeed, self).add_root_elements(handler)
        handler.addQuickElement(u'link', attrs={u'rel': u'hub', u'href': settings.PUSH_URL})

после этого в определениях классов фидов нужно заменить feed_type = Atom1Feed на feed_type = PubSubHubbubEnabledFeed

всё, добавление PuSH завершено! Пройдёт некоторое время, пока заинтересованные сайты узнают, что ваши фиды транслируют обновления таким способом, и после этого новые записи будут появляться на них практически моментально

ну а если вам хочется свести код минимуму, то наверное link можно прописать в фиде вручную, и вместо вызова метода publish просто отправлять на сервер запрос с параметрами hub.mode=publish&hub.url=yourfeedurls

кстати, вот обсуждение, которое сподвигло меня на доработку блога и этот пост

,

оптимизация userjs: порядок событий загрузки

я уже писал о том, что в опере код user javascript может быть исполнен в разное время в зависимости от расширения файла. Впрочем, многим скриптам нужно запускаться только тогда, когда весь DOM уже загружен. С привязкой к событиям load и DOMContentLoaded у меня не всегда всё было гладко (как выяснилось, из-за глупых опечаток), поэтому сейчас мне захотелось досконально разобраться, какой из вариантов привязки лучше всего использовать.

итак, я быстро набросал простенький html и два почти идентичных скрипта: test.js и test.user.js, отличающихся только расширением и строчкой идентификации внутри. В пустом профиле оперы я положил скрипты в папку userjs, и открыл сам документ. Вот что появилось после этого в консоли:

test.js: start
html: end of head
html: end of body
test.js: document.DOMContentLoaded (capture)
test.js: document.DOMContentLoaded
test.js: window.DOMContentLoaded (capture)
test.js: window.DOMContentLoaded
test.user.js: start
test.js: document.load (capture)
test.js: document.load
html: document.onload
test.user.js: document.load (capture)
test.user.js: document.load
test.js: window.load (capture)
test.js: window.load
html: body.onload
test.user.js: window.load (capture)
test.user.js: window.load

какие из этого можно сделать выводы?

  1. нет смысла использовать window.opera.addEventListener для таких событий: ни один из вариантов с ним не сработал
  2. как и было заявлено, родной опере формат .js (в отличие от «приблудного» от greasemonkey .user.js) исполняется до начала парсинга документа и, соответственно, до события DOMContentLoaded. Поэтому в скриптах лучше всего использовать вызов document.addEventListener('DOMContentLoaded', handler, true), не забывая про последний параметр, как нередко делал я : )
  3. код из файлов .user.js исполняется до события load на документе, поэтому в них можно использовать document.addEventListener('load', …), но наверняка можно обойтись и без него

подозреваю, что порядок событий load, назначенных разными способами, представляет интерес только для разработчиков браузеров, вынужденных трудиться над совместимостью. Кое-что в нём логично, что-то удивительно (например, body.onload идёт после document.load и даже window.load). Впрочем, теоретически это исследование может помочь несчастному, занимающемуся поддержкой кривого кода, но мне хочется верить, что проблем с этим ни у кого не возникнет : )

,

исключения в обработчиках аякс-запросов

в prototype.js используется не совсем очевидный способ работы с исключениями, возникающими при обработке аякс-запросов: по умолчанию они подавляются и не попадают в консоль ошибок вообще. Чтобы их видеть, нужно добавлять в параметры запросов

onException: function logException(request, exception) {
  // handle exception
}

или зарегистрировать глобальный обработчик:

Ajax.Responders.register({ onException: logException });

Однако при этом может возникнуть коллизия с другой фичей фреймворка: автоматическим исполнением пришедшего с сервера яваскрипта. По умолчанию установлено evalJS: true, и если в заголовках обнаружится что-то вроде Content-Type: text/javascript, прототайп попробует исполнить тело ответа как код, словит исключение, и передаст его в onException.

бороться с этим явлением можно двумя способами: либо добавлять в параметры каждого аякс-запроса evalJS: false, либо отдавать json с Content-Type: application/json. Первый способ просто непрактичен — некрасиво и где-нибудь наверняка забудется. Второй иногда невозможен…

хорошего решения проблемы я пока не знаю : (

,