Exemple d’API externe : communications entre ActionScript et JavaScript dans un navigateur Web

Flash Player 9 et les versions ultérieures, Adobe AIR 1.0 et les versions ultérieures

Cette application exemple illustre les techniques de communication possibles entre ActionScript et JavaScript au sein d’un navigateur Web. Il s’agit d’une application de messagerie instantanée qui permet à l’utilisateur de discuter avec lui-même (d’où le nom de l’application : Introvert IM). L’API externe permet d’envoyer les messages entre un formulaire HTML dans la page Web et une interface SWF. Les techniques décrites dans cet exemple sont les suivantes :

  • Vérification de la disponibilité du navigateur avant d’établir la communication afin de garantir une initialisation correcte

  • Vérification de la prise en charge de l’API externe par le conteneur

  • Appel des fonctions JavaScript à partir d’ActionScript, transmission des paramètres et réception des valeurs en retour

  • Mise à disposition des méthodes ActionScript que JavaScript doit appeler et exécution de ces appels

Pour obtenir les fichiers d’application de cet exemple, voir www.adobe.com/go/learn_programmingAS3samples_flash_fr. Les fichiers de l’application Introvert IM se trouvent dans le dossier Samples/IntrovertIM_HTML. L’application se compose des fichiers suivants :

Fichier

Description

IntrovertIMApp.fla

ou

IntrovertIMApp.mxml

Le fichier d’application principal pour Flash (FLA) ou Flex (MXML)

com/example/programmingas3/introvertIM/IMManager.as

Classe établissant et gérant les communications entre ActionScript et le conteneur.

com/example/programmingas3/introvertIM/IMMessageEvent.as

Type d’événement personnalisé distribué par la classe IMManager à la réception d’un message du conteneur.

com/example/programmingas3/introvertIM/IMStatus.as

Enumération dont les valeurs représentent les différents statuts de disponibilité pouvant être sélectionnés dans l’application.

html-flash/IntrovertIMApp.html

ou

html-template/index.template.html

La page HTML pour l’application pour Flash (html-flash/IntrovertIMApp.html) ou le modèle utilisé pour créer la page HTML du conteneur pour l’application pour Adobe Flex (html-template/index.template.html). Ce fichier contient toutes les fonctions JavaScript qui constitue le conteneur de l’application.

Préparation de la communication entre ActionScript et le navigateur

L’API externe est le plus souvent utilisée pour permettre aux applications ActionScript de communiquer avec le navigateur Web. Grâce à elle, les méthodes ActionScript peuvent appeler du code écrit dans JavaScript, et inversement. En raison de la complexité des navigateurs et de leurs processus internes de rendu des pages, il est impossible de garantir qu’un document SWF pourra enregistrer ses rappels avant l’exécution du premier code JavaScript de la page HTML. Par conséquent, avant d’appeler les fonctions du document SWF à partir de JavaScript, le document SWF doit toujours appeler la page HTML pour lui indiquer qu’il est prêt à accepter des connexions.

Par exemple, grâce à une série d’étapes effectuées par la classe IMManager, Introvert IM détermine si le navigateur est prêt à communiquer et prépare le fichier SWF à la communication. La première étape, qui vérifie que le navigateur est prêt à communiquer, a lieu dans le constructeur IMManager, comme suit :

public function IMManager(initialStatus:IMStatus) 
{ 
    _status = initialStatus; 
 
    // Check if the container is able to use the external API. 
    if (ExternalInterface.available) 
    { 
        try 
        { 
            // This calls the isContainerReady() method, which in turn calls 
            // the container to see if Flash Player has loaded and the container 
            // is ready to receive calls from the SWF. 
            var containerReady:Boolean = isContainerReady(); 
            if (containerReady) 
            { 
                // If the container is ready, register the SWF's functions. 
                setupCallbacks(); 
            } 
            else 
            { 
                // If the container is not ready, set up a Timer to call the 
                // container at 100ms intervals. Once the container responds that 
                // it's ready, the timer will be stopped. 
                var readyTimer:Timer = new Timer(100); 
                readyTimer.addEventListener(TimerEvent.TIMER, timerHandler); 
                readyTimer.start(); 
            } 
        } 
        ... 
    } 
    else 
    { 
        trace("External interface is not available for this container."); 
    } 
}

Tout d’abord, le code vérifie si l’API externe est disponible dans le conteneur actuel à l’aide de la propriété ExternalInterface.available. Si c’est le cas, le processus de mise en place de la communication commence. Pour parer aux éventuelles exceptions de sécurité et autres erreurs qui peuvent se produire pendant les communications avec une application externe, le code est enveloppé dans un bloc try (les blocs catch correspondants ont été omis de l’exemple pour plus de concision).

Le code appelle ensuite la méthode isContainerReady(), présentée ici :

private function isContainerReady():Boolean 
{ 
    var result:Boolean = ExternalInterface.call("isReady"); 
    return result; 
}

La méthode isContainerReady() utilise à son tour la méthode ExternalInterface.call() pour appeler la fonction JavaScript isReady(), comme suit :

<script language="JavaScript"> 
<!-- 
// ------- Private vars ------- 
var jsReady = false; 
... 
// ------- functions called by ActionScript ------- 
// called to check if the page has initialized and JavaScript is available 
function isReady() 
{ 
    return jsReady; 
} 
... 
// called by the onload event of the <body> tag 
function pageInit() 
{ 
    // Record that JavaScript is ready to go. 
    jsReady = true; 
} 
... 
//--> 
</script>

La fonction isReady() renvoie simplement la valeur de la variable jsReady. Cette variable a au départ la valeur false. Une fois que l’événement onload de la page Web est déclenché, la valeur de la variable devient true. En d’autres termes, si ActionScript appelle la fonction isReady() avant que la page soit chargée, JavaScript renvoie la valeur false à ExternalInterface.call("isReady"), à la suite de quoi la méthode ActionScript isContainerReady() renvoie la valeur false. Une fois la page chargée, la fonction JavaScript isReady() renvoie la valeur true, donc la méthode ActionScript isContainerReady() renvoie aussi la valeur true.

Dans le constructeur IMManager, deux solutions sont possibles en fonction de la disponibilité du conteneur. Si isContainerReady() renvoie la valeur true, le code appelle simplement la méthode setupCallbacks(), qui achève la mise en place de la communication avec JavaScript. Dans l’autre cas, si isContainerReady() renvoie la valeur false, le processus est pratiquement mis en attente. Un objet Timer est créé pour appeler la méthode timerHandler() toutes les 100 millisecondes, comme suit :

private function timerHandler(event:TimerEvent):void 
{ 
    // Check if the container is now ready. 
    var isReady:Boolean = isContainerReady(); 
    if (isReady) 
    { 
        // If the container has become ready, we don't need to check anymore, 
        // so stop the timer. 
        Timer(event.target).stop(); 
        // Set up the ActionScript methods that will be available to be 
        // called by the container. 
        setupCallbacks(); 
    } 
}

Chaque fois que la méthode timerHandler() est appelée, elle vérifie à nouveau le résultat de la méthode isContainerReady(). Lorsque le conteneur est initialisé, la méthode renvoie la valeur true. Le code arrête alors le minuteur et appelle la méthode setupCallbacks() pour finir la mise en place de la communication avec le navigateur.

Présentation des méthodes ActionScript à JavaScript

Comme le montre l’exemple précédent, une fois que le code établit que le navigateur est prêt, la méthode setupCallbacks() est appelée. Cette méthode prépare ActionScript pour recevoir des appels à partir de JavaScript, comme le montre cet exemple :

private function setupCallbacks():void 
{ 
    // Register the SWF client functions with the container 
    ExternalInterface.addCallback("newMessage", newMessage); 
    ExternalInterface.addCallback("getStatus", getStatus); 
    // Notify the container that the SWF is ready to be called. 
    ExternalInterface.call("setSWFIsReady"); 
}

La méthode setCallBacks() achève la préparation de la communication avec le conteneur en appelant ExternalInterface.addCallback() afin d’enregistrer deux méthodes qui pourront être appelées par JavaScript. Dans ce code, le premier paramètre (le nom qui sert à désigner la méthode dans JavaScript, soit "newMessage" et "getStatus") est identique au nom de la méthode dans ActionScript (dans ce cas, il n’y avait pas d’intérêt à utiliser des noms différents, on a donc réutilisé les mêmes noms par souci de simplification). Enfin, la méthode ExternalInterface.call() est utilisée pour appeler la fonction JavaScript setSWFIsReady(), qui avertit le conteneur que les fonctions ActionScript ont été enregistrées.

Communication d’ActionScript vers le navigateur

L’application Introvert IM met en évidence plusieurs exemples d’appel de fonctions JavaScript dans la page conteneur. Dans le cas le plus simple (un exemple issu de la méthode setupCallbacks()), la fonction JavaScript setSWFIsReady() est appelée sans transmettre de paramètres ni recevoir de valeur en retour :

ExternalInterface.call("setSWFIsReady");

Dans un autre exemple issu de la méthode isContainerReady(), ActionScript appelle la fonction isReady() et reçoit une valeur booléenne en réponse :

var result:Boolean = ExternalInterface.call("isReady");

Vous pouvez également transmettre des paramètres aux fonctions JavaScript à l’aide de l’API externe. Considérez par exemple la méthode sendMessage() de la classe IMManager, qui est appelée lorsque l’utilisateur envoie un nouveau message à son interlocuteur.

public function sendMessage(message:String):void 
{ 
    ExternalInterface.call("newMessage", message); 
}

Là encore, ExternalInterface.call() sert à appeler la fonction JavaScript désignée pour avertir le navigateur du nouveau message. En outre, le message lui-même est transmis comme paramètre supplémentaire à ExternalInterface.call(). Il est ensuite transmis comme paramètre à la fonction JavaScript newMessage().

Appel du code ActionScript à partir de JavaScript

Une communication se fait normalement de manière bidirectionnelle, ce que respecte l’application Introvert IM. D’un côté, le client de messagerie Flash Player appelle JavaScript pour envoyer des messages, de l’autre, le formulaire HTML appelle le code JavaScript pour envoyer des messages au fichier SWF et recevoir de celui-ci des informations. Par exemple, lorsque le fichier SWF avertit le conteneur qu’il a établi le contact et peut communiquer, la première action du navigateur consiste à appeler la méthode getStatus() de la classe IMManager pour recevoir du client de messagerie SWF le statut de disponibilité de l’utilisateur initial. Cela se fait dans la page Web, avec la fonction updateStatus(), comme illustré ci-après :

<script language="JavaScript"> 
... 
function updateStatus() 
{ 
    if (swfReady) 
    { 
        var currentStatus = getSWF("IntrovertIMApp").getStatus(); 
        document.forms["imForm"].status.value = currentStatus; 
    } 
} 
... 
</script>

Le code vérifie la valeur de la variable swfReady, qui vérifie si le fichier SWF a averti le navigateur qu’il avait enregistré ses méthodes auprès de la classe ExternalInterface. Si le fichier SWF est prêt à recevoir la communication, la ligne suivante (var currentStatus = ...) appelle la méthode getStatus() dans la classe IMManager. Trois opérations se produisent dans cette ligne de code :

  • La fonction JavaScript getSWF() est appelée et renvoie une référence à l’objet JavaScript représentant le fichier SWF. Le paramètre transmis à getSWF() détermine si l’objet de navigateur est renvoyé, au cas où il y aurait plus d’un fichier SWF dans la page HTML. La valeur transmise à ce paramètre doit correspondre à l’attribut id de la balise object et à l’attribut name de la balise embed, toutes deux utilisées pour inclure le fichier SWF.

  • Avec la référence au fichier SWF, la méthode getStatus() est appelée comme s’il s’agissait d’une méthode de l’objet SWF. Dans ce cas, le nom de fonction « getStatus » est utilisé, car c’est le nom sous lequel la fonction ActionScript a été enregistrée à l’aide de ExternalInterface.addCallback().

  • La méthode ActionScript getStatus() renvoie une valeur qui est attribuée à la variable currentStatus, laquelle devient ensuite le contenu (la valeur value) du champ de texte status.

Remarque : si vous vous référez au code, vous avez probablement noté que dans le code source relatif à la fonction updateStatus(), la ligne qui appelle la fonction getSWF() est en fait écrite comme suit : var currentStatus = getSWF("${application}").getStatus(). Le texte ${application} est un espace réservé dans le modèle de page HTML. Lorsqu’Adobe Flash Builder génère la page HTML en tant que telle pour l’application, cet espace réservé est remplacé par le texte faisant office d’attribut id de la balise object et d’attribut name de la balise embed (soit IntrovertIMApp dans l’exemple). Il s’agit de la valeur attendue par la fonction getSWF().

La fonction JavaScript sendMessage() illustre la transmission d’un paramètre à la fonction ActionScript (sendMessage() est la fonction appelée lorsque l’utilisateur appuie sur le bouton Envoyer de la page HTML).

<script language="JavaScript"> 
... 
function sendMessage(message) 
{ 
    if (swfReady) 
    { 
        ... 
        getSWF("IntrovertIMApp").newMessage(message); 
    } 
} 
... 
</script>

La méthode ActionScript newMessage() attend un seul paramètre. De ce fait, la variable JavaScript message est transmise à ActionScript en l’utilisant comme paramètre dans l’appel de la méthode newMessage() du code JavaScript.

Détection du type de navigateur

L’accès au contenu varie d’un navigateur à l’autre. Pour cette raison, il est important de toujours utiliser JavaScript pour détecter le navigateur utilisé et accéder ensuite à la séquence selon la syntaxe propre au navigateur à l’aide de l’objet de fenêtre ou de document, comme le montre la fonction JavaScript getSWF() de cet exemple :

<script language="JavaScript"> 
... 
function getSWF(movieName) 
{ 
    if (navigator.appName.indexOf("Microsoft") != -1) 
    { 
        return window[movieName]; 
    } 
    else 
    { 
        return document[movieName]; 
    } 
} 
... 
</script>

Si votre script ne détecte pas le type de navigateur de l’utilisateur, ce dernier peut noter un comportement inattendu lors de la lecture des fichiers SWF dans un conteneur HTML.