Pull to refresh

Встраиваем видео, фото и статьи в Gmail интерфейс

В основное время с интересом занимаюсь Iframely — опен-сорс парсерами и протоколом для встраивания виджетов. Недавно обсуждали с парнями: ведь Gmail и почта в целом является самой распространенной социальной сетью, многие предпочитают отправлять ссылки приватно почтой. Почему бы не попробовать встроить виджеты в почту.



Попробовал — получилось за день. Результат и процесс решения для Gmail под Chrome — под катом.

Сам Gmail начал предоставлять возможность просматривать Youtube видео по ссылкам, найденным в тексте письма. В дополнительных настройках (вкладка labs) можно также включить экспериментальный просмотр ссылок Google Maps, Picassa и Flickr. В общем список сайтов небольшой. Надо бы его расширить.

Для этого нужно решить три задачи:
  1. Получить доступ к Gmail UI для поиска линков в письмах, и добавления embed контента в их тело.
  2. По URI страницы получить embed код для встраивания ее контента в письмо.
  3. Не навредить usability почты. Продукт не должен мешать работать людям.

Одним из самых простых способов решения технических задач такого рода — написание Chrome расширения.

Пишем Chrome плагин


Альтернативой плагину, конечно, может служить Gmail contextual gadget, который и используется для отображения видео с Youtube. Однако, при таком варианте людям нужно поплясать с бубном для установки. Кроме того, разработка и поддержка потребует погружения в Gmail API, а у нас только один день на эксперимент.

Итак, решено, — пишем плагин, который сканирует DOM на Gmail страницах в поисках открытых писем и линков в них.

Подключаем наш код на Gmail домене. Для этого создаем каркас простейшего chrome extension и в manifest.json прописываем требуемый минимум (приведены не все атрибуты):
{
    "content_scripts": [ {
        "js": [
            "js/vendor/jquery-1.9.1.min.js",
            "js/links_extractor.js"
        ],
        "matches": [ "https://mail.google.com/*" ]
    } ],
    "permissions": [
        "https://mail.google.com/*"
    ]
}

Тут мы используем любимый jQuery для работы с DOM, а links_extractor.js — собственно будет рабочим кодом приложения. Просим доступ на домен Gmail. Больше никуда нам пока не надо. Этого будет достаточно для того, чтобы наш скрипт links_extractor.js заработал на интерфейсе Gmail.

Получаем html элемент письма, в котором лежат искомые линки, и в которое нужно вставить их отображения. Не идеальным, но простым и нересурсоемким способом будет регулярная проверка:
setInterval(function() {
    runLinkParsing();
}, 1000);

И, собственно, “сканирующая” DOM функция:
function runLinkParsing() {
    // Each mail.
    $('.gs').each(function() {
        var $mail = $(this);

        var links = [];

        // Each mail body.
        $mail.find('.ii.gt a').each(function() {
            var $this = $(this);
            var href = $this.attr('href');

            // Skip used links.
            if ($this.attr('data-used') == "true") {
                return;
            }
            $this.attr('data-used', "true");

            links.push(href);
        });

        if (links.length > 0) {
            // Party! Do something with links.
        }
    });
}

Тут видно jQuery селектор $mail на элемент каждого письма, в которое также нужно будет добавлять embed коды встраиваемого контента.

Такого подхода достаточно для работы с Gmail UI. В их логике весь видимый контент (кроме разве диалогов) — генерируется с нуля, а при закрытии — удаляется из DOM.

Интерфейс надо сделать нативным под стили Gmail, чтобы не раздражал пользотелей. Как это было сделано можно посмотреть в исходниках расширения, думаю это не столь интересно для описания общей идеи, хоть и важно.

Просеиваем линки. Ибо не все они одинаково полезны для отображения. Большинство того, что приходит нам на почту не подлежит встраиванию. Кроме того, много писем носят “административный” характер, а никак не интересный контент, который хотелось бы лицезреть. Некоторые линки вообще нежелательно трогать, так как они могут активировать аккаунт, переустанавливать пароль и прочее.

Во-первых, будем фильтровать сами линки, для этого был создан незамысловатый массив регекспов:
// Re matched against a.href before fetching data, e.g. http://domain.com/unsubscribe
var skipHrefRe = [/subscribe/i, /activate/i, /restore/i, /reset/i, /contact/i, /support/i, /about/i, /faq/i, /help/i];

Во-вторых, будем фильтровать по отправителю письма:
// Re matched against <from>, where email is <from>@domain.com
var skipFromRe = [/support/i, /reply/i, /team/i, /news/i, /info/i, /digest/i, /update/i, /admin/i, /support/i, /notification/i, /hello/i, /offers/i, /help/i];

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

Получаем и встраиваем embed код


Эта задача, на порядок сложнее предыдущей. Но на то и занимаемся мы Iframely, чтобы было проще и можно было бы писать подобные вещи за день.

Опен-сорс Iframely гейтвей используем так:
  1. Сначала надо получить Iframely данные по странице, которые включают унифицированную мету и список линков, доступных для встраивания (картинки, плееры, статьи).
  2. Потом надо выбрать какой линк из доступных мы хотим встроить и, используя библиотеку iframely.js, сгенерировать код для вставки.
  3. Ну или положиться на oEmbed приведение от Iframely. Правда, тогда потеряются многие необходимые нам для пользовательского интерфейса аттрибуты, такие как есть ли autoplay или является ли виджет responsive.

Код получения данных по странице через iframely.js весьма тривиален:
$.iframely.getPageData(uri, function(error, data) {
    // That’s it.
});

Теперь у нас есть data.links — линки, доступные для встраивания. Теперь надо выбрать какой рендерить.

Трудности выбора линка связанны необходимостью обеспечить пользователю ненавязчивый интерфейс. В нашем случае — чтобы видео не запускались автоматом, чтобы встроенные статьи не отвлекали от основного письма и чтобы по миниму встраивались не ssl ресурсы. При этом надо уменьшить количество кликов необходимых для старта видео или просмотра фотографии.

Вкратце алгоритм выбора линка в нашем случае можно описать так:
  1. Если есть картинки, найди подходящую по размерам текущему контейнеру, желательно https.
  2. Если есть плеер (видео, музыка), голосовалка или читалка (статья) — взять первое. Как правило сайт предоставляет один такой линк с одним размером или пропорциями.

Код этого алгоритма:
// Find image (photo)
var images = $.iframely.filterLinksByRel("image", data.links, {httpsOnly: true});
if (images.length == 0) {
    images = $.iframely.filterLinksByRel("image", data.links);
}
var image = $.iframely.findBestFittedLink($container.width(),$container.width(), images);

// Find player or survey or reader.
var goodLink = $.iframely.filterLinksByRel(
    ["player", "survey", "reader"],
    data.links,
    {httpsFirst: true, returnOne: true});

$container — это место, куда будут вставляться embed коды. По его размерам среди доступных ищутся приблизительно подходящие картинки — не слишком маленькие, и не слишком большие. Строки "player", "survey", "reader", "image" — семантические типы виджетов для встраивания.

Дальше полученный линк можно встроить. Картинку можно встроить непосредственно через link.href — ссылку на файл картинки и тег . Но в общем случае работает такой метод:
var $el = $.iframely.generateLinkElement(goodLink, {iframelyData: data});
$container.append($el);

Он сам создаст , ,
Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.