DekGenius.com
[ Team LiB ] Previous Section Next Section

26.2 Developing the MP3 Selectors

In the following sections, we create both the local and server MP3 selectors. These are the parts of the jukebox application that enable a user to select MP3s from both her own hard drive and from the server by using a graphical user interface instead of having to type in a URL or path to the file.

26.2.1 Creating a Local MP3 Selector

For a user to be able to add an MP3 to her jukebox playlist from her local hard drive, she needs to know the path to that file. This process can be simplified for the user if we can provide her with a way of browsing her hard drive graphically and selecting a file with her mouse. This is the idea behind the local MP3 selector feature.

HTML allows you to create forms with elements of type "file". File form fields include a Browse button that opens a dialog box so that users can select files from their local hard drives. Flash does not natively support a local browse feature; however, we can devise a workaround using HTML, JavaScript, and a Flash movie with a local connection. There are three files necessary for the local MP3 selector functionality—two HTML pages and one Flash movie. Let's look at each of these files individually.

26.2.1.1 Making the Form page

We will use a standard HTML file form field in a small pop-up browser window to allow the user to select a file from her hard drive. Create a new HTML document named localFileForm.html and add the following code to it:

<!-- Create a form (must be multipart/form-data in order for the file field 
      type to work properly). The form should submit to submitFileForm.html 
      (an HTML page we'll create next) using the GET method. -->
<form enctype="multipart/form-data" method="get" action="submitFileForm.html">

<!-- Create a file input field named path. -->
<input type="file" name="path">
<br>

<!-- Create a Submit button. -->
<input type="submit">
</form>

As you can see, localFileForm.html is not very complicated. If you test the page in a web browser, you can see that a Browse button is created automatically as part of the file input field. Clicking the Browse button opens a dialog box that allows you to select a local file.

26.2.1.2 Making the Submit page

Once a user has selected a file using the localFileForm.html page and clicked the Submit button, the form data is sent to another HTML page named submitFileForm.html. We want this HTML page to take the form data (specifically, the value from the file field) and pass it to a Flash movie, which we will create next. You can use the FLASHVARS attribute of the <OBJECT> and <EMBED> tags to pass a value to the movie loaded into the HTML page. Normally, if the values you want to pass to a Flash movie are static, you can hardcode them into the HTML code, as shown in this example (FLASHVARS attributes are shown in bold):

<OBJECT classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/
swflash.cab#version=6,0,0,0"
        WIDTH="550" HEIGHT="400" id="myMovie" ALIGN="">
  <PARAM NAME=movie VALUE="myMovie.swf">
  <PARAM NAME=quality VALUE=high>
  <PARAM NAME=bgcolor VALUE=#FFFFFF>
  <PARAM NAME=FLASHVARS VALUE="param1=value1&param2=value2">
  <EMBED src="myMovie.swf" quality=high bgcolor=#FFFFFF  
         WIDTH="550" HEIGHT="400" NAME="myMovie" ALIGN=""
         TYPE="application/x-shockwave-flash" 
         PLUGINSPAGE="http://www.macromedia.com/go/getflashplayer"
         FLASHVARS ="param1=value1&param2=value2">
  </EMBED>
</OBJECT>

However, we want to obtain the file value dynamically from the HTML form. One option is to use JavaScript to extract the value from the URL—the form values are appended to the URL since we used the GET method to submit the form—and then use the JavaScript write( ) method to generate the <OBJECT> and <EMBED> tags within the HTML page. This solution offers several advantages. First of all, because JavaScript is run on the client and understood by almost all web browsers, you don't have to worry about any server-side language. Furthermore, JavaScript shares the same syntax and many core classes with ActionScript, so it is approachable for most ActionScript developers.

In the following code block, you can see the HTML and JavaScript code that extracts the form value from the URL and passes it a Flash movie using the FLASHVARS attribute. Add this code to an HTML document named submitFileForm.html and save it to the same directory as localFileForm.html.

<HTML>
<HEAD>
<TITLE>Get Path</TITLE>
</HEAD>
<BODY bgColor=#FFFFFF leftmargin="0" marginheight="0" marginwidth="0" topmargin="0">
<SCRIPT language=JavaScript>
<!--

var path = location.search.split("=")[1];  
var swfCode = "<OBJECT classid=\'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000\' ";
swfCode += " codebase=\'http://download.macromedia.com/pub/shockwave/cabs/flash/
swflash.cab#version=6,0,0,0\'";
swfCode += " WIDTH=300 HEIGHT=150>";
swfCode += " <PARAM NAME=movie  VALUE='pathUploader.swf'>";
swfCode += " <PARAM NAME=quality VALUE=best> ";
swfCode += " <PARAM NAME=FlashVars VALUE='path=" + path + "'>";
swfCode += " <EMBED src='pathUploader.swf'";
swfCode += " FLASHVARS  ='path=" + path + "'";
swfCode += " quality=best WIDTH=300 HEIGHT=150 ";
swfCode += " TYPE=\'application/x-shockwave-flash\' ";
swfCode += "PLUGINSPAGE=\'http://www.macromedia.com/shockwave/download/
index.cgi?P1_Prod_Version=ShockwaveFlash\'> ";
swfCode += " </EMBED> </OBJECT> ";

document.write(swfCode);
//-->
</SCRIPT>

</BODY>
</HTML>

Let's take a closer look at some of the JavaScript code. First of all, we want to get the value that was submitted by the form. In JavaScript the query string portion of the URL (everything following the ?) can be referenced by location.search. In this case, the value of location.search is of the form path=userSelectedPath. You can use the split( ) method to split the value into an array using the equals sign as the delimiter; the chosen file path is stored in the array's second element. Hence, we use:

var path = location.search.split("=")[1];

Next, we want to construct the <OBJECT> and <EMBED> tags. Most of the string defining the tags is hardcoded to contain the necessary attributes. The name of our Flash movie, pathUploader.swf, is specified as the movie parameter for the <OBJECT> tag and the src attribute for the <EMBED> tag. Notice that the FLASHVARS attribute is set to include the file path obtained from the form.

var swfCode = "<OBJECT classid=\'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000\' ";
swfCode += " codebase=\'http://download.macromedia.com/pub/shockwave/cabs/flash/
swflash.cab#version=6,0,0,0\'";
swfCode += " WIDTH=300 HEIGHT=150>";
swfCode += " <PARAM NAME=movie  VALUE='pathUploader.swf'>";
swfCode += " <PARAM NAME=quality VALUE=best> ";
swfCode += " <PARAM NAME=FLASHVARS  VALUE='path=" + path + "'>";
swfCode += " <EMBED src='pathUploader.swf'";
swfCode += " FLASHVARS ='path=" + path + "'";
swfCode += " quality=best WIDTH=300 HEIGHT=150 ";
swfCode += " TYPE=\'application/x-shockwave-flash\' ";
swfCode += "PLUGINSPAGE=\'http://www.macromedia.com/shockwave/download/
index.cgi?P1_Prod_Version=ShockwaveFlash\'> ";
swfCode += " </EMBED> </OBJECT> ";

Once the string is constructed, we use the document.write( ) method to output the value in the HTML page.

document.write(swfCode);
26.2.1.3 Making the local connection Flash movie

To finish the local MP3 selector, we must create a Flash movie that takes the value passed to it via FLASHVARS and sends it to the main jukebox movie. You can enable movie-to-movie communication with two Flash movies running on the same computer using a LocalConnection object.

We'll name our Flash movie pathUploader.swf, as per the movie and src attributes in the preceding submitFileForm.html page. This movie should do the following:

  • Send the local MP3 file path to the main jukebox movie using a local connection

  • Display the selected MP3 file path to the user

  • Include a button that allows the user to close the pop-up window

Here are the steps involved in creating this Flash movie:

  1. Create a new Flash document named pathUploader.fla in the same directory as the HTML files you have already created.

  2. Adjust the document settings (under Modify Document) so that the movie has dimensions of 300 x 150 pixels, which are the dimensions of the browser window that we will open for displaying this movie.

  3. Add the PushButton component to the document's Library by dragging an instance from the Components panel onto the Stage and then deleting the instance. (The symbol is copied to the Library.)

  4. Add the following code to the first frame of the default layer of the main timeline:

    // Include MovieClip.as from Chapter 7 and TextField.as from Chapter 8.
    #include "MovieClip.as"
    #include "TextField.as"
    
    // Create a text field to display the path.
    _root.createTextFieldAuto("pathUploadConfirm", _root.getNewDepth(  ));
    pathUploadConfirm.multiline = true;
    pathUploadConfirm.text = "the file: \n" + path + 
                             "\n has been added to the playlist";
    
    // Format the text so it is centered.
    tf = new TextFormat(  );
    tf.align = "center";
    pathUploadConfirm.setTextFormat(tf);
    pathUploadConfirm._y = Stage.height/2 - pathUploadConfirm._height/2;
    pathUploadConfirm._x = Stage.width/2  - pathUploadConfirm._width/2;
    
    // Create a Close button so the user can close the pop-up window.
    _root.attachMovie("FPushButtonSymbol", "closeBtn", _root.getNewDepth(  ));
    closeBtn._y = pathUploadConfirm._y + pathUploadConfirm._height + 5;
    closeBtn._x = Stage.width/2 - closeBtn._width/2;
    closeBtn.setLabel("close window");
    
    // When the Close button is released, call the window.close(  ) method for the
    // HTML page using the getURL("javascript: void(  );") technique.
    closeBtn.onRelease = function (  ) {
      this.getURL("javascript: void(window.close(  ));");
    };
    
    // Create a local connection and send the MP3 path over a connection named
    // "pathSendConnection". The listening movie (the main jukebox movie) needs to
    // have a receivePathInfo(  ) method to receive this value.
    sender = new LocalConnection(  );
    sender.send("pathSendConnection", "receivePathInfo", path);
  5. Save the document and export the movie as pathUploader.swf.

26.2.2 Creating the Server MP3 Selector

In the preceding section, we developed a mechanism by which the user can select an MP3 from her local hard drive. In this section, we create a similar mechanism that allows the user to select an MP3 from a server. This portion of the application requires the use of Flash Remoting. Moreover, the example shown here uses ColdFusion to browse the contents of the server directory. However, this feature is not essential to the overall functioning of the application, and if you do not use Flash Remoting or ColdFusion, you can skip over this portion.

The server MP3 selector involves three basic parts:

  • A ColdFusion Component (CFC) that retrieves directory and file information and returns it to the Flash movie that calls it

  • A Flash movie that calls the CFC function via Flash Remoting and displays the results in a Tree component

  • An HTML page for the Flash movie

The server MP3 selector opens up in a new browser window, as does the local MP3 selector. The server MP3 selector should allow a user to browse the directory in which the CFC is saved, as well as any of the subdirectories. The user can select a file and add it to the playlist in the main jukebox movie by way of a local connection.

26.2.2.1 Making the CFC

The first step in creating the server MP3 selector is to make the CFC that retrieves the directory and file information. The CFC needs only one method, which takes a parameter specifying the name of the directory about which to return information. The method returns an object with the information about that directory, including the directory name and the files and subdirectories it contains.

To make the CFC, do the following:

  1. Create a new file named DirectoryBrowser.cfc in the ColdFusion web root (for example, C:\CfusionMX\wwwroot).

  2. Add the following code to the CFC:

    <cfcomponent>
      <cffunction name="getDirectoryInfo" access="remote" returntype="struct">
    
        <!--- Tell the method to expect a string parameter named dir --->
        <cfargument name="dir" type="string" required="false">
    
        <cfscript>
          // Use the path of the CFC on the server as the default path in case the
          // dir parameter is undefined.
          getdir = #GetDirectoryFromPath(GetCurrentTemplatePath(  ))#;
          if (isDefined("arguments.dir")){
            getdir = arguments.dir;
          }
        </cfscript>
    
        <!--- Use the <cfdirectory> tag to get a directory listing for the specified
              path. Sort the directory contents first by type, then by name. --->
        <cfdirectory directory="#getdir#" name="dirListing" sort="type ASC, name ASC">
    
        <cfscript>
          // Create a Struct to return the directory information. Add to this a dir 
          // property that contains the value of the current directory as well as a
          // dirListing property that contains the results of <cfdirectory>.
          res = StructNew(  );
          res.dir = getdir;
          res.dirListing = dirListing;
    
          // Return the Struct.
          return res;
        </cfscript>
      </cffunction>
    </cfcomponent>
  3. Save the CFC.

In the first part of the CFC method, we determine the path to the directory for which we should get the information. The first time the method is called, we don't know the path to the directory, but on subsequent calls (i.e., calls to retrieve subdirectory information) we do. For this reason, the dir parameter should not be required. Also, the directory path value that we use (getdir) should default to the path to the directory containing the CFC, but if a parameter is passed to the CFC method, that value should be used instead.

<cfargument name="dir" type="string" required="false">
<cfscript>
  getdir = #GetDirectoryFromPath(GetCurrentTemplatePath(  ))#;
  if (isDefined("arguments.dir")){
    getdir = arguments.dir;
  }
</cfscript>

The <cfdirectory> tag returns an array of Structs that provide information about all the files and subdirectories within a given directory. Each of the Structs has both a type and a name property, in which type can be either "Dir" or "File" and name is the name of the file or subdirectory. We sort the results first by type and then by name, yielding a listing in which the subdirectories are grouped together and alphabetized, as are the files.

<cfdirectory directory="#getdir#" name="dirListing" sort="type ASC, name ASC">
26.2.2.2 Making the server MP3 selector Flash movie

The next part of the server MP3 selector is the Flash movie that interfaces with the CFC via Flash Remoting. The Flash movie is opened in its own pop-up browser window, and it displays the directory listing using a Tree component. Once the user selects a file, the path is sent to the main jukebox movie using a local connection, just as with the local MP3 selector.

Here are the steps to complete to make the MP3 selector Flash movie:

  1. Create a new Flash document named directoryBrowser.fla.

  2. Create an instance of the Tree component in the document's Library. You can do this by dragging an instance of the component from the Components panel onto the Stage and then deleting it. The Tree component is part of the Flash UI Components Set 2 available for free from the Flash Exchange (http://www.macromedia.com/exchange/flash).

    // Include NetServices.as for Flash Remoting support.
    #include "NetServices.as"
    
    // Include MovieClip.as from Chapter 7 and TextField.as from Chapter 8.
    #include "MovieClip.as"
    #include "TextField.as"
    // Include Forms.as and Table.as from Chapter 11.
    #include "Forms.as"
    #include "Table.as"
    
    function init (  ) {
    
      // Create a Flash Remoting connection. If necessary, adjust the example gateway
      // URL to match your server configuration.
      var gwURL = "http://localhost:8500/flashservices/gateway";
      NetServices.setDefaultGatewayURL(gwURL);
      var conn = NetServices.createGatewayConnection(  );
    
      // Create a service object that maps to the DirectoryBrowser CFC.
      dirBrowserSrvc = conn.getService("DirectoryBrowser");
    
      // Create a response object. When a result is returned to this response object,
      // call the addNodes(  ) function (defined next) with the result returned from
      // the service function.
      dirInfoRes = new Object(  );
      dirInfoRes.onResult = function (result) {
        _root.addNodes(result);
      };
    
      // Call the getDirectoryInfo(  ) service function to initialize the movie with
      // the contents of the ColdFusion web root (where the CFC is stored). Tell
      // Flash to send any responses to the dirInfoRes response object.
      dirBrowserSrvc.getDirectoryInfo(dirInfoRes);
    
      // Create a local connection for sending the path to the main jukebox movie.
      sender = new LocalConnection(  );
    
      // The currentNode variable is used to keep track of the tree node that is 
      // being viewed. Initially no node is being viewed, so set it to null.
      currentNode = null;
    }
    
    // Create the form in which the user can browse the directory and subdirectories
    // using a Tree component.
    function createTreeForm (  ) {
    
      // Create the Tree component instance as well as a text field to display the
      // name of the selected file and a Submit button.
      _root.attachMovie("FTreeSymbol", "tree", _root.getNewDepth(  ));
      _root.createInputTextField("path", _root.getNewDepth(  ), 0, 0, 150, 20);
      _root.attachMovie("FPushButtonSymbol", "submitBtn", _root.getNewDepth(  ));
    
      // When a node is selected in the Tree component, 
      // call the doSelect(  ) function.
      tree.setChangeHandler("doSelect");
    
      submitBtn.setClickHandler("submitSelected");
      submitBtn.setLabel("submit");
    
      // Create a table for positioning the tree, text field, and button.
      treeTr0 = new TableRow(5, new TableColumn(5, tree));
      treeTr1 = new TableRow(5, new TableColumn(5, path));
      treeTr2 = new TableRow(5, new TableColumn(5, submitBtn));
      treeTable = new Table(5, 0, 0, treeTr0, treeTr1, treeTr2);
    
      // Create a form and add the elements to the form.
      treeForm = new Form(  );
      treeForm.addElement(tree);
      treeForm.addElement(path);
      treeForm.addElement(submitBtn);
    }
    
    // Create a form that displays the path of the selected file and allows the user
    // to close the window with a button after the file has been submitted. This code
    // is almost identical to the code contained within pathUploader.fla.
    function createConfirmForm (  ) {
      _root.createAutoTextField("pathUploadConfirm", _root.getNewDepth(  ));
      pathUploadConfirm.multiline = true;
      pathUploadConfirm.text = "the file: \n\n\n has been added to the playlist";
      tf = new TextFormat(  );
      tf.align = "center";
      pathUploadConfirm.setNewTextFormat(tf);
      pathUploadConfirm._y = Stage.height/2 - pathUploadConfirm._height/2;
      pathUploadConfirm._x = Stage.width/2 - pathUploadConfirm._width/2;
      _root.attachMovie("FPushButtonSymbol", "closeBtn", _root.getNewDepth(  ));
      closeBtn._y = pathUploadConfirm._y + pathUploadConfirm._height + 5;
      closeBtn._x = Stage.width/2 - closeBtn._width/2;
      closeBtn.setLabel("close window");
      closeBtn.onRelease = function (  ) {
        this.getURL("javascript: void(window.close(  ));");
      };
    
      // Add the elements to a form.
      confirmForm = new Form(  );
      confirmForm.addElement(pathUploadConfirm);
      confirmForm.addElement(closeBtn);
    }
    
    // Create a multipage form so that treeForm and confirmForm can occupy the same
    // space and only one is visible at a time.
    function createMultiForm (  ) {
      myMPForm = new MultiPageForm(  );
      myMPForm.addForm(treeForm);
      myMPForm.addForm(confirmForm);
      myMPForm.setPage(1);
    }
    
    // This function is invoked automatically every time a value is returned from the
    // service function (and it is passed that value in dlInfo). 
    function addNodes(dlInfo) {
    
      // If currentNode is null, it was the first call to the service function.
      // Therefore, create a root node for the Tree component and populate it with
      // the data for the web root directory listing.
      if (currentNode == null) {
    
        // The dir property is the full path to the directory that is being browsed.
        // To get the name of that directory, split the path into an array using "\"
        // as a delimiter for Windows systems. Don't forget to escape the backslash.
        // If you are using a Unix-based server, use "/" as the delimiter instead.
        var dirNameAr = dlInfo.dir.split("\\");
    
        // The directory name is mostly like the last element of the array.
        var dirName = dirNameAr[dirNameAr.length - 1];
    
        // If the directory path has a trailing slash, the last element of the array
        // is an empty string, so the directory name is the second-to-last element.
        if (dirName == "") {
          dirName = dirNameAr[dirNameAr.length - 2];
        }
    
        // Create the root node.
        var node = new FTreeNode(  );
        node.setLabel(dirName);
        node.setData(dlInfo.dir);
        tree.setRootNode(node);
        currentNode = node;
      }
    
      // The currentNode is the one being browsed. Set a custom property for the
      // current node that contains the full path on the server of the directory the
      // node represents.
      currentNode.baseDir = dlInfo.dir;
    
      // Add a trailing slash to baseDir, if necessary, so that you can safely append
      // a subdirectory name to it.
      if (currentNode.baseDir.lastIndexOf("\\") != currentNode.baseDir.length - 1) {
        currentNode.baseDir += "\\";
      }
    
      // The custom hasContents property indicates whether the contents of the
      // directory the node represents have already been loaded.
      currentNode.hasContents = true;
    
      var dl = dlInfo.dirListing;
    
      // Loop through all the directory listing's elements, and add nodes to the
      // current node for each of the subdirectories and files.
      for (var i = 0; i < dl.getLength(  ); i++) {
        var tmpNode = new FTreeNode(  );
        tmpNode.setLabel(dl.getItemAt(i).name);
        tmpNode.setData(currentNode.baseDir + dl.getItemAt(i).name);
        tmpNode.type = dl.getItemAt(i).type;
        if (dl.getItemAt(i).type.toLowerCase(  ) == "dir") {
          tmpNode.setIsBranch(true);
        }
        tree.addNode(currentNode, tmpNode);
      }
    }
    
    // The doSelect(  ) function is the callback function for when a node is selected.
    // It is automatically passed a reference to the Tree component.
    function doSelect (tr) {
    
      // Get the selected node.
      var sn = tr.getSelectedNode(  );
    
      // If the selected node is not a branch node (meaning it does not have any
      // child nodes), it represents a file; otherwise, it represents a directory.
      // Display the file name in the path text field and store the full path
      // information in a custom property of the text field so that if the user
      // clicks the Submit button, the full path can be located easily.
      if (!sn.isBranch(  )) {
        path.text = sn.getLabel(  );
        path.data = sn.getData().substr(tree.getRootNode(  ).baseDir.length);
        pathUploadConfirm.text = "the file: \n" + path.data + 
                                 "\n has been added to the playlist";
      }
    
      // Since a new node has been selected, set the
      // currentNode to the selected node.
      currentNode = sn;
    
      // If the directory listing hasn't already been downloaded, retrieve it from
      // the server by passing the service function the full path to the directory
      // represented by the selected node
      if (!sn.hasContents) {
        dirBrowserSrvc.getDirectoryInfo(dirInfoRes, sn.getData(  ));
      }
    }
    
    // The submitSelected(  ) function is invoked when the Submit button is clicked.
    function submitSelected (  ) {
    
      // If the data property of the text field is defined (meaning a file has been
      // selected) send the filename to the main jukebox movie using a local
      // connection (sender). Set the multipage form to the second page.
      if (path.data != undefined) {
        sender.send("pathSendConnection", "receivePathInfo", path.data);
        myMPForm.setPage(2);
      }
    }
    
    init(  );
    createTreeForm(  );
    createConfirmForm(  );
    createMultiForm(  );
  3. Save the Flash document. We export the movie in the next section.

Now let's look at some of the ActionScript code in this document a little more closely. Much of the code is quite straightforward, but some sections involve techniques with which you might not be familiar.

The init( ) function does not do much that is unusual. It creates a connection object and a service object that maps to the DirectoryBrowser.cfc file. In addition, it creates a response object for handling the results from the service function. This uses standard Flash Remoting techniques, as discussed in Chapter 20. The call to the service function is standard as well, but notice that we don't pass any parameters to the function. The first time this service function is called, we don't yet know the path to the directory to list. Remember that you designed the CFC function such that if it receives no parameter, it uses the directory in which the CFC is stored.

function init (  ) {
  var gwURL = "http://localhost:8500/flashservices/gateway";
  NetServices.setDefaultGatewayURL(gwURL);
  var conn = NetServices.createGatewayConnection(  );
  dirBrowserSrvc = conn.getService("DirectoryBrowser");
  dirInfoRes = new Object(  );
  dirInfoRes.onResult = function (result) {
    _root.addNodes(result);
  };
  dirBrowserSrvc.getDirectoryInfo(dirInfoRes);
  sender = new LocalConnection(  );
  currentNode = null;
}

The createTreeForm( ) and createCofirmForm( ) functions are very straightforward. They each create form elements, position them, and add them to forms. Then, the createMultiForm( ) function adds both forms to a multipage form and sets it to display the first page. This allows the two forms to occupy the same space while only one is visible.

function createMultiForm (  ) {
  myMPForm = new MultiPageForm(  );
  myMPForm.addForm(treeForm);
  myMPForm.addForm(confirmForm);
  myMPForm.setPage(1);
}

The addNodes( ) function is called whenever a result is returned from the service function, which we use to populate the selected node in the Tree component. The first time this function is called, the currentNode variable is null, and the function must create the root node of the Tree component. In this case, it needs to get the directory name from the full path. One convenient way to do this is to split the path into an array using the slash as a delimiter. For example, if the path value is "C:\CFusionMX\wwwroot\", you can create an array with values "C:", "CFusionMX", "wwwroot", and an empty string (because of the trailing slash). The directory name is always either the last element of the array (in the case of no trailing slash) or the second-to-last element (in the case of a trailing slash). This code finds the directory name in either case:

if (currentNode == null) {
  var dirNameAr = dlInfo.dir.split("\\");
  var dirName = dirNameAr[dirNameAr.length - 1];
  if (dirName == "") {
    dirName = dirNameAr[dirNameAr.length - 2];
  }
  var node = new FTreeNode(  );
  node.setLabel(dirName);
  node.setData(dlInfo.dir);
  tree.setRootNode(node);
  currentNode = node;
}

When the user selects a file element from a tree node, you need to know the path to that file on the server. For this purpose, you should store the directory path (dlInfo.dir) as a property of the node that is currently being populated. Additionally, to append the filename to the path, the path must end in a trailing slash, so we add a trailing slash, if necessary:

currentNode.baseDir = dlInfo.dir;
if (currentNode.baseDir.lastIndexOf("\\") != currentNode.baseDir.length - 1) {
  currentNode.baseDir += "\\";
}

The custom hasContents property tells Flash if a node already has contents loaded into it:

currentNode.hasContents = true;

Once the contents have been retrieved from the server, it would be wasteful to request that information again, so we'll make future Flash Remoting requests only if hasContents is not true.

The last part of the addNodes( ) function populates the selected node with the contents. The dirListing property of the object that is returned from the service function is an array of objects in which each object has a name and type property (this is the value that the <cfdirectory> returns). To populate the selected node, use a for statement to loop through all the elements of the object array. For each element, create a tree node in which the label is the name of the file or subdirectory, and the data is the full path to the file or subdirectory. Furthermore, if the element is a subdirectory, use the setIsBranch( ) method to configure the node so that it can be opened (expanded into a subdirectory listing).

var dl = dlInfo.dirListing;
for (var i = 0; i < dl.getLength(  ); i++) {
  var tmpNode = new FTreeNode(  );
  tmpNode.setLabel(dl.getItemAt(i).name);
  tmpNode.setData(currentNode.baseDir + dl.getItemAt(i).name);
  tmpNode.type = dl.getItemAt(i).type;
  if (dl.getItemAt(i).type.toLowerCase(  ) == "dir") {
    tmpNode.setIsBranch(true);
  }
  tree.addNode(currentNode, tmpNode);
}

The doSelect( ) callback function is invoked whenever a tree node is selected. To process the node, you must determine its type. Nodes that represent files are not branch nodes, as detected with the isBranch( ) method.

When a user selects a file from the Tree component, you should:

  1. Display the filename in the path text field

  2. Store the file's path so that you can access it easily, if and when the user chooses to submit that file to the jukebox

  3. Display the path in the pathUploadConfirm text field so that the user gets the confirmation that she has added the correct file before clicking Submit

Notice that the path.data value is a substring of the selected node's data value. The jukebox movie needs to know only the relative path to the file and not the full path. Since the CFC and the jukebox .swf file are stored in the same directory, the relative path to the selected file is the difference between the full path to the file and the full path to the application's root directory (which is stored in the baseDir property of the Tree component's root node object). For example, if the full path to the selected file is C:\CFusionMX\wwwroot\myMp3.mp3 and the full path to the application's root directory is C:\CFusionMX\wwwroot\, then the relative path to the file is simply myMp3.mp3. You can employ a little trick to determine the relative path: start with the file's full path, and then extract the substring starting from the length of the root's path and spanning to the end of the string. Additionally, set the currentNode variable to reference the selected node. If the contents of the node have not been downloaded, call the service function to retrieve the data, passing it the path to the directory that the node represents.

function doSelect (tr) {
  var sn = tr.getSelectedNode(  );
  if (!sn.isBranch(  )) {
    path.text = sn.getLabel(  );
    path.data = sn.getData().substr(tree.getRootNode(  ).baseDir.length);
    pathUploadConfirm.text = "the file: \n" + path.data + 
                             "\n has been added to the playlist";
  }
  currentNode = sn;
  if (!sn.hasContents) {
    dirBrowserSrvc.getDirectoryInfo(dirInfoRes, sn.getData(  ));
  }
}

When the user clicks the Submit button, send the relative path of the selected file to the main jukebox using a local connection. Additionally, display the next page of the multipage form (the confirmation screen).

function submitSelected (  ) {
  if (path.data != undefined) {
    sender.send("pathSendConnection", "receivePathInfo", path.data);
    myMPForm.setPage(2);
  }
}
26.2.2.3 Making the HTML page for the server MP3 selector

The last step in making the server MP3 selector is to export the .swf file and create the HTML page in which to embed it. Flash's Publish feature creates both the .swf and .html files. Afterwards, we modify the .html file to set the margins to 0 so that the Flash movie is flush with the top-left corner of the browser window.

Here are the steps to create the .swf file and HTML page:

  1. With the directoryBrowser.fla open in Flash, choose File Publish Settings, which opens the Publish Settings dialog box.

  2. Under the Formats tab, select the Flash and HTML checkboxes. Also, select the Use Default Names checkbox so that Flash generates files named directoryBrowser.swf and directoryBrowser.html.

  3. Click the Publish button to generate the .swf and .html files.

  4. Click OK to close the Publish Settings dialog box.

  5. Close the Flash document.

  6. Open directoryBrowser.html in a text editor or web page editor. If you use a WYSIWYG editor such as Dreamweaver, switch to Code view mode to modify the code directly.

  7. Modify the <body> tag so that it reads:

    <BODY bgcolor="#FFFFFF" leftmargin="0" topmargin="0">
  8. Save directoryBrowser.html and close it.

26.2.2.4 Notes on the server MP3 selector

Note the following when implementing the server MP3 selector:

  • The server MP3 selector allows the user to browse the directory (and its subdirectories) in which the CFC is stored on the server. Therefore, to let the user select MP3 files from the server, place them within that directory or one of its subdirectories.

  • The Tree component distinguishes between selecting a node and opening/expanding a node, but our select handler function is not called only when a node is selected. When the user clicks on a node icon to highlight it, our doSelect( ) handler function is triggered. However, a branch node can be opened/expanded by clicking on the plus sign next to the node icon. This does not automatically select the node, and there is no simple way to detect this event. The problem is that nodes aren't populated until they are selected. Therefore, a node that has been opened but not selected does not display its contents. There are at least two possible solutions:

    • Leave things as they are, and require the user to both select and expand a node to view the contents.

    • Revise the code so that it recursively populates all the subdirectories from the beginning.

  • As designed, the main jukebox movie must be served from a web server (as opposed to running from a local file) to work correctly with the server MP3 selector. Furthermore, the CFC, the Flash movies, and the HTML files must be in the same directory. This ensures that the paths returned from the CFC match up relative to the location of the Flash movie.

    [ Team LiB ] Previous Section Next Section