Rich Internet Applications for SLA Research/FlashComm Database Interaction Flash

Creating the Flash Movie

edit

Setting Up The Timeline

edit

In Flash MX 2004, create a timeline with the following layers, keyframes, and labels.

File:AMFPHPChat-timeline-setup.png

Note that the ActionScript for this movie will be placed in the CSAS layer.

Creating the User Interface

edit

Login Keyframe

edit

Add user interface elements to the UI layer so that your movie resembles this image.

File:AMFPHPChat-login-frame-UI.png

Name the interface elements as follows:

  • user name text area: username_txt
  • password text area: password_txt
  • Enter button: enter_pb
  • bottom text area: output_txt

Wait Keyframe

edit

Add a line of text as in this image.

File:AMFPHPChat-wait-frame-UI.png

Chat Researcher Keyframe

edit

Add user interface elements to the UI layer so that your movie resembles this image.

File:AMFPHPChat-chat researcher-frame-UI.png

Name the interface elements as follows:

  • AudioConference communication component: audioChat_mc
  • SetBandwidth communication component: bw_mc
  • Ring Bell button: bell_pb
  • UserColor communication component: userColor_mc
  • large text area: chat
  • single line text area: chatIn
  • Send button: chat_bt

Set the click handlers for the following buttons:

  • Save Text button: writeSSText
  • End Session button: endSession
  • Ring Bell button: dingDong
  • Send button: addText

Chat Subject Keyframe

edit

Follow the instructions for the Chat Researcher keyframe, with the exception of the Save Text and End Session buttons, which should be omitted. The movie should resemble this image.

File:AMFPHPChat-chat subject-frame-UI.png

Adding the ActionScript

edit

The following ActionScript code should be added to each of the indicated keyframes. It should be placed on the CSAS layer.

Login Keyframe

edit
// AMFPHPChat - CSAS layer - login keyframe
//
//Needed classes
#include "NetServices.as"
//just for debugging purposes
#include "NetDebug.as"
#include "synchAndStatusHandler.as"
//
Microphone.prototype.onStatus = onStatusTemplate;
NetConnection.prototype.onStatus = onStatusTemplate;
NetStream.prototype.onStatus = onStatusTemplate;
SharedObject.prototype.onStatus = onStatusTemplate;
//
var appName:String = "AMFPHPChat";
//
// *************************************************************
// Netservices set-up: This creates a Flash Remoting connection 
// to the AMFPHP-enabled web server (e.g., Apache) and allows 
// the .swf to call a function in a PHP script, thereby working 
// around the crossdomain restrictions imposed on loadVars objects.
//
// http://www.amfphp.org for more information on the technology.
// *************************************************************
NetServices.setDefaultGatewayUrl("http://DOMAINNAME/cgi-bin/" + appName + "/gateway.php");
//
// Then, create the connection object without passing a response object parameter 
// to the method. The response object will be specified when the component's function
// is called via the service object, myService, defined below.
AMFPHPChatConnection = NetServices.createGatewayConnection();
//
// Result handler for the AMFPHP call "AMFPHPService.validateUser"
// in validateUserAMF(), father below
loginAMFRequest = new Object(); 
loginAMFRequest.onResult = function(loginResult){
  // TODO: AMFPHP service needs to return value that
  // distinguishes between error/incorrect login as well as
  // researcher and subject logins
  trace("loginResult: " + loginResult);
  output_txt.text += loginResult + "\n";
  Key.removeListener(loginKeyListener);
  gotoAndStop("wait");
}
//Create the service used to access PHP code via AMFPHP
AMFPHPService = AMFPHPChatConnection.getService("DBServices");
// instantiate generic object to serve as key listener
loginKeyListener = new Object();
// 
loginKeyListener.onKeyDown = function() {
  if(Key.getCode() == Key.ENTER) {
    // validate user via function that
    // queries AMFPHP script
    validateUserAMF();
  };
};
//
Key.addListener(loginKeyListener);
// validate user via function that
// queries AMFPHP script
this.enter_pb.onRelease = function() {
  validateUserAMF();
};
// TO DO: Callback code (a la that in SSAS) needed
// to time out the validateUserAMF call to AMFPHP gateway
validateUserAMF = function () {
  trace("validating user");
  userIDValue = username_txt.text;
  trace("userIDValue: " + userIDValue);
  passwordValue = password_txt.text;
  trace("passwordValue: " + passwordValue);
  AMFPHPService.validateUser(loginAMFRequest, username_txt.text, password_txt.text);
};
//
stop();

Wait Keyframe

edit
// AMFPHPChat_03 - CSAS layer - wait keyframe
// VERSION DATE: 17APR2005
//
#include "NetDebug.as"
#include "synchAndStatusHandler.as"
load("components.asc");
// set globals and constants
RING_VOLUME = 30;
SESSION_MAX_LENGTH = 60;
// generic data object
_global.session = new Object();
// session.username from log-in input
_global.session.username = username;
_global.session.pwd = password;
trace("username: " + session.username);
trace("password: " + session.pwd);
// NetConnection code
// session.uri sets path to server-side application
_global.session.uri = "rtmp://localhost/" + appName + "/instance01";
trace("_global.session.uri: " + _global.session.uri);
//
// create connection object for components and shared objects
chat_nc = new NetConnection();
//
// connect connection object with FlashCom server
chat_nc.connect(session.uri, session.username);
// Set maximum scroll of chat textarea
chat.maxscroll = 1000;
trace("chat.maxscroll: " + chat.maxscroll);
//
// create remote (server-side) shared object: textchat
//
// textchat_so methods should be defined before connecting
// shared object with FlashCom server
AMFPHPChat_so = SharedObject.getRemote("AMFPHPChat_so", _global.session.uri, false);
AMFPHPChat_so.onSync = onSyncTemplate;
//
// AMFPHPChat_so.enterChat
// Function is to be called when set number of users
// have logged in and are waiting at the "wait" frame.
AMFPHPChat_so.enterChat = function() {
  trace("in enterChat");
  gotoAndStop("chat_researcher");
  // this code will have to be changed to decide whether to go
  // to the researcher interface or the subject interface
};
//
// connect shared object with FlashCom server
trace("wait; chat_nc: " + chat_nc);
AMFPHPChat_so.connect(chat_nc);
//
// Let FlashCom know app is waiting
chat_nc.call("waiting", null);
//
stop();

Chat Researcher Keyframe

edit
// AMFPHPChat - CSAS layer - chat_researcher keyframe
//
writeSSText = function() {
  trace("writeSSText CSAS function, calling writeTextToDB on server");
  chat_nc.call("saveTextToDB", null);
};
//
endSession = function() {
  trace("endSession called");
  chat_nc.call("destroySession", null);
};
//
addText = function() {
  //trace("in addText - length: " + length(chatIn.text));
  if(length(chatIn.text) > 0) {
    // call clientChatMessage on server
    trace("calling clientChatMessage on server");
    userColor = new String(gFlashCom.userprefs.color);
    userColor = "#" + userColor.substr(2);
    trace("addText() - userColor: " + userColor);
    chat_nc.call("clientChatMessage", null, chatIn.text, userColor);
    // clear input line text
    chatIn.text = "";
    // maintain focus on input line
    Selection.setFocus("chatIn");
  };
};
//
// serverChatMessage function; called from SSAS (main.asc)
AMFPHPChat_so.serverChatMessage = function(msg) {
  trace("in serverChatMessage - msg: " + msg);
  chat.htmlText += msg;
  chat.scroll = chat.maxscroll;
};
//
// Chat updates on the server with msg upon connection.
// Chat will be blank for first user; subsequent users will get
// latest chat.maxscroll units of any chat text preceding their
// connecting. Chat text is cleared when user count reaches zero.
// Called from SSAS (main.asc)
AMFPHPChat_so.startChat = function(msg) {
  trace("in startChat - msg: " + msg);
  chat.htmlText = msg;
  // connect FCS UI components
  app_init();
};
//
// THIS NEEDS FIXING! As of 4-3-05, a user who rejoins the chat
// has her text color set to that of the other user. Perhaps persistent
// shared object needed?
//
// instantiate generic object to serve as color change listener
colorListener = new Object();
//
colorListener.onColorChange = function(){
  colorListener.color = gFlashCom.userprefs.color;
  trace("colorListener.color: " + gFlashCom.userprefs.color);
};
//
gFlashCom.userprefs.addListener(colorListener);
//
// instantiate generic object to serve as key listener
keyListener = new Object();
//
// define onKeyDown to trigger check for enter key
keyListener.onKeyDown = function() {
  if(Key.getCode() == Key.ENTER) {
    addText();
  };
};
//
Key.addListener(keyListener);
//
// create chat_doorbell sound
chat_doorbell = new Sound(this);
chat_doorbell.attachSound("doorbell.wav");
chat_doorbell.setVolume(RING_VOLUME);
chat_doorbell.stop();
trace("chat_doorbell: " + chat_doorbell);
//
// ring doorbell; calls SSAS, which then calls
// AMFPHPChat_so.clientRing on each client
dingDong = function() {
  trace("Ding Dong!");
  chat_nc.call("doorbellRing", null);
};
//
AMFPHPChat_so.clientRing = function() {
  trace("in clientRing");
  chat_doorbell.start();
};
//
app_init = function() {
  // connect UI component(s)
  //trace("bandwidth_mc.connect");
  trace("app_init; session.username: " + session.username);
  // userColor_mc.setUsername(session.username);
  userColor_mc.connect(chat_nc);
  bw_mc.setUsername(session.username);
  bw_mc.connect(chat_nc);
  audioChat_mc.setUsername(session.username);
  audioChat_mc.connect(chat_nc);
  Selection.setFocus("chatIn");
  historyScroll.setScrollTarget(chat);
  historyScroll.setScrollPosition(chat.maxscroll);
};
//
app_close = function() {
  //trace("app_close: closing...");
  chat.text = "";
};
//
// tell server-side scripts that client is ready
chat_nc.call("ready", null, sessionID);
//
stop();

Chat Subject Keyframe

edit

NOTE: The chat_subject keyframe can use the same code as the chat_researcher keyframe because the text-writing and session-ending code cannot be accessed without the UI buttons in the chat_researcher keyframe.

Synchronization and Status Handler Code

edit

NOTE: The following code should be placed in a file named synchAndStatusHandler.as. This code is useful for debugging in the Flash MX 2004 authoring environment. It reports all synchronization messages (e.g., successes and errors) and status messages. Code could be added to respond to specific events such as errors in connecting or unexpected disconnections.

// AMFPHPChat
// *TEMPLATE* synchAndStatusHandler - CSAS/SSAS - "#include" file
//
onSyncTemplate = function (info) {
  trace("Data Synchronizing");
  // 
  // This structure may be used as a prototype for onSync handlers.
  //
  // First, loop through the array of objects with the IN operator
  // 
  for (name in info) {
    trace("[sync] Array Object #"+name+"  code("+info[name].code+","+info[name].name+")");
    // 
    // ::: switch to handle code values returned by the object
    // Place Custom Code Here
    // 
    switch (info[name].code) {
      case "change" :
        trace("::change of data by another client");
        // Place Custom Code Here
        //
        break;
      case "success" :
        trace("::successful change of data from this server");
        // Place Code Here
        //
        break;
      case "reject" :
        trace("::rejected SharedObject write operation");
        // Place Code Here
        //
        break;
      case "clear" :
        trace("::initialization of the SharedObject");
        // Place Code Here
        //
        break;
      case "delete" :
        trace("::deleted Attributes");
        // Place Code Here
    }
    // ::: End the switch, continue looping through the information object
  }
  // ::: End of onSync handler
};
//
function onStatusTemplate(info) {
  infoMessage_array = info.code.split(".");
  trace(" ** status message received for: "+infoMessage_array[0]+" **");
  trace(" ** "+new Date());
  trace("    |::: Level--> "+info.level);
  trace("    |::: Code --> "+info.code);
  //
  // Trace information properties 
  if (info.description != undefined) {
    trace("    |::: Description--> "+info.description);
  }
  if (info.details != undefined) {
    trace("    |::: Details--> "+info.details);
  }
  if (info.application != undefined) {
    trace("    |::: Application--> "+info.application);
  }
  switch (infoMessage_array[0]) {
    case "Application" :
      trace("    | ::::Application Messages");
      // Display script error messages
      if (infoMessage_array[1] == "script") {
        trace("    |::: Script Error Details.  Filename--> "+info.filename+"   Line--> "+info.lineno);
        trace(info.linebuf);
      }
      // Place Code Here
      break;
    case "Camera" :
      //   | :::: Handle Camera Messages
      // Place Code Here
      break;
    case "Microphone" :
      //    | :::: Handle Microphone Messages
      // Place Code Here
      break;
    case "NetConnection" :
      //    | ::: Handle NetConnection Messages
      if (infoMessage_array[2] == "Success") {
        trace("    |::* Connection accepted by server; continuing to load.");
        // Place (below) scripts to run when the connection has succeeded
      }
      if (infoMessage_array[2] == "Closed") {
        trace("    |::* Connection was closed; returning the user!");
        app_close();
      }
      // Place Code Here
      break;
    case "NetStream" :
      //    | :::: Handle NetStream Messages
      // Place Code Here
  }
  trace(" ** End Status Message for: "+infoMessage_array[0]+" **");
}

NEXT: FlashComm Database Interaction: FlashComm