ГлавнаяПо делу › Страшный XML для безстрашных пионеров начинающих флеш разработчиков.

Страшный XML для безстрашных пионеров начинающих флеш разработчиков.

December 29th, 2008


Вступительная речь (литературщина)

Помните "Страшные сказки для безстрашных пионеров" Эдуарда Успенского? Что? Страшно было?! То то же! Чего только стоит один "гроб на колесиках" и "красная рука". Мне особенно нравилось о чёрной простыне, которая прилетала в лагерь и душила непослушных пионеров. В то далекое время лихорадило и колотило от страха всех учеников от первых до выпускных класов. Страну охватил децкий истероз. Родители писали гневные письма с требованиями немедленно прикратить публикацию этих жутких расказов. Расказы оказались немыслимо страшные для стирильной децкой психики, привыкшей к добрым крокодилам Генам, Хрюшам и Степашкам (если вы поняли о чем я). Нинешним же пионерм никакой Фреди Крюгер баки не забъет!
Итак о чем это я? Собственно каждый уважающий себя пионер-старшекласник должен иметь (получить в наследство от старшего брата или сматерить сам) в своем арсенале самопал, мундштук для курения бычков, зажигалку и кучу прочих вещей… Начнем с мундштука и попытаемся вкурить о чем изложено ниже.
Мундштуков т.е. класов для загрузки XML в сети пруд пруди. Да и статей кстати тоже. Но все же. Я попытался в этом тексте коротко изложить все самое самое. После прочтения текста, любой начинающий флеш-разработчит сможет запросто:

    — Загрузть XML любой сложности и легко получить из него все что потребуется.
    — Рзобратся с политикой безопасности Flash Player.
    — Обойти ограничения кросс-доменной загрузки файлов с помощю простого PHP скрипта.
    — CDATA и запрещенные символы.
    — Как запрещенные символы быстро впихнуть в htmlText.
    — Встроить к себе на сайт читалку LiveJournal.
    — Приспосабливать полученый исходный код демо-приложения в своих целях.

Кратко о XML

XML (англ. eXtensible Markup Language — расширяемый язык разметки; произносится [экс-эм-эл]) — рекомендованный Консорциумом Всемирной паутины язык разметки, фактически представляющий собой свод общих синтаксических правил. XML предназначен для хранения структурированных данных (взамен существующих файлов баз данных), для обмена информацией между программами, а также для создания на его основе более специализированных языков разметки (например, XHTML), иногда называемых словарями. XML является упрощённым подмножеством языка SGML.

Целью создания XML было обеспечение совместимости при передаче структурированных данных между разными системами обработки информации, особенно при передаче таких данных через Интернет.
Более подробно можно почитать тут.

Демо приложение. Читалка ЖЖ RSS

Это работающее приложение. Простой просмотрщик страшного RSS моего ЖЖ. Очень запросто можно переделать в читалку чего угодно. Так как RSS везде одинаковый, большого труда это не должно составить. Использовать можно как есть т.е. с дизайном, если он вам нравится.
Кстати, если вы заметили, вокруг приложения нету квадратной рамки с надписью "Click here to activate this control". Как от нее избавится? В списке файлов ниже обратите внимание на ссылку SWFObject v1.5

Понятное дело что Flash имеет массу ограничений по отображению HTML тэгов. В ячейке ниже выводится полная версия поста. Делается это с помощью короткого JavaScript.
NB: За период Лето 2007 у меня в ЖЖ есть очень объемные записи с фотографиями. Так что если экономите трафик, особо не кликайте.

Класс XMI расширяющий возможности XML

Как и встроеный класс XML, класс XMI имеет метод load(); действующий точно так же как и XML.load();
Есть еще три дополнительных события:

    onXMLLoad – оповещающее о успешной загрузке XML;
    onLoadFailure – оповещающее о том, о чем нам слышать совсем не желательно. Тоесть об ошибке. Статус и таблица разшифровки значений, такая же как и для ХМЛ.

0 без ошибок; парсинг успешно завершен.
-2 Секция CDATA неправельно оканчивается.
-3 декларация XML неправельно оканчивается.
-4 декларация DOCTYPE dнеправельно оканчивается.
-5 Комментарий неправельно закрыт.
-6 Элемент XML неправельно форматирован.
-7 Нехватает памяти.
-8 Значение атрибута неправельно оканчивается.
-9 Начальный тэг не совпадает с закрывающим его конечным тэгом.
-10 Конечный тэг не совпадает с открывающим его начальным тэгом.

    onXMLLoadProgress – Используется в случаи если нужно загрузить большой статический XML файл. Без информативного прилоадера в таких случаяж никак не обойтись, пользователь должен знать "а долго ли еще?". Транслирует с интервалом раз в 100 милисекунд всем подключенным объектам статус загрузки XML файла (т.е. количество загруженых байтб а так же столько их есть всего)

В классе XMI нас интересует всего лишь одино событие onXMLLoad. Ниже варианты простого использования:

Самый простой и быстрый пример использования.
import XMI;
// создаем новый экземпляр класа
var my_xml:XMI = new XMI();
my_xml.addListener(this); //подключаем слушателя
my_xml.load('ink_rss.xml');
//
this.onXMLLoad = function(xml_obj:Object, xmi_instance:XMI) {
        trace("my_xml loaded");
        var items_length:Number = xml_obj.channel.item.length;
        for (var i = 0; i<items_length; i++) {
                var date = xml_obj.channel.item[i].pubDate;
                trace(date);// в этом месте должно вывести все даты из загруженого XML
        }
};
this.onLoadFailure = function() {trace("fail to load");};

Ахуеть! Дайте две.

Как быть если надо загрузить несколько XML файлов? Очень просто. Нужно всего лишь создать лишь дополнительный объект-слушатель для события onXMLLoad

import XMI;
//
var listener:Object = {};
var cdata_xml:XMI = new XMI();
cdata_xml.addListener(listener);
cdata_xml.load('cdata_xml.xml');
//
listener.onXMLLoad = function(xml_obj:Object, xmi_instance:XMI) {
        trace("cdata_xml loaded");
};

При использовании в AS2 классе, как то не совсем удобно создавать лишние объекты лишь для прослушки событий onXMLLoad. Поэтому можно использовать еще такой вариант опознавания экземпляров класса XMI:

private function onXMLLoad(xml_obj:Object, xmi_instance:XMI) {
        if (xmi_instance == items_xml) {
                // делай раз
        } else if (xmi_instance == search_xml) {
                // делай два
        }else if (xmi_instance == other_xml) {
                // делай три :)
        }
}

Собственно сам класс изнутри как он есть. Все что он делает это:
Конвертирует повторяющихся узлов XML в массив объектов. Уникальные же узлы просто в объекты, используя имена атрибутов как свойства объектов а значения соответственно как значения.

dynamic class XMI extends XML {
        private var xml_data_obj:Object;
        private var interval:Number;
        function XMI() {
                super();
                AsBroadcaster.initialize(this);
                ignoreWhite = true;
        }
        public function addListener(xobj:Object) {
        }
        public function removeListener(xobj:Object) {
        }
        public function broadcastMessage(event_name:String) {
        }
        public function load(url:String) {
                interval = setInterval(this, "checkProgress", 100);
                return super.load.apply(super, arguments);
        }
        private function onLoad(success:Boolean) {
                //
                if (!success || status != 0) {
                        clearInterval(interval);
                        return broadcastMessage("onLoadFailure", status);
                }
                var xml_data_obj:Object = parse(firstChild);
                var cnt:Number = 0;
                for (var i in xml_data_obj) {
                        ++cnt;
                        if (cnt>1) {
                                break;
                        }
                }
                if (cnt == 1) {
                        xml_data_obj = xml_data_obj[i];
                }
                var tmp = {};
                tmp.data = xml_data_obj;
                broadcastMessage("onXMLLoad", tmp.data, this);
        }
        private function parse(node:XMLNode):Object {
                var value:Object = new Object();
                var nodes:Number = node.childNodes.length;
                for (var i = 0; i != nodes; ++i) {
                        var name:String = node.childNodes[i].nodeName;
                        if (name != null) {
                                if (value[name] != undefined) {
                                        if (!(value[name] instanceof Array)) {
                                                value[name] = new Array(value[name]);
                                        }
                                        value[name].push(getValue(node.childNodes[i]));
                                } else {
                                        value[name] = getValue(node.childNodes[i]);
                                }
                        } else {
                                value = getValue(node.childNodes[i]);
                        }
                }
                var attributes:Object = getAttributes(node);
                if (attributes != null) {
                        if (nodes != 0) {
                                if (!(value instanceof XMLNode)) {
                                        for (var i in value) {
                                                attributes[i] = value[i];
                                        }
                                } else {
                                        attributes['_val'] = value.nodeValue;
                                }
                        }
                        return attributes;
                }
                return value;
        }
        private function getAttributes(node:XMLNode):Object {
                var attributes = new Object();
                for (var i in node.attributes) {
                        attributes[i] = node.attributes[i];
                }
                return i != undefined ? attributes : null;
        }
        private function getValue(node:XMLNode):Object {
                switch (node.nodeType) {
                        case 1 :
                        return parse(node);
                        case 3 :
                        return node;
                }
                return null;
        }
        private function checkProgress() {
                var bytes_loaded:Number = getBytesLoaded();
                var bytes_lotal:Number = getBytesTotal();
                if (bytes_loaded == bytes_lotal) {
                        clearInterval(interval);
                }
                broadcastMessage("onXMLLoadProgress", bytes_loaded, bytes_lotal);
        }
}

Политика безопасности FlashPlayer. Кросс-доменные правила загрузки (crossdomain.xml)

Вкратсе история такая. Как только мы начинаем грузить какие либо даные используя:

XML.load();
XML.sendAndLoad();
LoadVars.load();
LoadVars.sendAndLoad();
loadVariables();
loadVariablesNum();

Flash документ пытается загрузить файл с кросс-доменными правилами (crossdomain.xml, cross-domain policy file). Файл с кросс-доменными правилами представляет из себя XML файл, который дает возможность серверу "показать", что его данные и документы доступны для SWF файлов, находящихся в указанных в файле с правилами доменах. Любой SWF файл, который находится в домене, указанном в файле с правилами на сервере, будет иметь доступ к файлам и данным на этом сервере.
Мой crossdomain.xml выглядит так:

    <?xml version=“1.0″?>
    <!– http://ink.envisionext.com/crossdomain.xml –>
    <cross-domain-policy>
      <allow-access-from domain=“ink.envisionext.com”     />
      <allow-access-from domain=“*.envisionext.com” />
      <allow-access-from domain=“76.9.3.44″   />
      <allow-access-from domain="*.sharedfont.com" /> Это значит, что если вдруг Иван захочет у себя на сайте разместить SWF который грузит XML с моего сайта, то все у него все получится.
    </cross-domain-policy>

Когда Flash документ пытается получить доступ к файлу в другом домене, Flash Player автоматически пытается загрузить файл с правилами с этого домена. Если домен, в котором находится Flash документ, включен в файл с правилами, он получает доступ к данным.

Файл с правилами должен иметь имя crossdomain.xml и может находиться в корневом или ином каталоге сервера (в последнем случае необходимо будет указать где при помощи ActionScript, например так:
System.security.loadPolicyFile(“http://ink.envisionext.com/docs/xml_files/crossdomain.xml“);.
Файлы с правилами работают только на серверах, взаимодействующих по HTTP, HTTPS или FTP. Файл с правилами зависит от протокола и порта сервера, на котором он находится.

Как уже видно, XML файл с правилами состоит из одного единственного тэга <cross-domain-policy>, который, в свою очередь, содержит 0, 1 или более тэгов <allow-access-from/>. Каждый тэг <allow-access-from/> содержит аттрибут domain, определяющий либо точный IP адрес, либо точное имя домена, либо групповой символ, указывающий на любой домен. Групповым символом может быть астерикс (*), соответствующий всем доменам и всем IP адресам, или астерикс с суффиксом, соответствующий всем доменам с таким суффиксам. Если дочитали до этого места, то подробнее можно дочитать на русском сдесь.

Файл с правилами, не содержащий <allow-access-from/> тэгов, равносилен его полному отсутствию.

Примите к сведенью.

На практике же облом начинается вот в тамом месте:

Допустим грузите вы XML из указаного ниже URL.:

XML.load("http://envisionext.com/main.xml");

И локально все абсолютно отлично работает.
Закачали вы все файлы на сайт, набираете адресс в броузере http://www.envisionext.com/myAppliaction.html …а там ничё и не грузится.
А теперь дубль два:
набираете адресс в броузере немного подругому http://envisionext.com/myAppliaction.html и о чудо! Все грузится и все работает.
Все дело в том что WWW разпознается как поддомен. Тоесть срабатывает кроссдоменная политика загрузки файлов. Вы пытаетесь загрузить файл с другого домена. Кстати, все это справедливо и не работает и в обратном порятке, если у вас прописано XML.load("http://www.envisionext.com/main.xml"); а страницу вы открываете без WWW http://envisionext.com/myAppliaction.html
Поетому, если в приложении используются прямые ссылки на загружаемые файлы, без crossdomain.xml обойтись никак нельзя. Но если очень хочется, то можно…

С этого места можно уже спроить:
— А как собственно, в приложение на этой странице грузится XML с сайта LiveJournal? Ведь не положено же! Неужто в файл crossdomain.xml на LiveJournalе вписано <allow-access-from domain=“*.envisionext.com” />?
— Конечно нет! Куда мне с такой рожей в калашный ряд?
О том как обойти кроссдоменную политику загрузки с помощью небольшого PHP скрипта читайте ниже. А пока вот о чем:

Security Settings

Если вы распакуете архив с файлами используемыми в даном тексте и попытаетесь запустить my_lj_app.swf

локально, то Flash Player выдаст вот такое окошко (Картинка 1):
В котором написано что "Следующее приложение на вашем компютере пытается загрузить данные из интернет…". И дальше, как и можно предположить, работать ничего не будет.
Знакомо такое окошко?

Adobe Flash Player Security Window
Картинка 1

Чтобы избавится от такого окона в ваших приложениях (что немаловажно для флещ-разработчика) надо пойти на сайт Macromedia и в Global Security Settings panel добавить локальную папку из которой вы запускаете ваше приложение в список разрешенных локаций. В моем случаи это папка I:wwwroot см. (Картинка 2). При попытке запуска SWF файла из другой папки или диска, окно безопасности все еще будет срабатывать.

Global Security Settings panel
Картинка 2

Вот так выглядит вкладка Settings Managerа в которой нужно добавлять надежные, провереные безопасные места из которых вы открываете свои SWF файлы загружающие данные из интернет.

PHP файл. Обход кроссдоменной загрузки

Ну конечно же я не договаривался с LiveJournal о размещении моего домена в их файле crossdomain.xml, да у них и файла такого помоему и нет. Станет каждый флеш девелопер тревожить по чем зря разработчиков LiveJournal и что это будет? Что бы не задавать лишних вопросов и не просить глупостей у серёзных людей, проделываем такой фокус.
Делаем так, что как будто-бы наши XML и SWF файлы находятся в одном домене. Реализовывается это с помощью простого PHP скрипта.
Файл прост до невозможного. Всего несколько строк. Все что он делает это:
— Открывает страницу содержащую XML моего ЖЖ
— По одному килобайту вычитывает содержимое этой страници
— Закрывает файл и выдаем прочитаный XML как результат своей работы.
Вот так просто и достигается эфект присутствия “два в одном”, тоесть два файла в одном домене.

<?php
$rh = fopen (“http://users.livejournal.com/_ink/data/rss”, “r”);
while (!feof($rh))
{
  $ret= fread($rh, 1024);

  if ($ret  == FALSE) { fclose($rh);

        return false;
    }else {
        echo $ret;
        }
}
?>

Кстати. Такая же фишка прокатит в случае если вам надо открыть например картинку с чужого или своего поддомена. Речь сейчас не об этом, но есть весьма интересный глюк, когда невозможно доступится к BitmapData загруженой с другого домена картинки.

CDATA или куда деть запрещенные символы

В случаи с RSS LiveJournal все символи которые могут испортить XML форматирование переведены с помощью аналога PHP функции htmlspecialchars в удобоваримый вид.
Тоесть:

&’ (ampersand) стает ‘&amp;’
‘"’ (double quote) стает ‘&quot;’
”’ (single quote) стает ‘&#039;’
‘<’ (less than) превращается в ‘&lt;’
‘>’ (greater than) и сотвецтвенно ‘&gt;’ и так далее…

Но иногда просто таки жизненно неоходимо впихнуть что то поизвращенней. Но скорее всего, из за того что Flash имеет проблемы с отобрадением HTML, нужно впихнуть предформатированиы
<PRE> текст. Я засовываю его обычно в CDATA.
Что такое CDATA? CDATA — <![CDATA[Это всякая муть с кучей специальных символов которой место как раз тут. Например такая: <div id="flashid" style="width:100%; height:325px;">]]>. Дальше и больше о CDATA ничего знать ненадо. Итак. Что же делать с полученым в таком виде (&lt;div id="flashid" style="width:100%; height:325px;"&gt;]]&gt;) текстом? Он совсем не годится для отображении в htmlText полях.
Есть способ дурацкий, а есть правельный. Итак правельный способ. Заодно бонусом ответ на поставленый (если учесть что этот текст я начал писать два года назад то "только что поставленый") вопрос в сообществе ru_flash.

     Для примера с CDATA и ответа на вопрос берем XML найденый там же:

<?xml version=’1.0′ encoding=‘utf-8′ ?>
<menuitems>
        <item type=“battons”>
            <battName>Cyclone Aviation</battName>
            <datatext><![CDATA[<b>This is bold text </b> And this is not! &quot;&quot;&quot; &lt;&gt;!!== <font color="#DA0000">RED</font>]]></datatext>
            <simage>1.jpg</simage>
            <bimage>Image1.jpg</bimage>
            <parts>
                <image id=“ids1″ mainmenuid=“vfvdf” slideid=“sfsd” submenuid=“jkjkkjkj” video=“no” smalpicture=“Image1.jpg” bigpicture=“Image1.jpg” order=“1″ />
                <image id=“ids2″ slideid=“sfsd” submenuid=“fhfhf” mainmenuid=“vfvdf” video=“no” smalpicture=“Image14.jpg” bigpicture=“Image15.jpg” order=“2″ />
                <image id=“ids3″ slideid=“sfsd” submenuid=“fhgfhgy” mainmenuid=“vfvdf” video=“no” smalpicture=“Image15.jpg” bigpicture=“Image15.jpg” order=“3″ />
            </parts>

        </item>

        <item type=“battons”>

            <battName>Cyclone Aviation</battName>
            <datatext><![CDATA[The explosions took place minutes apart at an open-air auditorium and a popular outdoor restaurant.]]></datatext>
            <simage>2.jpg</simage>
            <bimage>Image2.jpg</bimage>
            <parts>
                <image id=“ids1″ mainmenuid=“vfvdf” slideid=“sfsd” submenuid=“jkjkkjkj” video=“no” smalpicture=“Image1.jpg” bigpicture=“Image1.jpg” order=“1″ />
                <image id=“ids2″ slideid=“sfsd” submenuid=“fhfhf” mainmenuid=“vfvdf” video=“no” smalpicture=“Image14.jpg” bigpicture=“Image15.jpg” order=“2″ />
                <image id=“ids3″ slideid=“sfsd” submenuid=“fhgfhgy” mainmenuid=“vfvdf” video=“no” smalpicture=“Image15.jpg” bigpicture=“Image15.jpg” order=“3″ />
            </parts>
        </item>
</menuitems>


Итак. Открывает файл cdata_usage.fla приложеный в архив и смотрим.
Как доступится к тексту в CDATA? После загрузки XML именно так:

trace(xml_obj[0].datatext); //&lt;b&gt;This is bold text &lt;/b&gt; And this is not! &amp;quot;&amp;quot;&amp;quot; &amp;lt;&amp;gt;!!== &lt;font color=&quot;#DA0000&quot;&gt;RED&lt;/font&gt;

Как получить читаемый HTML? Именно так:

var cdata_str = xml_obj[0].datatext;
var str = new XML(cdata_str).firstChild.nodeValue;
out_txt.htmlText = str;

И наконец то ответ на вопрос о картинках:


// для того что бы доступится к ID картинок
trace(xml_obj[0].parts.image[0].id); //ids1
trace(xml_obj[0].parts.image[1].id); //ids2
trace(xml_obj[0].parts.image[2].id); //ids3

Список файлов в архиве

Filename                        Size      Description
-------------------------   --------      -----------
cdata_xml.xml                   2 KB        XML файл с примером CDATA
ink_rss.xml                     29 KB       XML файл RSS моего ЖЖ
get_livejournal.php             1 KB        Скрипт чтения RSS из моего ЖЖ

LJitem.as                       2 KB        Класс айтема для отображения заголовка и даты
MyLJApp.as                      3 KB        Класс приложение, в этом месте все и собирается
XMI.as                          3 KB        Класс XMI, собственно вокруг него и весь разговор
SimpleButton.as                 1 KB        Мелочи жизни...простейшая кнопка
SimpleScroller.as               2 KB        Скролус Симпулус или скроллер обыкновенный, тоже из отряда простейших :)

my_lj_app.fla	               78 KB        Исходник приложения
my_lj_app.swf                  57 KB        Компилированая версия
simple_usage.fla               42 KB        Примера самого простого использования класса XMI
simple_usage.swf                2 KB


swfobject.js                    7 KB        JavaScript файл SWFObject v1.5 (настоятельно рекомендую ознакомится)
flash_resize.js                 2 KB        JavaScript файл для изменения высоты слоя в который выводится контент.

Архив файлов для скачивания (исходные файлы)

Скачать файлы 112 Kb

Ну вот собственно и все. Текст этот начал я писать года два назад. Да все никак руки не доблудили зайти да закончить. Очень надеюсь что он хоть кому то будет полезен. Если да, то пришлите мне открытку :). Время паковать файлы. Я надеюсь что после прочтения даного текст о прочитавшем его начинающем флеш-разработчике скажут как говорил великий Classик Пушкин.AS:
“и лёгким манием руки на русских двинул он полки”

NOTA BENE

За все грамматические ошибки в этом тексте и прочие недопонимания смысла несет ответственность Ваш провайдер. Плохая передача даных нах!

Категория: По делу Ярлыки: , ,

Смотрите также

Декодирование картинки из Base64 и вывод полученого изображения во Flash. Конвертирование Nellymoser ASAO Codec FLV файлов в WAV/MP3
  1. Макс
    March 2nd, 2010 at 15:34 | #1

    Я в as не силен, знаю только примитивные вещи, я аниматор.
    Три дня искал, как же связать xml и кнопку флеша, чтобы по клику загружался в текстовое поле форматированный текст – ни чего дельного не смог найти и применить.
    Спасибо вам огромное. Ваш ресурс и исходник очень помогли. Собственно я и искал исходник, т.к. лучше один раз увидеть, чем читать бесконечные статьи и частные обсуждения и терять время.
    Спасибо.

  2. September 6th, 2010 at 13:18 | #2

    Здравствуйте
    У меня вопрос – всю плешь уже проел себе
    Вы пишите про ЧТЕНИЕ из CDATA
    А как записать туда и сохранить все это без кракозябликов?
    Я спрашиваю именно про XML
    Допустим, я пишу
    var textNodeValue:String=”Содержимое секции CDATA”;
    var mainXML:XML=new XML(“”);
    var cdataNode:XMLNode=new XMLNode(3,””);
    mainXML.appendChild(cdataNode);

    В сохраненном файле, естественно, кракозяблики
    Подскажите как мне “зааппендить” :) нод как CDATA нод
    Или может быть на стадии прочтения этих кракозябликов(когда я открываю файл) есть что-то что их переводит в символы ?

  3. September 6th, 2010 at 13:21 | #3

    сожрала цмс кусок кода в строке cdataNode:XMLNode=
    После 3йки открывались кавычки в них записывал я начало секции CDATA с квадратными скобками + textNodeValue + в кавычках закрывал CDATA

  4. September 6th, 2010 at 15:46 | #4

    нашел ответ:
    вместо var cdataNode:XMLNode=new XMLNode нужно использовать ХМЛ
    то есть
    var cdataNode:XML = XML(все что внутри CDATA)

  1. Пока нету.