Dreamweaver MX Extensions

Workshop #4: Creating a Menu Command

We've already built quite a lot in the practice sessions. In this workshop, we'll put all the pieces together, using isCommandChecked() and getDynamicContent() , as well as other command API options, to create something robust and useful.

Sample File: Creating a Menu Command that Displays the Images in a Document

For this workshop, we'll create a dynamically generated submenu that lists all the images in the current document, includes a checkmark if one of those images is selected, and selects the appropriate image if the user chooses it from the submenu. We'll even tell Dreamweaver to scroll, if necessary, to display the newly selected image in the Document window.

Task 1: Create the menu entry

Quit Dreamweaver and open menus .xml. Navigate to your menu entries at the bottom of the Modify menu. Add another <menu> entry, containing the following code:

<menu name="Select Image" id="DWMenu_Modify_SelectImage"> <menuitem dynamic name="(no images)" file="Menus/Development/Select_Images.htm" id="Development_SelectImages" /> </menu>

This code creates another submenu in the Modify menu, this one named Select Image. The Select Image submenu is set to contain at least one menu item, and possibly more, generated by the getDynamicContent() function in the Select_Images.htm command filealthough the menu entry won't work yet, because the Select_Images.htm file hasn't yet been created. You may already have figured out that the coding for the getDynamicContent() function in Select_Images.htm must be written to return an array of strings containing names of images in the user's document; or, if no images are present in the user's document, code must be written to return null , which will result in the Select Image submenu displaying with one menu item only, named (no images) .

note

If you don't want your Modify menu cluttered up with Development entries, you can take this opportunity to strip out all the custom <menu> and <menuitem/> elements you added in the Practice Session.

Task 2: Create the command file

Start with the basic command file framework. In your text editor, create a new HTML file. Save it in the Menus/Development folder as Select_Images.htm. Enter the following basic command file framework code:

<html> <head> <title>Select Image</title> <script language="JavaScript"> //determine if command is available in the menu function canAcceptCommand() { //return true if images are present in the user's document return true; } //generate dynamic submenu function getDynamicContent(itemID) { //return an array of strings to create menu items for all images in document //or return null } function receiveArguments(itemID) { //collect argument from menu item that accesses this command file return null; //temporary } //hold argument collected by receiveArguments() var gWhat; function isCommandChecked() { //if an image is selected, put a check mark by corresponding menu item return false; //temporary } function selectImage() { //this is the main function //it selects an image in the user's document } </script> </head> <body onLoad="selectImage()"> </body> </html>

Can you see what all the pieces of this command file are for? Because the command is for working with the images in a document, you need canAcceptCommand() to make it unavailable in the menu (grayed out) if the current document contains no images. Because you want to generate a dynamic submenu with multiple entries, you need the getDynamicContent() function. Because you may want one of the menu items to have a checkmark, you need isCommandChecked() . And because the main command function, myCommand() , will need to know which dynamically generated menu item is being used, you need receiveArguments() and its related global variable, gWhat . The comments in the code indicate approximately what needs to be accomplished within each function. The functions that must return values have been given very simple return statements, so the command file is syntactically and logically correct.

At this point, you should be able to launch Dreamweaver and see your Modify > Select Image submenu in place, though it will contain only the default menu item.

Task 3: Fill in the getDynamicContent() function

Your goal is to collect a list of all the images in the current document and create a menu item for each image. To do this, you must fill in the getDynamicContent() function to return an array of strings if the user's document includes images, and a null value if there are no images.

  1. Open your command file, Select_Images.htm. Revise the getDynamicContent() function by adding a framework of comments, like this (new code is in bold):

    function getDynamicContent(itemID) { //access the DOM so the document can be examined //if the document contains images //create an array to be returned //for each image in the document, add a text string to the array //or return null return null; }

    As these comments indicate, if the document contains images, the function needs to step through the document's node list of images, creating a text string for each one.

  2. To access the DOM (the first commented task), add the following code:

    function getDynamicContent(itemID) { //access the DOM so the document can be examined var myDOM=dw.getDocumentDOM(); //if the document contains images //create an array to be returned //for each image in the document, add a text string to the array //or return null return null; }

  3. Then add a conditional statement to test whether the document contains images:

    function getDynamicContent(itemID) { //access the DOM so the document can be examined var myDOM=dw.getDocumentDOM(); //if the document contains images if (myDOM.getElementsByTagName('IMG').item(0) { //create an array to be returned //for each image in the document, add a text string to the array } else { //or return null return null; } }

  4. Within the conditional, create an array to hold the return string:

    function getDynamicContent(itemID) { //access the DOM so the document can be examined var myDOM=dw.getDocumentDOM(); //if the document contains images if (myDOM.getElementsByTagName('IMG').item(0) { //create an array to be returned var imageList = new Array(); //for each image in the document, add a text string to the array return imageList; } else { //or return null return null; } }

  5. And, finally, the trickiest partadd a for loop within the conditional, to construct the array of text strings from the document's images:

    function getDynamicContent(itemID) { //access the DOM so the document can be examined var myDOM=dw.getDocumentDOM(); //if the document contains images if (myDOM.getElementsByTagName('IMG').item(0)) { //create an array to be returned var imageList = new Array(); var imageName; //for each image in the document, add a text string to the array for (var a=0;a<myDOM.getElementsByTagName('IMG').length;a++) { imageName = myDOM.getElementsByTagName('IMG').item(a).src; imageList[a] = new String(imageName+";id='"+itemID+"_"+a+"'"); } return imageList; } else { //or return null return null; } }

    What's happening here? The imageName variable is created in line 8 to hold the src attribute for each image that is processed . Then the for loop in lines 1013 steps through the node list of images in the document, accessed using the dom.getElementsByTagName() method. For each image in the node list, a string is constructed from the imageName , the itemID (the parameter Dreamweaver passes automatically to this function, and which holds the unique ID for the Select Image <menuitem/> element), and the counter variable a .

    For instance, assuming that the first image in your document has a src attribute of images/myImage.gif , and remembering that the menu item for this dynamic command has an id attribute of Development_SelectImage , the concatenation in line 12 would create images/myImage.gif; id='Development_SelectImage_0' as the first string to go into the imageList array. Assuming that the second image in the document has a src attribute of images/otherImage.gif , the string to be created for that dynamic menu item would be images/otherImage.gif;id='Development_SelectImage_1' . Figure 5.37 shows a diagram of how the concatenation works. (Why add the number from the counter variable a to the end of the ID? Because it guarantees that the ID for each dynamically generated menu item is unique. The first time through the for loop, a = 0 and the ID generated will be Development_SelectImage_0; the second time through the loop, the ID will be Development_SelectImage_1; and so on.)

    Figure 5.37. Concatenating dynamic elements into a properly formatted return string for getDynamicContent() .

  6. At this point, your command contains enough information to correctly display the dynamic Modify > Select Image submenu. Launch Dreamweaver and try it out by opening an HTML document containing several images. When you choose Modify > Select Image, the dynamic menu that Dreamweaver generates should contain the name of every image in the document, as shown in Figure 5.38.

    Figure 5.38. A dynamically generated submenu showing path information for all image files in the current document.

  7. One problem remains with getDynamicContent() in this command. If the images in the user's document have long, complex file paths (such as ../../images/myImage.gif ), the menu items will be cumbersome and long. To alleviate this, truncate the imageName like this (new code is in bold):

    function getDynamicContent(itemID) { //access the DOM so the document can be examined var myDOM=dw.getDocumentDOM(); //if the document contains images if (myDOM.getElementsByTagName('IMG').item(0)) { //create an array to be returned var imageList = new Array(); var imageName; //for each image in the document, add a text string to the array for (var a=0;a<myDOM.getElementsByTagName('IMG').length;a++) { imageName = myDOM.getElementsByTagName('IMG').item(a).src; imageName = imageName.substring(imageName.lastIndexOf('/') +1,imageName.length); imageList[a] = new String(imageName+";id='"+itemID+"_"+a+"'"); } return imageList; } else { //or return null return null; } }

    This new code statement uses the JavaScript substring method to isolate just the filename of the image in question, removing all folder paths and other URL information.

Task 4: Revise the canAcceptCommand() function to gray the command out if there is no document open, or if there are no images present

There's nothing new about this one by now. In your command file, revise the canAcceptCommand() function to look like this (new code is in bold):

function canAcceptCommand() { //return true if images are present in the user's document //access the user's document var myDOM = dw.getDocumentDOM(); //if there's no open document, return false if (!myDOM) return false; //if there are any <img> tags, return true if (myDOM.getElementsByTagName('IMG').item(0)) { return true; } else { return false; } }

What's happening here? The dom.getElementsByTagName() method is used to access the node list of images for the current document. If the node list contains at least one item ( item(0) ), the function returns true ; otherwise , it returns false .

Task 5: Collect arguments with receiveArguments()

Why are you doing this? Remember that all dynamically generated items in the Select Images submenu access this same command file. In essence, a separate instance of the Select_Images.htm command file will be run for each submenu item. In the main function, which you will write next , you need to determine which of the submenu items is calling each particular instance of the command file. Remember also that the dynamically generated menu items automatically pass their ID values to the command file as arguments. All you need to do is collect them.

note

Because the Select Image menu item will be grayed out if no images are present, the Select Image submenu will never appear unless images are present. So it isn't really necessary to test for the presence of images in the getDynamicContent() function, or to return a null value if no images are present. If no images are present, getDynamicContent() will never be executed because canAcceptCommand() will return false and stop the whole API procedure.

You've already created a global variable gWhat to store the argument being passed to the command. The function receiveArguments() just needs to collect the argument and put it in that variable. Add the following code to your document:

function receiveArguments() { //collect argument from menu item that accesses this command file gWhat = arguments[0]; }

note

As you may remember, some functions such as isCommandChecked() and setMenuText() are automatically passed these arguments. Because the main function is locally created by you and is not part of the API, however, you must collect the arguments to use them.

Task 6: Create the main function

What do you want to happen when the user clicks on one of the submenu items? You want the image to be selected in the document. You also want Dreamweaver to scroll to the image if it needs to so that the user can examine it. That functionality needs to be built into the command's main function.

  1. In scripting terms, what you need to do is collect the argument (to determine which menu item the user has clicked on), and from that, determine which image to select. The argument is the ID, remember, but exactly what does the ID contain? To find out, revise your main function to look like this:

    function selectImage() { window.alert(gWhat); }

  2. Try the command out now. Choosing one of the submenu items brings up an alert window with the message Development_SelectImage_ n that's the argument you constructed in getDynamicContent() .

  3. How do you determine the correct image to select based on the argument? The last character in the argument string is the item number of an image in the node list created using getElementsByTagName() . If you can isolate that character and convert it back from a substring into a number, you can reconstruct the image array, and determine which image name the user just clicked on. To do this, change your main function code to look like this (new code is in bold):

    function selectImage() { var what = new String(gWhat); what = what.substring(what.lastIndexOf("_")+1,what.length); var myDOM = dw.getDocumentDOM(); var myImages = myDOM.getElementsByTagName('IMG'); var myObject = myImages.item(parseInt(what)); }

    Can you see what's happening here? In line 2, you collect the global variable gWhat , converting it into a string object so that you can perform string operations on it. In line 3, you extract the portion of the string that contains the image's node list number. You then gain access to the DOM ( myDOM ) and to the node list of images ( myImages ). And finally, in line 6, you parse the what substring as an integer and use it to specify the correct image in the node list. You now have access to the image, stored in the myObject variable.

    note

    You could also have approached the problem from another way, and included the image's src name in the unique ID generated by getDynamicContent() . Then, you could use a for loop to step through the image array in the document until you found a match of src values.

  4. What do you do with the image, now that you have access? You select it, of course! Revise your function to look like this (new code is in bold):

    function runCommand() { var what = new String(gWhat); what = what.substring(what.lastIndexOf("_")+1,what.length); var myDOM = dw.getDocumentDOM(); var myImages = myDOM.getElementsByTagName('IMG'); var myObject = myImages.item(parseInt(what)); myDOM.setSelectedNode(myObject,false,true); }

    What does this code do? As you learned in Chapter 4, dom.setSelectedNode() selects an object. If you look at the table shown in Table 4.14 in Chapter 4, you'll see that this method has two optional parameters, both Booleans: bSelectInside and bJumpToNode . You don't want to use the first of these, so you set it to false . But you do want Dreamweaver to scroll to the selected object, if it needs to, to display the selected image in the Document window; so you set bJumpToNode to true .

  5. Try it out! To see it working, use a test document that has lots of images and requires some scrolling. Choosing an image from the submenu should scroll to the image and leave it selected.

Task 7: Revise the isCommandChecked() function so that it puts a checkmark by a menu item if its associated image is selected

This is very similar to what you did in Task 5 of the practice session, earlier in this chapter. When the user chooses Modify > Select Images and causes its submenu to display, if there's already an image selected, you want that menu entry to have a checkmark next to it.

  1. The function must determine which of the submenu items was chosen , which image is associated with that item, and if the image is selected in the document. To start, add the following set of comment lines to the isCommandChecked() function in your command file:

    function isCommandChecked() { //collect the argument, and isolate the item number at its end //get access to the image node list, and the current selection //if the selection is an image, and if its number in the node list matches the argument, //return true //otherwise, return false return false; }

  2. To fill in the code for the first commented task, remember that each menu item that calls this command file will have a name that specifies an image and an ID that looks like this: Development_SelectImage_1 , where the number at the end corresponds with the image's number in the document's node list of images. This information is passed automatically to isCommandChecked() as arguments[0] . To determine which image this instance of the command file is referring to, you need to isolate the number at the end of this parameter. Revise your isCommandChecked() function to look like this (new code is in bold):

    function isCommandChecked() { //collect the argument, and isolate the item number at its end var what = new String(arguments[0]); what = what.substring(what.lastIndexOf('_')+1,what.length); var myNumber = parseInt(what); //get access to the image node list, and the current selection //if the selection is an image, and if its number in the node list matches the argument, //return true //otherwise, return false return false; }

    By feeding arguments[0] into a specifically created String object ( what ), you guarantee that the substring method will work on it. By parsing the truncated what string as an integer, you then guarantee that you will be able to use it to access numbered items in the document's images node list.

  3. To access the appropriate image, add the following code:

    function isCommandChecked() { //collect the argument, and isolate the item number at its end var what = new String(arguments[0]); what = what.substring(what.lastIndexOf('_')+1,what.length); var myNumber = parseInt(what); //get access to the image node list, and the current selection var myDOM=dw.getDocumentDOM(); var myImages=myDOM.getElementsByTagName('IMG'); var myObject=myDOM.getSelectedNode(); //if the selection is an image, and if its number in the node list matches the argument, //return true //otherwise, return false return false; }

    In these three lines of code, you access the DOM ( myDOM ), access the node list of images for the document ( myImages ), and finally access the selected object ( myObject )whether it's an image or not.

  4. To determine whether the selected object is the same image referenced by arguments[0] , add the following code:

    function isCommandChecked() { //collect the argument, and isolate the item number at its end var what = new String(arguments[0]); what = what.substring(what.lastIndexOf('_')+1,what.length); var myNumber = parseInt(what); //get access to the image node list, and the current selection var myDOM=dw.getDocumentDOM(); var myImages=myDOM.getElementsByTagName('IMG'); var myObject=myDOM.getSelectedNode(); //if the selection is an image, and if its number in the node list matches the argument, if (myObject.nodeType=="1" && myObject.tagName=="IMG" && myObject==myImages.item(myNumber)) { //return true return true; //otherwise, return false }else{ return false; } }

    In line 11, you're asking if the selected object ( myObject ) is of node type 1 (HTML tag); if the selected object has a tagName attribute of IMG ; and if the selected object is the same object as that found in item ( myNumber ) of the document's node list of images. For instance, if the arguments[0] parameter contains the string Development_SelectImage_4 , is the selected object the same object also referred to as item(4) in the node list of images? If so, return true (and add a checkmark to this menu item). If not, return false and add no checkmark.

  5. You might think you're done at this point, but not quite. What if there are no images? What if the user has nothing selected? To take care of these possible situations, you need to wrap the entire set of statements in a few conditionals (new code is in bold):

    function isCommandChecked() { //window.alert(arguments[0]); //collect the argument, and isolate the item number at its end var what = new String(arguments[0]); //if there are any arguments if (what) { what = what.substring(what.lastIndexOf('_')+1,what.length); var myNumber = parseInt(what); //get access to the image node list, and the current selection var myDOM=dw.getDocumentDOM(); var myImages=myDOM.getElementsByTagName('IMG'); var myObject=myDOM.getSelectedNode(); //if there is a selection if (myObject) { //if the selection is an image, and if its number in the node list matches the argument, if (myObject.nodeType=="1" && myObject.tagName=="IMG" && myObject==myImages.item( myNumber)) { //return true return true; } //end if (myObject) } //end if (what) //otherwise, return false }else{ return false; } }

  6. Reload Dreamweaver extensions and try it out! Open a Dreamweaver document containing several images and select one. Then access the Modify > Select Image submenu. All images in your document should be represented by menu items, and the menu item corresponding to the selected image should appear with a checkmark next to its name.

    Try the command with no images selected. Try the command in a document with no images. How does your command fare? If you have problems, troubleshoot to identify and eliminate them. Then congratulate yourself on a command well done. Listing 1 shows the final, working version of this command.

Listing 5.3 Final Code for the Select Image Menu Command, Commented for Reference

<html> <head> <title>Select Image</title> <script language="JavaScript"> //determine if command is available in the menu //return true if images are present in the user's document function canAcceptCommand() { //access the user's document var myDOM = dw.getDocumentDOM(); //if there's no open document, return false if (!myDOM) return false; //if there are any <img> tags, return true if (myDOM.getElementsByTagName('IMG').item(0)) { return true; } else { return false; } } //determine if a check mark should appear next to the menu item function isCommandChecked() { //collect the argument, and isolate the item number at its end var what = new String(arguments[0]); if (what) { // if there are no images, there will be no argument what = what.substring(what.lastIndexOf('_')+1,what.length); var myNumber = parseInt(what); //get access to the image node list and the current selection var myDOM=dw.getDocumentDOM(); var myImages=myDOM.getElementsByTagName('IMG'); var myObject=myDOM.getSelectedNode(); //if the selection is an image, and if its number in the node list matches the argument, //return true if (myObject.nodeType=="1" && myObject.tagName=="IMG" && myObject==myImages.item( myNumber)) { return true; } //otherwise, return false }else{ return false; } } //generate dynamic submenu function getDynamicContent(itemID) { //access the DOM so the document can be examined var myDOM=dw.getDocumentDOM(); //if the document contains images if (myDOM.getElementsByTagName('IMG').item(0)) { //create an array to be returned var imageList = new Array(); var imageName; //for each image in the document, add a text string to the array for (var a=0;a<myDOM.getElementsByTagName('IMG').length;a++) { //put src path in imageName imageName = myDOM.getElementsByTagName('IMG').item(a).src; //truncate imageName for display imageName = imageName.substring (imageName.lastIndexOf('/')+1,imageName.length); //construct return string consisting of name and ID imageList[a] = new String(imageName+";id='"+itemID+"_"+a+"'"); } //if there is an array of strings to return, return it return imageList; //otherwise, return null and no submenu entries will be generated } else { return null; } } //determine which of the dynamic entries has called this instance of the command function receiveArguments() { //collect argument from menu item that accesses this command file gWhat = arguments[0]; } //global variable to hold argument collected by receiveArguments() var gWhat; //main function, called and executed onLoad function selectImage() { //collect argument to determine which dynamic menu item user has chosen var what = new String(gWhat); //isolate node list number from end of argument, to determine which image to process what = what.substring(what.lastIndexOf("_")+1,what.length); //access the image in question var myDOM = dw.getDocumentDOM(); var myImages = myDOM.getElementsByTagName('IMG'); var myObject = myImages.item(parseInt(what)); //and select the image, scrolling to show it if needed myDOM.setSelectedNode(myObject,false,true); } </script> </head> <body onLoad="selectImage()"> </body> </html>

note

Troubleshooting

What if it doesn't work? Logic errors can be the bane of commands as complex as this one, though if you followed the code written here, your logical bases should be covered. If you run into syntactical errors, make sure your code matches the code in Listing 1 exactly. Watch out for typos!

If you are experiencing problems, at what point in the API procedure are they happening? Can you tell which function is executing improperly? Here's a trick if you think one of the API functions is causing problems: Add an alert statement to each API function getDynamicContent() , isCommandChecked() , receiveArguments() so that you can tell when each is function is being executed. The code for the alert message can be simple, like window.alert("The getDynamicContent() function is running.") . Then try to run the command, paying attention to when the alerts appear in relation to when the command runs into problems.

note

Commands versus menu commands, revisited: In terms of functionality, menu commands have special menurelated API elements that commands don't have. There's nothing, however, to stop you adding your regular commands to your folder within the Configuration/Menus folder. It makes for a tidier Configuration folder if all your custom commands are in that one folder, rather than having half of them in the Commands folder. To move the Automatic Alt text command to the Menus folder, move Automatic Alt Text.htm to Configuration/Menus/Development, and add a <menuitem/> tag to the proper place in the menus.xml file.

Категории