понедельник, 11 августа 2008 г.

Easy Way to Embed Transparent PNG24 into IE6 without JavaScript (vml xhtml)

Итак, есть задача - отобразить полупрозрачный png24:

  1. Способ должен работать везде, в том числе и в IE6 (и IE5 тоже было бы неплохо);
  2. Код должен быть семантичным;
  3. Картинка не является фоновой, но подумать о фоновом PNG не помешает;
  4. Желательно обойтись без яваскрипта;
  5. Посторонние визуальные эффекты недопустимы;
  6. Тормоза недопустимы;
  7. Лишние запросы на сервер и лишний трафик крайне нежелательны;
  8. Код должен быть валидным;
  9. При невозможности соблюдения всех вышеперечисленных условий должен соблюдаться принцип graceful degradation.
Решение
И, как всем известно, есть одна маленькая проблема - версии IE младше седьмой полупрозрачный PNG отобразить не в силах. Решений множество, но все они мне не нравятся по причине плохого качества - в них не соблюдается принцип graceful degradation [9] или [2, 4, 6, 7].
По сути нужно решить две задачи:
  1. Выделить IE6-, оставив остальным просто тег img.
    Это достаточно просто реализуется через условные комментарии для IE. Это просто волшебная вещь.
    [if (lt IE 7)&vml]
  2. Вылечить IE6-.
    Обычно все используют фильтры и JS, что и приводит к [5, 6]. Но есть способ лучше! Это VML! Поддерживается начиная с IE5.
    <v:image src="flower.png" style="width:85px;height:88px;" />
В очередной раз убеждаюсь, что тестировать нужно только в реальном окружении! Что эмуляторы IE врут безбожно. Забудьте по MultipleIEs и IETester. Поставьте официально бесплатную виртуальную машину от майкрософт с официально бесплатным образом нужной винды и увидите разницу.
Кстати, валидатор от W3C таки научился понимать пространства имен. Это позволяет не использовать условные комментарии для включения VML.

Особенности метода
  1. Картинка должна иметь указанный размер, иначе не будет показана;
  2. Картинка должна иметь точно указанный размер, иначе будет отмасштабирована;
  3. Бывают непонятные смещения в 1px. Лечится через margin;
  4. До работы с фоновым изображением у меня руки не дошли, теоретически это тоже можно эмулировать через v:image. Когда понадобится - решу и эту проблему.
Глюки
  1. Неполноценное кеширование [7]. IE6 всегда будет посылать лишние запросы If-Modified-Since. В IE7 это залечили, кстати, но нам это сейчас бесполезно. Решить эту проблему в IE6 мне не удалось;
  2. Значок и контур на месте загружаемой картинки [5]. Очень неприятно. Очень. Т.е. не работает нормальная интеграция в DOM и браузер не в состоянии определить загружена картинка или нет. Решить эту проблему мне не удалось.
Заключение
Я считаю, что эти глюки не противоречат [9]. Доля IE6 падает и проблема уходит. А пока она не ушла совсем, то можно и потерпеть, если пользуешься устаревшим браузером. Безусловно, всем этот метод не всем подходит, но на моих проектах доля IE6 уже менее 15%. Прогресс идет.

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

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

воскресенье, 10 августа 2008 г.

The Best Way to Embed Flash 9 into XHTML without JavaScript, part I

Я тут совершенно случайно ввязался в масштабное исследование, посвященное внедрению флеша в страницы. Точнее, я просто не подозревал, что вокруг столь небольшой темы написано просто умопомрачительное количество некорректной информации.

Итак, необходимо внедрить Flash 9 (AVM2) и при этом достигнуть баланса со следующим списком требований:

  1. Должно играть до полной загрузки ролика (streaming);
  2. Должна работать коммуникация Flash с браузером, в обе стороны;
  3. Должно работать во всех более-менее заметных браузерах;
  4. Должно работать с типом контента application/xhtml+xml для xhtml;
  5. Нежелательны лишние запросы на сервер;
  6. Категорически нежелательны посторонние визуальные эффекты;
  7. Решение должно быть валидным;
  8. Если плагина нет - должен ставиться, если плагин устарел - должен обновляться;
  9. Очень хотелось бы обойтись без яваскрипта;
  10. Желательна отдача referer при загрузке swf, а то ходят слухи, что иногда не отдается;
  11. Желательна работа при режиме высокой безопасности в IE и при отключенном ActiveX;
  12. Отсутствие глюков - чтобы параметры передавались, прозрачность работала и т.д.;
  13. Семантичность - отсутствие лишних сущностей в коде;
  14. Отсутствие необходимости щелкать для активации в IE и Opera;
  15. При невозможности соблюдения всех вышеперечисленных условий должен соблюдаться принцип graceful degradation.
Изучение темы показало, что используются следующие основные способы внедрения флеша в страницу:
  1. через тег embed;
  2. через тег object;
  3. также можно воспользоваться JS-библиотекой типа SWFObject2;
  4. комбинация вложенных тегов object или object&embed.
Начнем с JS [9]
Честно говоря, смысл использования SWFObject от меня ускользнул. Если не вкладывать никакого дополнительного смысла в море статей о нем, то все речи сводятся к щелчку для активации, невнятному "упрощению интеграции" и "определении версии плеера". Первое быстро становится неактуально, т.к. майкрософт убрал эту необходимость. Второе - простно враньё, ведь процесс только замедляется [5, 9], становится менее надежным [3] и работает не всегда [4, 9]. Третье - тема необычайно сложная и не от SWFObject в ней всё зависит. А нужно, чтобы работало [8].
Поэтому, по возможности, сторонние JS-библиотеки я постараюсь не использовать.

Режим высокой безопасности в IE [11]
В этом случае всё печально - Flash работать не будет. Совсем, как не исхитряйся.
Нюансы в IE:
  1. Embed не работает (работает только noembed), но IE ничего не сообщает и не предлагает. Что плохо. И, кстати, атрибут PLUGINSPAGE в IE не работает [8];
  2. При использовании object IE7 выдает желтую полосу с предупреждение, что ActiveX отключен и ссылку на справку. Что неплохо.
Т.е. [11] из списка требований исключается ввиду нереализуемости.

embed
Это тег старый, на данный момент абсолютно нестандартный, но зато поддерживаемый почти всем чем только можно. Кстати, имеет все шансы реинкарнироваться в html5. Всем хорош, только есть у него несколько родовых травм: сейчас невалиден [7] и, главное, совсем не работает мост JS-to-AVM2 [2] во всех версиях IE. Работает только SetVariable для AVM1. Также есть вышеописанная проблема с [11]. Т.е. применение этого тега возможно, но только в некоторых случаях: не нужен JS или не нужен IE. Увы, это не ко мне. Копаем дальше.

Комбинация вложенных тегов object или object&embed
На мой взгляд, вложенные теги - бестолковая комбинация [13], права на жизнь не имеет. Лишняя возня с определением реально используемого тега ID для JS не добавляет привлекательности. object&embed - это вообще не пойми что по причине вышеописанных проблем у embed. Изначально, наверное, идея была в том, чтобы обойти браузеры не поддерживающие тег object, но такие экземпляры вымерли ещё 10 лет назад даже на мобильных устройствах.     
А вот пара тегов object и условных комментариев явно имеет право на жизнь, хотя и усложняет код.

object
Тег рекомендован W3C, так что в теории всё должно быть хорошо. Но на практике имеется миллион глюков и куча устаревшей и мусорной информации вокруг его использования. Честно говоря, умаялся я этот мусор разгребать, всё описывать не буду, только основные находки и соображения:
  1. Указывать атрибут classid для IE нет необходимости. Отлично работает и просто type="application/x-shockwave-flash";
  2. Наличие атрибута data убивает streaming в IE [1]! Кроме того, IE ещё и уродливую рамку на месте объекта отображает, пока всё не загрузит [6];
  3. Отсутствие атрибута data приводит к незагрузке ролика в Firefox! А вот всем остальным браузерам достаточно параметра movie;
  4. Атрибут STANDBY, позволяющий разместить комментарий типа "Идет загрузка" пока грузится swf не работает в IE. В остальных не проверял, т.к. во всех остальных всегда работает streaming;
  5. Атрибут codebase="http://fpdownload.adobe.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,124,0" отлично работает в IE, но не работает в остальных браузерах. А IE предлагает загрузить новую версию плеера, если текущая устарела. Наверное лучше, по возможности, использовать "#version=9,0,0,0" т.к. майкрософт распространяет версию 9,0,115,0 в составе SP3. Этим мы несколько уменьшим кол-во загрузок, т.е. будем меньше беспокоить наших пользователей;
  6. Атрибут codetype можно не использовать, т.к. type достаточно;
  7. Эксперименты с атрибутами тега param valuetype и type не увенчались успехом;
  8. Эксперименты с data="data:application/x-shockwave-flash," не увенчались успехом.
Возможные решения
Видно, что всё вертится вокруг борьбы с атрибутом data:
  1. Можно не бороться, а одновременно использовать и атрибут data, и параметр movie.
    <object width="400" height="300" type="application/x-shockwave-flash"
    data="http://example.org/flash.swf">
      <param name="movie" value="http://example.org/flash.swf">
    </object>
    Работать будет везде, но в IE работать будет уродливо.
  2. Можно бороться только с Firefox присвоением data через JS
    <object id="swf1" width="400" height="300" type="application/x-shockwave-flash">

      <param name="movie" value="http://example.org/flash.swf">
    </object>
    <js> document.getElementById("swf1").data = "http://example.org/flash.swf" </js>
    Причем на firefox можно и не проверять - отлично работает и без этого.
    Способ в целом неплохой, но требует включенного JS под Firefox.
  3. Можно бороться только с IE через условные комментарии
    <!--[if IE]>
      <object id="swf1" width="400" height="300" type="application/x-shockwave-flash">
    <![endif]-->
    <!--[if !IE]><-->
      <object id="swf1" width="400" height="300" type="application/x-shockwave-flash" data="http://example.org/flash.swf">
    <!--><![endif]-->

      <param name="movie" value="http://example.org/flash.swf">

    </object>
    Это, пожалуй самое лучшее решение. Работает везде (похоже, что вообще в абсолютно всех браузерах) и работает красиво, плавно, без посторонних эффектов.
Понятное дело, что я за последний вариант. Замечания по нему:
  1. Небольшое дублирование кода. Настолько небольшое, что это и недостатком сложно назвать;
  2. Забыли про щелчки для активации;
  3. Забыли про альтернативный контент, и про инсталляцию плеера, если его нет;
  4. Забыли про обновление плеера, если устарел.
Обновление и инсталляция плеера
Это тема сложная, достойная рассмотрения в следующей (ещё не написанной) части статьи, поэтому сейчас ограничусь общими соображениями:
  1. Вообще-то плеер умеет обновляться и сам. Он проверяет наличие обновлений при загрузке очередного ролика, но не чаще чем раз в 30 дней. Можно и помочь браузеру обновит плеер, задав атрибут codebase="http://fpdownload.adobe.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0". Это имеет смысл только для IE, т.к. codebase должен указывать только на свой домен, что в данном случае не соблюдается. IE на это плюет, а остальные блюдут. Причем, можно не повторять codebase у каждого тега object, а ограничиться однократным упоминанием.
  2. Если плеера нет, или он недоступен, то будет отображен так называемый альтернативный контент. Соответственно, нужно этим контентом сказать пользователю, что ему нужно установить плеер.
    <a href="http://www.adobe.com/go/getflashplayer">
    Для корректной работы сайта вам необходимо установить проигрыватель Adobe Flash<br />
    <img src="http://www.adobe.com/images/shared/download_buttons/get_flash_player.gif" alt="Установить" /></a>
    А если используется IE, то теоретически можно и ничего не говорить, т.к. вначале отработает предложение установить плагин по codebase, а после этого пользователь должен пенять на себя.
    Кстати, заметили, что все ссылки уже давно переехали с macromedia.com на adobe.com?
Щелчок для активации
Это, пожалуй, самая большая проблема, также достойная рассмотрения в следующей (ещё не написанной) части статьи. На данный момент щелчка требуют IE6 и IE7 без последних обновлений и Opera9. Т.е. проблема вроде как изживает сама себя, но всё-таки хотелось бы решить красиво. Жаль, что программно узнать необходимость фикса невозможно.

Заключение
Вот ссылки на пример:
  • HTML (Content-Type: text/html);
  • XHTML (Content-Type: application/xhtml+xml) - напоминаю, xhtml в IE не работает.
Предложено достаточно элементарное, но весьма даже рабочее решение. Работа над недоработками ведется. Исследована применимость тега embed, который также небезынтересен.

Я специально оговорюсь: я знаю, что я не открыл Америку. Как минимум один человек из Чехии уже публиковал аналогичное решение, но проблема в том, что очень часто чтобы задать вопрос нужно знать ответ. Этой заметкой я описываю не только найденное решение, но и кучу сопутствующей информации. Надеюсь, кому-нибудь это поможет. Кроме того, работа не окончена, я ещё буду возвращаться к этой теме. Я был бы очень благодарен за любые отзывы, критику и сообщения об ошибках.