Rich Internet Applications for SLA Research/FlashComm Database Interaction Flash

= Creating the Flash Movie =

Setting Up The Timeline
In Flash MX 2004, create a timeline with the following layers, keyframes, and labels. Note that the ActionScript for this movie will be placed in the CSAS layer.

Login Keyframe
Add user interface elements to the UI layer so that your movie resembles this image. 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
Add a line of text as in this image.

Chat Researcher Keyframe
Add user interface elements to the UI layer so that your movie resembles this image. 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
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.

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

Login Keyframe
// AMFPHPChat - CSAS layer - login keyframe // //Needed classes //just for debugging purposes // 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;
 * 1) include "NetServices.as"
 * 1) include "NetDebug.as"
 * 2) include "synchAndStatusHandler.as"

Wait Keyframe
// AMFPHPChat_03 - CSAS layer - wait keyframe // VERSION DATE: 17APR2005 // 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;
 * 1) include "NetDebug.as"
 * 2) include "synchAndStatusHandler.as"

Chat Researcher Keyframe
// 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
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
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