Форум Flasher.ru

Форум Flasher.ru (http://www.flasher.ru/forum/index.php)
-   ActionScript 3.0 (http://www.flasher.ru/forum/forumdisplay.php?f=83)
-   -   Observer JS <---> AS (http://www.flasher.ru/forum/showthread.php?t=111174)

ALiEN_ 17.04.2008 17:08

Observer JS <---> AS
 
В проектах часто необходимо использовать связки JS с FLASH... Раньше использовался getURL, fscommand, сейчас ExternalInterface, но способ использования обычно ограничивался на вызовове разных функций.
Эта система работала хорошо, но приходилось заранее продумывать систему общения, задавать имена функций.
Если немного подумать, можно схитрить и использовать всего две функции, одну входящую для JS и одну входящую для Flash, тогда отдавая в функцию два параметра, первый из которых "евент" и второй системный параметр, можно все упростить. Внутри функций можно написать простой парсер на switch и строить схему по входящим евентам.
Эта схема не плоха и действует безукоризненно, пока колличество евентов не переваливает за сотню.

Во флеше, очень удобна встроенна система евентов, но как было бы удобно если бы эта же система евентов распространилась и на JS.

Например, JS говорит - flashMovie.addJSListener ( "click", myJSFunction ); и флешка добавляет слушателя, а при генерации события "click", любые подписанные на событие слушатели в JS получат уведомление.

Теперь собственно вопросы:
- А как делаете это взаимодействие Вы?
- Есть ли какие-то готовые решения?

akerka 18.04.2008 12:38

Сделать то можно все что угодно. Вопрос размеров конечного JS файла. Проще использовать конкретные вызовы чем разрабатывать механизм прозрачного обмена сообщениями, производительный и небольшой по объему.

ALiEN_ 21.04.2008 15:18

Вобщем вот набросок, рабочий. Если кому надо, юзайте.

Код:

package
{
       
        import flash.display.Sprite;
        import flash.events.*;
        import flash.external.*;
       
        public class JSListener extends Sprite
        {
               
                private var externalHandlers = [];
               
                static private var self;
               
                static public function instance ():JSListener
                {
                       
                        if ( !self ) self = new JSListener ();
                       
                        return self;
                       
                }
               
                public function JSListener ():void
                {
                       
                        ExternalInterface.addCallback ( "addJSListener", addJSListener );
                       
                }
                               
                public function addJSListener ( eventName:String, functionName:Function ):void
                {
                       
                        externalHandlers = {};
                       
                        if ( !( eventName in externalHandlers ) ) externalHandlers [ eventName ] = [];
                       
                        externalHandlers [ eventName ].push ( functionName );
                       
                }
               
                public function JSEvent ( ev, param1 = null, param2 = null ):void
                {
                       
                        var event:Array = ( ( externalHandlers [ ev.name ] || [] ) as Array ).concat ( ( externalHandlers [ '__all__' ] || [] ) as Array );
                       
                        var o:Object = joinObjects ( param1, param2 );
                       
                        for ( var i:uint = 0, len = event.length; i < len; i++ ) ExternalInterface.call ( event [ i ], o );
                         
                }
               
                private function joinObjects ( a:Object, b:Object ):Object
                {
                       
                        var c:Object = {};
                       
                        for ( var i in a ) c [ i ] = a [ i ];
                       
                        for ( i in b ) c [ i ] = b [ i ];
                       
                        return c;
                       
                }
                               
        }
       
}

В классе так же предусмотрен евент "__all__", это если JSхочет слушать все события.


Использовать можно например так:

Код:

var jsEvent = JSListener.instance ().JSEvent;

jsEvent( { type:"my_event" } );

Т.е. добавляем ссылку на синглтон в область видимости класса. И генерим события.

Единственное что смущает, это то что приходится создавать синглтон и проводить инициализацию. Может можно сделать более красивое решение?

ALiEN_ 25.04.2008 17:51

Доработал класс. Некоторые возможности:

- прозрачная генерация и получение событий (JS <-> AS, AS <-> AS, JS <-> JS);

http://qwehkwerjhbgkwe.livejournal.com/51060.html

Psycho Tiger 26.04.2008 13:08

Большое спасибо за класс.
ЗЫ, я думаю тему есть смысл переместить в ФАК.

ALiEN_ 26.04.2008 14:21

Надо чтобы гуру форума заценили для начала ) Класс ведь реально весьма полезный и многим пригодится.
На всякий случай добавлю сюда код, т.к. в жж он побился.

Вот некоторые возможности:

- прозрачная генерация и получение событий ( JS <-> AS, AS <-> AS, JS <-> JS );
- возможность использовать флешку чисто, как обсервер для JS;
- возможность рассылать события бродкастом, т.е. нам все равно где находится подписчик какая у него глубина вложенности и т.д.;
- не имеет значения, где находится подписчик во флеш или в JS и не важно кто сгенерит событие.
- есть событие __all__, при подписке к которому подписчик будет слышать вообще все евенты.

Код:

package
{

       
        import flash.external.*;
       
       
       
        public class EManager extends Object
        {
               
               
                private var externalHandlers = [];
               
                static private var _inst:EManager;
               
               
               
                static public function get inst ()
                {
                       
                        if ( !_inst ) _inst = new EManager ();
                       
                        return _inst;
                       
                }
               
               
               
                public function EEventDispatcher ():void
                {
                       
                        ExternalInterface.addCallback ( "subscribe", subscribe );
                       
                        ExternalInterface.addCallback ( "unsubscribe", unsubscribe );
                       
                        ExternalInterface.addCallback ( "notify", notify );
                       
                }
               
               
               
                public function subscribe ( event:String, handler ):void
                {
                       
                        if ( !( event in externalHandlers ) ) externalHandlers [ event ] = [];
                       
                        externalHandlers [ event ].push ( handler );
                       
                }
               
               
               
                public function unsubscribe ( event:String, handler ):void
                {
                       
                        var handlers:Array = externalHandlers [ event ];
                       
                        if ( handlers )
                                for ( var i:uint = 0, len = handlers.length; i < len; i++ )
                                        if ( handlers [ i ] == handler )
                                        {       
                                       
                                                handlers.splice ( i, 1 );
                                               
                                                break;
                                               
                                        }
                       
                }
                       

               
                public function notify ( event:String, params:Object = null ):void
                {
                       
                        var handlers:Array = ( ( externalHandlers [ event ] || [] ) as Array ).concat ( ( externalHandlers [ '__all__' ] || [] ) as Array );
                                               
                                               
                        for ( var i:uint = 0, len = handlers.length; i < len; i++ )
                        {

                                if ( handlers [ i ] instanceof Function )
                                        handlers [ i ] ( params );
                                else
                                        ExternalInterface.call ( handlers [ i ], params );
                                                               
                        }
                         
                }
               
                               
        }
       
}

Пример использования:
Например подпишемся на евент прямо во флеше, и получим его:
Код:

EManager.inst.subscribe ( "myEvent", myFunc );

EManager.inst.notify ( "myEvent", { param1:"a", param2:"b" } );

function myFunc ( e )
{
       
        trace ( e.param1 + " " + e.param2 );
       
} // a b

подписка от JS происходит точно так же =)

BlooDHounD 26.04.2008 18:02

ничё прозрачного я в этом не вижу.

ALiEN_ 27.04.2008 15:01

а что именно смущает?

BlooDHounD 27.04.2008 20:05

собственно вся конструкция и смущает.

ALiEN_ 27.04.2008 22:16

во всяком случае, я еще альтернатив не видел.

BlooDHounD 27.04.2008 22:24

почему не использовался стандартный евентдиспатчер?

и зачем вообще писать его в таком виде? чем вас не устроили обычные методы?

ALiEN_ 28.04.2008 00:28

а в каком виде его надо было написать? )))
стандартный евентдиспатчер никуда не делся, для внутриклассовых собшений он остается. этот класс позволяет гонять события наружу - не задумываясь о том что и кто будет с ними делать.
раньше что бы что-то вызвать в JS надо было знать названия функций - теперь это вообще не обязательно JS сам подписывается, сам отписывается, сам генерит события. Это мегаудобно, и мне и яваскриптеру.

BlooDHounD 28.04.2008 00:44

так чем вас не устроил интерфейс IEventDispatcher ? зачем изобретать велосипед, с квадратными колёсами?

ALiEN_ 28.04.2008 01:58

а при чем тут IEventDispatcher, ведь если его использовать, у меня код многократно усложнится!?
Мне хотелось бы увидеть пример...
Я не говорю что мой класс идеален, но, повторюсь, альтернатив еще не видел ни с квадратными колесами ни с круглыми )

terbooter 28.04.2008 07:21

ALiEN_, по-моему штучка полезная.
А можете выложить исходники простенького примерчика, чтобы нагляднее было?

BlooDHounD 28.04.2008 11:21

да полно альтернатив :) просто найти надо :)

использование вашего класса несёт под собой несколько проблем:
1. я НЕ использую его как встроенный EventDispatcher, хотя он выполняет туже функцию.
2. нету типизации, используются динамические объекты.
3. отсутствуют слабые ссылки.

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

ALiEN_ 28.04.2008 13:35

BlooDHounD

ну где же? я не нашел. честно отписался вначале темы, что сначала искал, но может плохо. вобщем если поделишься альтернативой, буду благодарен. ))

1. он совсем не EventDispatcher в этом и разница. Он много шире - мостик между JS и AS событиями. EventDispatcher остается только для внутриклассовых событий, хотя можно, безусловно и использовать только мой класс и он справится на ура.
2. тут никуда не деться, ведь в JS все является объектами. И как раз только благодаря этому получилось этот класс написать.
3. Слабые ссылки? a зачем здесь слабые ссылки? в какое место их прикрутить ))

где тут подозрительные маханации? )) Что под этими махинациями имеется ввиду?

teebooter, сегодня выложу полноценный пример, надеюсь будет время.

etc 28.04.2008 13:38

ALiEN_, прозрачно — это когда я могу написать dispatchEvent(new MyCustomEvent(MyCustomEvent.CUSTOM_EVENT)) и оно будет получено в JS.
А то что вы написали ни в какие ворота. Есть стандартная схема событий, вы зачем-то придумали свою, не вписывающуюся ни в какие стандарты.

BlooDHounD 28.04.2008 14:09

Цитата:

Сообщение от ALiEN_ (Сообщение 735979)
1. он совсем не EventDispatcher в этом и разница. Он много шире - мостик между JS и AS событиями. EventDispatcher остается только для внутриклассовых событий, хотя можно, безусловно и использовать только мой класс и он справится на ура.

"совсем нет", но при этом я могу его так использовать? тогда где слабые ссылки, приоритеты, и т.д.?
Цитата:

Сообщение от ALiEN_ (Сообщение 735979)
2. тут никуда не деться, ведь в JS все является объектами. И как раз только благодаря этому получилось этот класс написать.

мне казалось что это класс для АС3 ... почему-то, когда серверники пишут сервер на Джаве под флэш, они не исходят из того, что у нас массивы имеют динамическую типизацию.
Цитата:

Сообщение от ALiEN_ (Сообщение 735979)
3. Слабые ссылки? a зачем здесь слабые ссылки? в какое место их прикрутить ))

а в какое место использовать ваш класс "на ура", если он не соответсвует простейшим принципам АС3 ?
Цитата:

Сообщение от ALiEN_ (Сообщение 735979)
где тут подозрительные маханации? )) Что под этими махинациями имеется ввиду?

всё выше перечисленное является такими махинациями.

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

BlooDHounD 28.04.2008 14:19

вот вам пример моей упращённой реализации прозрачной работы с ExternalInterface,
Код:

package by.blooddy.platform.external {

        import flash.external.ExternalInterface;
        import flash.utils.describeType;
        import flash.utils.getQualifiedClassName;
        import flash.utils.flash_proxy;

        use namespace flash_proxy;

        public dynamic class ExternalConnection {

                public function ExternalConnection() {
                        super();
                        if ( ExternalInterface.available ) {
                                var methods:XMLList = describeType( this ).method.@name;
                                var name:String;
                                for each ( var method:XML in methods ) {
                                        name = method.toXMLString();
                                        ExternalInterface.addCallback( name, this[name] );
                                }
                        } else {
                                // throw
                        }
                }

                public function call(name:String, ...arguments):* {
                        if ( ExternalInterface.available ) {
                                arguments.unshift(name);
                                ExternalInterface.call.apply( ExternalInterface, arguments );
                        } else {
                                // throw
                        }
                }

        }

}

любой наследник этого класса будет являться слушателем ExternalInterface. любой метод автоматически добавляется в callback. получаем некое подобие NetConnection и LocalConnection. дальше его можно расширить до уровня работы со свойством client, прикрутить диспатч евент, и динамический callback при добавлении нового метода.

самое главная демонстрация этого кода, это то, что разработчик не парится про проверки на avaible и добавления колбэков. это называется прозрачная работа. лежит яваскрипт, или C# приложение, разработчику наплевать: он уверен, что класс родитель справился со своей задачей на ура.

ALiEN_ 28.04.2008 15:50

Не понимаю, что конкретно смущает в такой реализации:

Код:

EManager.inst.subscribe ( "click", myFunc );

this.addEventListener ( "click", myFync );

Нормальная простая запись ... без гемора для разработчика.

Конечно еще привычнее чтобы многие объекты по умолчанию были наследниками как с EventDispatcher. ))) Да правильнее будет именно так.

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

ALiEN_ 28.04.2008 21:39

рабочий пример и немного подправленный класс в архиве: http://***********/1909526


Часовой пояс GMT +4, время: 13:00.

Copyright © 1999-2008 Flasher.ru. All rights reserved.
Работает на vBulletin®. Copyright ©2000 - 2026, Jelsoft Enterprises Ltd. Перевод: zCarot
Администрация сайта не несёт ответственности за любую предоставленную посетителями информацию. Подробнее см. Правила.