<object>, <embed> и валидный код

В детстве нас всех учили, что для встраивания флэша на страницу нужно пользоваться тегами object и embed. Объясняли это тем, что object используется Internet Explorer'ом на винде, а браузеры, основанные на Netscape понимают только embed.

Казалось бы, вставляй оба тега и будет счастье. Только вот счастья нет, потому что embed — это нестандартный тег, которого нет в спецификации HTML4.01 / XHTML1.0. (однако, он включён в черновик HTML5).

Но это ещё не вся шокирующая правда. Дело в том, что все более-менее современные браузеры поддерживают стандартный тег object.

В своём блоге я часто использую флэш, чтобы показывать видяшки и вставлять музыку. При этом я просто копирую под плеера с сайта и вставляю его в запись, либо использую oembed (о котором я скоро напишу заметку). В большинстве случаев этот кусок кода содержит оба тега, а иногда и вовсе только embed.

Все знают, что валидный HTML код — это мой пунктик. Поэтому я не стал терпеть этих невалидных корявок в своём собственном блоге и решил разобраться. Погуглив, я нашёл рецепт (написанный аж в 2002 году), описывающий, как вставлять объекты на страницу, используя при этом только валидный код. Вкратце суть его такова:

  1. Для тега object нужно указать атрибут type, например type="application/x-shockwave-flash"
  2. Также для object нужно указать атрибут data. В нём указывается URL объекта, который вы хотите вставить
  3. После этого нужно удалить тег embed нафиг

Чтобы не заниматься такой чисткой вручную, я написал простенькую функцию, которая автоматически исправляет HTML код перед отображением на странице. Вот она:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
from BeautifulSoup import BeautifulSoup, Tag


def fix_embeds(value):
    soup = BeautifulSoup(value)

    for object in soup.findAll("object"):
        movie = None
        for param in object.findAll("param"):
            if param["name"] == "movie":
                movie = param["value"]
        embeds = object.findAll("embed")
        if embeds:
            embed = embeds[0]
        else:
            embed = None
        data = object.get("data")
        if not data:
            if movie:
                object["data"] = movie
            elif embed and embed.get("src"):
                object["data"] = embed["src"]
            else:
                continue

        if not object.get("type") and embed.get("type"):
            object["type"] = embed["type"]
        del object["classid"]
        for embed in object.findAll("embed"):
            embed.extract()

    for embed in soup.findAll("embed"):
        src = embed.get("src")
        type = embed.get("type")
        if not src or not type:
            continue
        width = embed.get("width")
        height = embed.get("height")
        object = Tag(soup, "object")
        object["data"] = src
        object["type"] = type
        if width:
            object["width"] = width
        if height:
            object["height"] = height
        embed.replaceWith(object)


    return unicode(soup)

На её основе можно, например, сделать фильтр для шаблонов Django.

Обратите внимание, что я принудительно удаляю атрибут classid из тегов object. Его назначение остаётся для меня загадкой, однако если он присутствует, то не отображается музыкальный проигрыватель с Jamendo.

Пользуйтесь на здоровье. Если обнаружите какие-то косяки — пишите.

P.S. Теперь у меня полностью валидный бложек.