Example: Cross-Application Extension: Thumbnail Builder


Example: Cross-Application Extension: Thumbnail Builder

As noted earlier, Dreamweaver comes with Create Web Photo Album, which is a tool for creating thumbnails. However, this command is intended for batch-processing a series of images. What if you want to create thumbnails one at a time? I wrote the Thumbnail Builder extension to fill just that need (see Listing 7-1).

Listing 7-.1 Thumbnail Builder.htm (07_ThumbnailBuilder.mxp)
 <!-- MENU-LOCATION=NONE -->  <html>  <head>  <title>Thumbnail Builder</title>  <!-- By Joseph Lowery,       Author of the Dreamweaver Bible series,       the Fireworks Bible series and Beyond Dreamweaver       jlowery@idest.com  -->  <script src="../Shared/Fireworks/FwExec.js"></script>  <script language="javascript"  src="../Shared/MM/Scripts/CMN/UI.js"></script>  <script language="javascript"  src="../Shared/MM/Scripts/CMN/FILE.js"></script>  <script language="javascript"  src="../Shared/MM/Scripts/CMN/enableControl.js"></script>  <script language="javascript"  src="../Shared/MM/Scripts/CMN/enableControl.js"></script>  <script language="javascript"  src="../Shared/MM/Scripts/CMN/docInfo.js"></script>  <script src="ThumbnailBuilder.js"></script>  <link rel="stylesheet" href="mmres://user_interface_dialog.css">  <style type="text/css">  <!-- .textbox {  width: 30px; height: 15px}  -->  </style>  </head>  <body onLoad="initUI()">  <form name="theForm">    <table border="0">      <tr>        <td colspan="4" nowrap><currentsize>Current Image: W: xx        &nbsp;&nbsp;H:          xx</currentsize></td>      </tr>      <tr>        <td colspan="4"><newsize>Thumbnail: W:        &nbsp;&nbsp;&nbsp;&nbsp;H:</newsize></td>      </tr>      <tr>        <td colspan="4">          <hr>        </td>      </tr>      <tr>        <td>Size:</td>        <td>          <div align="right">            <input name="sizeRB" type="radio"            onClick="javascript:document.theForm.widthText.focus()"            value="width" checked>          </div>        </td>        <td>Width</td>        <td nowrap>          <input type="text" name="widthText" class="textbox"          onBlur="checkField(this.name)">          pixels </td>      </tr>      <tr>        <td>&nbsp;</td>        <td>           <div align="right">            <input type="radio" name="sizeRB" value="height"            onClick="javascript:document.theForm.            heightText.focus()">          </div>        </td>        <td>Height</td>        <td nowrap>          <input type="text" name="heightText" class="textbox"          onBlur="checkField(this.name)">          pixels </td>      </tr>      <tr>        <td>&nbsp;</td>        <td>          <div align="right">            <input type="radio" name="sizeRB" value="percentage"            onClick="javascript:document.theForm.            percentageText.focus()">          </div>        </td>        <td>Percentage</td>        <td nowrap>          <input type="text" name="percentageText" class="textbox"          onBlur="checkField(this.name)">          % </td>      </tr>      <tr>        <td colspan="4">          <hr>        </td>      </tr>      <tr>        <td>Border:</td>        <td>          <div align="right">            <input type="checkbox" name="htmlCB" value="html">          </div>        </td>        <td>HTML</td>        <td>&nbsp; </td>      </tr>      <tr>        <td>&nbsp;</td>        <td>          <div align="right">            <input type="checkbox" name="bevelCB" value="checkbox">          </div>        </td>        <td>Bevel</td>        <td>&nbsp; </td>      </tr>      <tr>        <td>&nbsp;</td>        <td>          <div align="right">            <input type="checkbox" name="dropshadowCB"            value="checkbox">          </div>        </td>        <td nowrap>Drop Shadow</td>        <td>&nbsp;</td>      </tr>      <tr>        <td colspan="4">          <hr>        </td>      </tr>      <tr>        <td nowrap>Target:</td>        <td>          <div align="right">            <input type="radio" name="targetRB" value="jspw">          </div>         </td>        <td colspan="2">Just So Picture Window</td>      </tr>      <tr>        <td>&nbsp;</td>        <td>          <div align="right">            <input type="radio" name="targetRB" value="radiobutton">          </div>        </td>        <td colspan="2">_blank page</td>      </tr>    </table>  </form></body>  </html> 

Here's how the command is used:

  1. The user selects an image to convert to a thumbnail and run the command.

    The user must select an image for the command to become active in the menu.

  2. In the Thumbnail Builder dialog box, the user must enter the size of the thumbnail first. An image can be sized in one of three ways:

    • Width The entered value sets the width of the thumbnail in pixels; the height is proportionately calculated.

    • Height The user sets the thumbnail's height in pixels; again, the other dimensionin this case widthis automatically calculated according to the width to height ratio of the original image.

    • Percentage The width and height are calculated according to the entered percentage value.

      After the user selects the method and enters the value, the new dimensions are presented just below the original dimensions, as shown in Figure 7.5.

      Figure 7.5. The Thumbnail Builder interface displays the settings from the previous session to enhance user productivity.

      graphics/07fig05.gif

  1. Next , the user selects the type of effects to be applied to the thumbnail. Again, the user has three choices: HTML, which places a standard 1-pixel wide border around the image, indicating it is a link; Bevel, which applies Fireworks' built-in Bevel effect to the thumbnail; and Drop Shadow, which, as expected, adds a Fireworks drop shadow to the thumbnail.

    NOTE:

    Only one extension (a command) is calling another (a behavior) here, but arguments are being passed to the second extension from the first. You'll see how it's done later in the section "Executing the Thumbnail Builder Command."

    The effects can be used in combination with each other, although combining HTML with the Drop Shadow option might not create the desired look because the HTML border wraps around the entire image, including the shadow.

  2. The final choice determines how the original image is displayed after the thumbnail is clicked. If the user has the Just So Picture Window extension by E. Michael Brandt installed, that option becomes available; if selected, the original image opens in a separate window, sized to fit the graphic. Otherwise, the image is targeted to open in the same browser window as the current document.

  3. After the user selects the options and clicks OK, the thumbnail is built in Fireworks and stored in the same folder as the original image; the same original filename plus a suffix of _small is used.

  4. In Dreamweaver, the original image is replaced with the new thumbnail, complete with a link to the larger image. In the code, this line:

     <img src="images/tiger.jpg" width="407" height="256"> 

    is replaced with this line:

     <a href="images/tiger.jpg"><img src="images/tiger_small.jpg"  width="407" height="256"></a> 

    If the Just So Picture Window option is selected, an onClick function call is added to the <a> tag in addition to the necessary behavior function being written to the <head> section.

NOTE

The Thumbnail Builder extension and all the source files are located in the Extensions folder on the book's companion web site. You'll also find the Just So Picture Window extension included so that you can use the Thumbnail Builder to its fullest extent.


Next, we'll deconstruct the primary code from the Thumbnail Builder extension. Not only will you find a carefully drawn roadmap to create your own cross-application extensions in the following sections, but I've also included numerous tips and techniques for successfully building extensions of any type.

Controlling Menu Access

In versions prior to Dreamweaver MX, any command placed in the Configuration/Commands folder was automatically placed in the Commands menuunless a special snippet of code was placed at the top of the file. To avoid having your command placed at the bottom of the Commands menu, you need to include the first line you see here from the start of my command file, 07_ThumbnailBuilder.mxp:

 <!-- MENU-LOCATION=NONE -->  <html>  <head>  <title>Thumbnail Builder</title> 

With <!-- MENU-LOCATION=NONE --> in place, you're free to specify where and how you would like the command to be accessed. When the Extension Manager packages the command for installation, menu placement is noted in the .mxi file under the <configuration-changes> section (see Listing 7-2).

Listing 7-2 ThumbnailBuilder.mxp (07_ThumbnailBuilder.mxp)
 <macromedia-extension     name="Thumbnail Builder"     version="1.0.0"     type="Command">     <!-- List the required/compatible products -->     <products>        <product name="Dreamweaver" version="6" primary="true" />        <product name="Fireworks" version="3" primary="false" />     </products>     <!-- Describe the author -->     <author name="Joseph Lowery" />     <!-- Describe the extension -->     <description>     <![CDATA[     Thumbnail Builder creates a thumbnail from a selected image,     replaces the original image with the thumbnail and then creates a     link to the larger image. Thumbnail size may be specified by the     width, height or percentage. A variety of border attributes (HTML,     bevel and drop shadow) are available).     The original image can appear in the current browser window or, if     you have E. Michael Brandt's Just So Picture Window installed, in     its own window.     ]]>     </description>     <!-- Describe where the extension shows in the UI of the product -->     <ui-access>     <![CDATA[      You'll find this command in the Commands menu and in the image     context menu.     ]]>     </ui-access>     <!-- Describe the files that comprise the extension -->     <files>        <file name="Commands/ThumbnailBuilder.htm"  destination="$dreamweaver/configuration/commands" shared="true"/>        <file name="Commands/ThumbnailBuilder.js"  destination="$dreamweaver/configuration/commands" />        <file name="Commands/FwExecDlg.htm"  destination="$dreamweaver/configuration/commands" shared="true" />        <file name="Commands/FwExecDlg.js"  destination="$dreamweaver/configuration/commands" shared="true" />        <file name="Shared/Fireworks/FwExec.js"  destination="$dreamweaver/configuration/Shared/Fireworks"  shared="true" />     </files>     <!-- Describe the changes to the configuration -->  <configuration-changes>  <menu-insert insertAfter="DWMenu_Commands_OptimizeInFW">    <menuitem name="Thumbnail Builder..."  file="Commands/ThumbnailBuilder.htm" id="DWMenu_ThumbnailBuilder" />  </menu-insert>  <menu-insert insertAfter="DWContext_Image_OptimizeInFW">    <menuitem name="Thumbnail Builder..."  file="Commands/ThumbnailBuilder.htm"  id="DWContext_Image_ThumbnailBuilder" />  </menu-insert>  </configuration-changes>  </macromedia-extension> 

For the Thumbnail Builder command, I wanted to offer two options for access. The first option is under the Commands menu, after another cross-application extension, the Optimize in Fireworks entry. Wherever possible, I like to group related commands. The second option is in the context or pop-up menu for images, again after the Optimize in Fireworks command. After looking up the ID labels for the two Optimize in Fireworks entries in the Configuration/Menus/ menus .xml file, I insert the following code at the end of my .mxi file:

graphics/07icon01.gif

The two <menu-insert> entries do the same job and are identical except for the id property value, which must be unique.

TIP

If you're not familiar with the .mxi format or the process of packaging extensions, you can find all the necessary documentation from within the Extension Manager. Choose Help, Creating and Submitting Extensions for an overview of the procedure. Within the section on packaging an extension, you'll find a link to a document detailing the Macromedia Exchange Installation (MXI) file format.


Including the Proper Files

The next segment of code in the ThumbnailBuilder.htm file is made up of a series of includes. Aside from all the power offered by its DOM and JavaScript API, Dreamweaver includes a wealth of programming routines that are available for private use. Most of these files are located in the Configuration/Shared/MM folder and can be referenced with a <script scr="fileName"></script> code.

In ThumbnailBuilder.htm, five such files are included from Macromedia's script cache and one from one mine:

graphics/07icon02.gif

The first included file, FwExec.js, is necessary for smooth communication with Fireworks, as described earlier in this chapter. The next four files contain specific functions that are called within my command. The UI.js file, for example, is included in almost every extension I write because it contains the extraordinarily useful function, findObject() , which allows form elements to be properly located regardless of their location in the file. Some JavaScript files contain more specialized functions that are useful only in certain circumstances, such as the SetEnabled() function found in the enableControl.js file; this function is used to enable or disable form elements programmatically.

Styling the Dialog Box

Unknown to many extension developers, Dreamweaver uses its own built-in CSS style sheets to achieve a consistent look and feel throughout its many user interfaces. One such style sheet, user_interface_dialog.css , controls the appearance of all text and form input fields. To access this style sheet, link to it in this manner:

 <link rel="stylesheet" href="mmres://user_interface_dialog.css"> 

The mmres:// protocol designates a particular namespace recognized within Dreamweaver.

I also employ another style sheet declaration; rather than a link to an external file, this is an internal declaration for the custom class . textbox . I apply this CSS class to the various text fields for a consistent width that I control.

 <style type="text/css">  <!-- .textbox {  width: 30px; height: 15px}  -->  </style> 

Not only do these style sheet declarations ensure the appearance of the dialog box at runtime, but they also help during design time.

Adding Text Dynamically

In Thumbnail Builder, I need to show both the size of the original image and the dimensions of the new thumbnail. The original image dimensions depend on the selected image; therefore, they might vary one use to another. The thumbnail dimensions, on the other hand, are user determined and could change repeatedly while the user tries out different values.

To show both of these varyingly dynamic bits of information, two custom tags are used: <currentsize> and <newsize> . The two tags are embedded in the body text of the form as shown in the following code:

graphics/07icon03.gif

The <currentsize> text is calculated when the extension first loads. You'll find this code in the initUI() function:

graphics/07icon04.gif

The getNaturalSize() function returns a two-element array representing the width and height respectively. The document.getElementsByTagName ('currentsize') function call is used to display the dimensions in the dialog box. Note that the document DOM is addressed for the getElementsByTagName() function rather than the more commonly seen dw.getDocumentDOM() . Here, "document" refers to the extension interface, the dialog box, rather than the current Dreamweaver document. The getElementsByTagName() function also returns an arraystored in my variable, theHeading but because only one <currentsize> tag is available, I can replace the innerHTML of the first element with my new text, as shown in the third line of the preceding code snippet.

A similar strategy is used to display the dimensions of the thumbnail in the function updateSize() . This function is called upon initialization and whenever a new value is entered in one of the three size text fields. The updateSize() function first retrieves the selected image dimension to be used to calculate the new thumbnail size:

 dw.getNaturalSize(getFullPath(theSelObj.getAttribute("src"))); 

Next, we set up to replace the <newsize> text in the dialog box, as we did with the original size text:

 var theNewSizeText = document.getElementsByTagName('newsize'); 

Now we're ready to see which option is checked and react accordingly . In each case, one dimension is gathered from the user input and the second dimension is calculated. The calculations vary according to the option chosen , but notice that in each circumstance, the parseInt() function is used to convert the text of the submitted value into a number:

 if (theSizeChoice[0].checked) {   //width     theNewWidth = parseInt(findObject("widthText").value);     theNewHeight = parseInt((theNewWidth / parseInt(theOldSize[0]))  * parseInt(theOldSize[1]));  }  if (theSizeChoice[1].checked) {   //height     theNewHeight = parseInt(findObject("heightText").value);     theNewWidth = parseInt((theNewHeight / parseInt(theOldSize[1]))  * parseInt(theOldSize[0]));  }  if (theSizeChoice[2].checked) {   //percentage     var thePercent = parseInt(findObject("percentageText").value);     theNewWidth = parseInt((thePercent / 100) * theOldSize[0]);     theNewHeight = parseInt((thePercent / 100) * theOldSize[1]);  } 

After the calculations are all done, the inner HTML of the <newsize> tag is replaced with the desired text string:

 theNewSizeText[0].innerHTML = "Thumbnail: <b>W: " + theNewWidth +  " &nbsp;&nbsp;H: " + theNewHeight + "</b>";  } 

The bold and non-breaking space tags are added to give the dimensions a bit of emphasis and structure.

Crafting the Input Fields

One of my goals in structuring an interface is to make the user experience as intuitive as possible. The next bit of code illustrates one method of achieving that goal. Thumbnail Builder has a series of radio buttons , each with an associated text field, as shown in Figure 7.6. I know that when users select a particular radio buttonWidth, for examplethey intend to enter a value in the associated text field. The following bit of code uses inline JavaScript to set the focus to the proper text field after a radio button is selected.

Figure 7.6. The Size section depends on a couple of custom JavaScript functions to make it intuitive for users to change selections.

graphics/07fig06.gif

 <input type="radio" name="sizeRB" value="width"  onClick="javascript:document.theForm.widthText.focus()"> 

This code eliminates the need for a second click and makes it easy for the user to quickly enter his value. The reverse assumption also holds true: If I, as a user, enter a value in the Width text field, it can be assumed that I want to choose the Width option. Although simple in concept, these automatic selections requires a bit more coding, so here I'll call a custom function after the value has been entered and the field has been exited:

 <input type="text" name="widthText" class="textbox"  onBlur="checkField(this.name)"> 

The checkField() function checks to see which text field is being used and then performs two operations. First, it clears the other two text fields. If I had an old value in Percentage, and I entered a new value in Width, I wouldn't want the old value anymore, would I? Second, checkField() sets the appropriate radio button. Moreover, when those operations are completed, the updateSize() function (discussed in the previous section) is called so that my new thumbnail dimensions are displayed:

graphics/07icon05.gif

There is, as usual, a number of ways this function could have been written; you could, for example, structure it using a series of case statements rather than the if clauses as I did here.

Limiting the Command's Use

That completes our tour of the Thumbnailbuilder.htm file, which creates the user interface and includes the necessary files. Now let's look at the companion file, 07_ThumbnailBuilder.mxp (see Listing 7-3).

Listing 7-.3 ThumbnailBuilder.mxp (07_ThumbnailBuilder.mxp)
 /********************** GLOBAL VARS ************************/  var theSizeChoice = findObject("sizeRB");  var theCurrentSize;   var picWinHeight, picWinWidth, picWinAlt, picWinColor,  picWinHugger, picWinHugMargin;  /******************** GLOBAL CONSTANTS *********************/  /***********************************************************/  function canAcceptCommand() {     var theDom = dw.getDocumentDOM();     var selection = dreamweaver.getSelection();     var theSel = dreamweaver.offsetsToNode( selection[0],     selection[1] );     return(theSel != null && theSel.nodeType == Node.ELEMENT &&     theSel.tagName == "IMG");  }  function commandButtons(){     return new Array( 'OK', 'runCommand()', 'Help',     'doHelp()','Cancel', 'window.close()');  }  function doHelp() {  dw.browseDocument('http://www.fp2dw.com/extensions/tbhelp.htm');  }  function fw_makeThumbnail(theFile,theSizeOption, theSizeValue,  theBevelOption, theShadowOption, theBG){     var theMsg = "OK";     theSizeValue = parseInt(theSizeValue);     //lower the height/width value to accommodate for the shadow     if (theShadowOption == "true" && theSizeOption != "scale") {        theSizeValue = theSizeValue - 8;     }     var theDoc = fw.openDocument(theFile,true);   //opens file as new     theDoc.setMatteColor(true, theBG);     var theWidth = theDoc.width;     var theHeight = theDoc.height;     if (theSizeOption == "width") {       theSizeValue = theSizeValue / theWidth;     }     if (theSizeOption == "height") {        theSizeValue = theSizeValue / theHeight;     }     if (theSizeOption == "scale") {        theSizeValue = theSizeValue / 100;     }     theDoc.selectAll();     theDoc.scaleSelection(theSizeValue, theSizeValue,     "autoTrimImages transformAttributes");     if (theBevelOption == "true" && theShadowOption == "true")     {//do both        fw.getDocumentDOM().applyEffects({ category:"Untitled",        effects:[ { AngleSoftness:3, BevelContrast:75, BevelType:0,        BevelWidth:10, ButtonState:0, DownBlendColor:"#0000003f",        EdgeThreshold:0, EffectIsVisible:true,        EffectMoaID:"{7fe61102-6ce2-11d1-8c76000502701850}",        EmbossFaceColor:"#ffffff00", GlowStartDistance:0,        GlowWidth:0, HiliteColor:"#ffffff",        HitBlendColor:"#ffffff3f", LightAngle:135,        LightDistance:100, MaskSoftness:0,        OuterBevelColor:"#df0000", ShadowColor:"#000000",        ShowObject:false, SlopeMultiplier:1, SlopeType:0,        category:"Inner Bevel", name:"Inner Bevel" },        { EffectIsVisible:true, EffectMoaID:"{a7944db8-6ce2-11d1-       8c76000502701850}", ShadowAngle:315, ShadowBlur:4,        ShadowColor:"#000000a5", ShadowDistance:7, ShadowType:0,         category:"Shadow and Glow", name:"Drop Shadow" } ],        name:"Untitled" });     } else if (theBevelOption == "true" && theShadowOption !=     "true") {        theDoc.applyEffects({ category:"Untitled", effects:[ {        AngleSoftness:3, BevelContrast:75, BevelType:0,        BevelWidth:10, ButtonState:0, DownBlendColor:"#0000003f",        EdgeThreshold:0, EffectIsVisible:true,        EffectMoaID:"{7fe61102-6ce2-11d1-8c76000502701850}",        EmbossFaceColor:"#ffffff00", GlowStartDistance:0,        GlowWidth:0, HiliteColor:"#ffffff",        HitBlendColor:"#ffffff3f", LightAngle:135,        LightDistance:100, MaskSoftness:0,        OuterBevelColor:"#df0000", ShadowColor:"#000000",        ShowObject:false, SlopeMultiplier:1, SlopeType:0,        category:"Inner Bevel", name:"Inner Bevel" } ],        name:"Untitled" });     } else if (theShadowOption == "true" && theBevelOption !=     "true") {        theDoc.applyEffects({ category:"Untitled", effects:[ {        EffectIsVisible:true, EffectMoaID:"{a7944db8-6ce2-11d1-       8c76000502701850}", ShadowAngle:315, ShadowBlur:4,        ShadowColor:"#000000a5", ShadowDistance:7, ShadowType:0,        category:"Shadow and Glow", name:"Drop Shadow" } ],        name:"Untitled" });     }     theDoc.setDocumentCanvasSizeToDocumentExtents(true); //allows     the canvas to grow     var theExt = theFile.substr(theFile.lastIndexOf('.'));     theFile = theFile.substr(0,theFile.lastIndexOf('.')) +     "_small";     if (theExt.toLowerCase().indexOf('gif') != -1) {        theDoc.setExportOptions({ exportFormat:"GIF",        paletteTransparency:"index"});     }     if (theExt.toLowerCase().indexOf('jp') != -1) {        theDoc.setExportOptions({ exportFormat:"JPEG"});     }     fw.saveDocument(theDoc,theFile + ".png");     fw.exportDocumentAs(null, theFile, null);     fw.closeDocument(theDoc,false);     return theMsg;  }  function runCommand() {     //Get the DOM of the user's document     var theDom = dw.getDocumentDOM();     var theBG = theDom.body.bgcolor?theDom.body.bgcolor:"#ffffff";     var theSizeOption, theSizeValue;     var theBevelOption = "false";     var theShadowOption = "false";     var offsets = theDom.getSelection();     var theSelObj = dreamweaver.offsetsToNode(offsets[0],offsets[1]);     var theFileBase = theSelObj.getAttribute("src");     var jspwFileBase = theFileBase;     var theFileURL = getFullPath(theFileBase);     var thePath = "";     if (theFileBase.lastIndexOf("/") != -1) {        thePath =theFileBase.substr(0,theFileBase.lastIndexOf("/"));        theFileBase = theFileBase.substr(theFileBase.lastIndexOf("/"));     }     if (theSizeChoice[0].checked) {        theSizeOption = "width";        theSizeValue = findObject("widthText").value;        findObject("heightText").value = "";        findObject("percentageText").value = "";        if (theSizeValue == ""  isNaN(theSizeValue)) {           alert("Please enter a numeric value in the Width field.");            findObject("widthText").focus();           return;        }     }     if (theSizeChoice[1].checked) {        theSizeOption = "height";        theSizeValue = findObject("heightText").value;        findObject("widthText").value = "";        findObject("percentageText").value = "";        if (theSizeValue == ""  isNaN(theSizeValue)) {           alert("Please enter a numeric value in the Height           field.");           findObject("heightText").focus();           return;        }     }     if (theSizeChoice[2].checked) {        theSizeOption = "scale";        theSizeValue = findObject("percentageText").value;        findObject("widthText").value = "";        findObject("heightText").value = "";        if (theSizeValue == ""  isNaN(theSizeValue)) {           alert("Please enter a numeric value in the Scale           field.");           findObject("scaleText").focus();           return;        }     }     if (findObject("bevelCB").checked) {        theBevelOption = "true";        }     if (findObject("dropshadowCB").checked) {        theShadowOption = "true";     }     var thePicWin = 1;     getNotesPicWindow();     if (findObject('targetRB')[0].checked) {        var jspwStr="JustSoPicWindow(" + jspwFileBase + "," +        picWinWidth + "," + picWinHeight + "," + picWinAlt + "," +        picWinColor + "," + picWinHugger + "," + picWinHugMargin + ")";        if (thePicWin) thePicWin =        dw.popupAction("JustSoPicWindow.htm",jspwStr);        if (thePicWin == jspwStr) {// when the user Canceled jspw           return;        }     }     result = execFunctionInFireworks(fw_makeThumbnail, theFileURL,     theSizeOption, theSizeValue, theBevelOption, theShadowOption,     theBG);     // If execFunctionInFireworks returned NULL, everything went OK;     // time to insert the images     if (result == "OK"){        var theExt = theFileBase.substr(theFileBase.lastIndexOf('.'));        var theNewFile = theFileBase.substr        (0,theFileBase.lastIndexOf('.')) + "_small";        theNewFile = thePath + theNewFile + theExt;        theNewFileURL = getFullPath(theNewFile);        var theSizeArray = dw.getNaturalSize(theNewFileURL);        var newID = makeUniqueName('IMG','tbb_')        theSelObj.setAttribute("id",newID);        theSelObj.setAttribute("src",theNewFile);        theSelObj.setAttribute("width",theSizeArray[0]);        theSelObj.setAttribute("height",theSizeArray[1]);        var theBorder = "0";        if (findObject('htmlCB').checked) {           theBorder = "1";           if (findObject('htmlText').value) {              theBorder = findObject('htmlText').value;            }        }        theSelObj.setAttribute("border",theBorder);        var theCode = theSelObj.innerHTML;        if (findObject('targetRB')[0].checked) {//Just So Pic Win           theSelObj.outerHTML = '<a href="javascript:;" onClick="'           + thePicWin + ';return document.MM_returnValue' +           '">' + theCode + '</a>';        } else {           theSelObj.outerHTML = '<a href="' + thePath +           theFileBase + '">' + theCode + '</a>';        }     }     saveRadioPreferences('sizeRB','targetRB');     saveTextPreferences('widthText','heightText','percentageText',     'htmlText','bevelText');     saveBoxesPreferences('bevelCB','htmlCB','dropshadowCB');     window.close();     FWLaunch.bringDWToFront();     return;  }  function saveTextPreferences(){//1.0     if (typeof MMNotes == 'undefined') {return;} // Check for     MMNotes extension.     var theSettings,metaFile;     theSettings = saveTextPreferences.arguments;     metaFile = MMNotes.open(document.URL, true);     if (metaFile) {        for (var i=0; i<theSettings.length; i++){           var settingName;           //Assemble the name of the key inside the Design note           settingName = "pref_"+theSettings[i];           //Set the value of the key           MMNotes.set(metaFile,settingName,findObject           (theSettings[i]).value);        }        MMNotes.close(metaFile);     }  }  //Get an array of text form elements and retrieve their setting  //Then, populate the elements with the relevant values  function getTextPreferences(){//1.0     if (typeof MMNotes == 'undefined') {return;}     // Check for MMNotes extension.     var theSettings,metaFile;     theSettings = getTextPreferences.arguments;     metaFile = MMNotes.open(document.URL, true);     if (metaFile) {        for (var i=0; i<theSettings.length; i++){           var settingName,settingValue;           //Assemble the name of the key inside the Design note           settingName = "pref_"+theSettings[i];           //Get the value of the key           settingValue = MMNotes.get(metaFile,settingName);           //If we have a saved setting           if(settingValue){              //Set the status of the form element              findObject(theSettings[i]).value = settingValue;           }        }        MMNotes.close(metaFile);     }  }  //Get an array of check boxes and save their setting  //Settings names are pref_nameOfFormElement  function saveBoxesPreferences(){//1.0     if (typeof MMNotes == 'undefined') {return;} // Check for     MMNotes extension.     var theSettings,metaFile;     theSettings = saveBoxesPreferences.arguments;      metaFile = MMNotes.open(document.URL, true);     if (metaFile) {        for (var i=0; i<theSettings.length; i++){           var settingName;           //Assemble the name of the key inside the Design note           settingName = "pref_"+theSettings[i];           //Set the value of the key  MMNotes.set(metaFile,settingName,findObject(theSettings[i]).checked);         }         MMNotes.close(metaFile);     }  }  //Get an array of checkboxes form elements and retrieve their  setting  //Then, change the status of the elements  function getBoxesPreferences(){//1.0     if (typeof MMNotes == 'undefined') {return;} // Check for     MMNotes extension.     var theSettings,metaFile;     theSettings = getBoxesPreferences.arguments;     metaFile = MMNotes.open(document.URL, true);     if (metaFile) {        for (var i=0; i<theSettings.length; i++){           var settingName,settingValue;           //Assemble the name of the key inside the Design note           settingName = "pref_"+theSettings[i];           //Get the value of the key           settingValue = MMNotes.get(metaFile,settingName);           //If we have a saved setting           if(settingValue){              //Set the status of the form element              findObject(theSettings[i]).checked = eval(settingValue);           }        }        MMNotes.close(metaFile);     }  }  //Get an array of radio form elements and save their setting  //Settings names are pref_nameOfFormElement  function saveRadioPreferences(){//1.3     if (typeof MMNotes == 'undefined') {return;} // Check for     MMNotes extension.     var theSettings,metaFile;     theSettings = saveRadioPreferences.arguments;     metaFile = MMNotes.open(document.URL, true);     if (metaFile) {        for (var i=0; i<theSettings.length; i++){           var settingName;           //Assemble the name of the key inside the Design note           settingName = "pref_"+theSettings[i];           //Find the selected element and set the value of the key           for (var h=0; h<findObject(theSettings[i]).length; h++){              if (findObject(theSettings[i])[h].checked){                 MMNotes.set(metaFile,settingName,h);              }           }        }        MMNotes.close(metaFile);     }  }  //Get an array of radio form elements and retrieve their setting  // change the status of the elements  function getRadioPreferences(){//1.3     if (typeof MMNotes == 'undefined') {return;} // Check for     MMNotes extension.     var theSettings,metaFile;     theSettings = getRadioPreferences.arguments;      metaFile = MMNotes.open(document.URL, true);     if (metaFile) {        for (var i=0; i<theSettings.length; i++){           var settingName,settingValue;           //Assemble the name of the key inside the Design note           settingName = "pref_"+theSettings[i];           //Get the value of the key           settingValue = MMNotes.get(metaFile,settingName);           //If we have a saved setting           if(settingValue){              //Set the status of the form element              findObject(theSettings[i])[settingValue].checked =              true;           }        }        MMNotes.close(metaFile);     }  }  function checkField(theFieldName) {  /*   activates onBlur     checks to see if current text field is empty or with value     if it's with a value, zeros out other text fields and     sets the radio button  */     var theField = findObject(theFieldName);     if (theField.value != "") {        if (theFieldName == "widthText") {           findObject("heightText").value = "";           findObject("percentageText").value = "";           findObject("sizeRB")[0].checked = true;        }        if (theFieldName == "heightText") {           findObject("widthText").value = "";           findObject("percentageText").value = "";           findObject("sizeRB")[1].checked = true;        }        if (theFieldName == "percentageText") {           findObject("heightText").value = "";           findObject("widthText").value = "";           findObject("sizeRB")[2].checked = true;        }     }     updateSize();  }  function updateSize() {     var theNewWidth, theNewHeight;     var offsets = dw.getDocumentDOM().getSelection();     var theSelObj = dreamweaver.offsetsToNode(offsets[0],offsets[1]);     var theOldSize = dw.getNaturalSize(getFullPath     (theSelObj.getAttribute("src")));     var theNewSizeText = document.getElementsByTagName('newsize');     if (theSizeChoice[0].checked) {//width        theNewWidth = parseInt(findObject("widthText").value);        theNewHeight = parseInt((theNewWidth / parseInt        (theOldSize[0])) * parseInt(theOldSize[1]));     }     if (theSizeChoice[1].checked) {//height        theNewHeight = parseInt(findObject("heightText").value);        theNewWidth = parseInt((theNewHeight /        parseInt(theOldSize[1])) * parseInt(theOldSize[0]));     }     if (theSizeChoice[2].checked) {//percentage        var thePercent = parseInt(findObject("percentageText").value);        theNewWidth = parseInt((thePercent / 100) * theOldSize[0]);        theNewHeight = parseInt((thePercent / 100) * theOldSize[1]);     }     if (!(findObject("widthText").value == "" &&  findObject("heightText").value == "" &&  findObject("percentageText").value == "")) {//nothing to show first  time         theNewSizeText[0].innerHTML = "Thumbnail: <b>W: " +        theNewWidth + " &nbsp;&nbsp;H: " + theNewHeight + "</b>";     }  }  function getNotesPicWindow(){     if (parseInt(dw.appVersion) != 3){        var handle=MMNotes.open(dw.getSiteRoot()+"vwd.mno",true) ;        var PicWindowItems = MMNotes.get(handle,"picwindow");        MMNotes.close(handle);        PicWindowItems = PicWindowItems.split("_vwd_");        if (PicWindowItems[0]) {           picWinHugger = PicWindowItems[0] ;           picWinHugMargin  = PicWindowItems[1] ;           picWinAlt = PicWindowItems[2] ;           picWinColor = PicWindowItems[3]  ;        } else {           picWinHugger =  "fullscreen";           picWinHugMargin  = "0";           picWinAlt = "* * Click screen to close * *";           picWinColor = "#336699";        }        picWinWidth = theCurrentSize[0];        picWinHeight = theCurrentSize[1];     }  }  function initUI() {       // Check for FW4; if not present, close the command dialog     if (dw.getDocumentPath("document") == "" ) {        alert("Please save document before using this command.")        window.close();     }     var theVersion = dw.appVersion.substr(0,4);     if (parseFloat(theVersion) < 6.00) {        alert("You need Dreamweaver MX or higher for this        command.");        window.close;     }     if (!FWLaunch.validateFireworks(3.0)){        alert("You need Fireworks 3 to run this command.");        window.close();     } else {        if (!DWfile.exists(dw.getConfigurationPath() +        "/Behaviors/Actions/JustSoPicWindow.htm")) {           SetEnabled(findObject("targetRB")[0],false);           findObject("targetRB")[1].checked = true        } else {           SetEnabled(findObject("targetRB")[0],true);        }        var offsets = dw.getDocumentDOM().getSelection();        var theSelObj = dreamweaver.offsetsToNode        (offsets[0],offsets[1]);        if (theSelObj.getAttribute("id")) {           if (theSelObj.getAttribute("id").substr(0,4) == "tbb_") {              alert("I like infinite regression as much as the next              fellow, \nbut I'm afraid you can't make a thumbnail of              a thumbnail.\n\nPlease choose a different image.")              window.close();           }        }        theCurrentSize = dw.getNaturalSize(getFullPath        (theSelObj.getAttribute("src")));        var theHeading = document.getElementsByTagName        ('currentSize');        theHeading[0].innerHTML = "Current Image: W: " +        theCurrentSize[0] + " &nbsp;&nbsp;H: " + theCurrentSize[1]         getRadioPreferences('sizeRB','targetRB');  getTextPreferences('widthText','heightText','percentageText','html  Text','bevelText');        getBoxesPreferences('bevelCB','htmlCB','dropshadowCB');        updateSize();     }  } 

First, we need to make sure that an image is actually selected. If the user does not have an image selected, we want the menu item for the command to appear inactive. To achieve this, we need only use one of the special Command API functions: canAcceptCommand() . The following code verifies first there is a selection, second, that the selected item is a tag of some kind (that is, not text or a comment) and, finally, that the selected item is an <img> tag.

 function canAcceptCommand() {     var theDom = dw.getDocumentDOM();     var selection = dreamweaver.getSelection();     var theSel = dreamweaver.offsetsToNode( selection[0],     selection[1] );     return(theSel != null && theSel.nodeType == Node.ELEMENT &&     theSel.tagName == "IMG");  } 

If canAcceptCommand() returns true , the menu item is available; if it returns false , the item is unselectable.

Setting Up the Buttons

Next, we need to set up the primary buttons to execute the command, cancel, or get help. Again, a special Command API function is employed: commandButtons() . The commandButtons() function uses a simple array of name-value pairs. Each pair consists of the buttons label and the function that is executed when that button is selected.

 function commandButtons(){      return new Array( 'OK', 'runCommand()', 'Help', 'doHelp()',      'Cancel', 'window.close()');  } 

The function is either a custom one, like those for OK and Help, or a builtin JavaScript function, like window.close() for Cancel.

For any extension other than those intended for my own use, I always try to provide some form of help. Not only is help the essence of user friendliness, but it also cuts down on the email queries I have to field. For simple extensions, my doHelp() function opens an alert with a brief explanation. For more complex explanations , I prefer to host a page on one of my web sites. The page can contain far more extensive help than an alert, and it is easily modified as required.

 function doHelp() {  dw.browseDocument('http://www.fp2dw.com/extensions/tbhelp.htm');  } 

The browseDocument() function opens the designated page in the user's primary browser.

Initializing the User Interface

So far in this analysis, I've mentioned a couple of functions that are called during initialization. Let's take a closer look at what else is going on in the initUI() function.

The first part of initUI() is devoted to error checking. First, we need to make sure the file is saved to avoid pathname hassles when storing the new thumbnail.

 if (dw.getDocumentPath("document") == "" ) {     alert("Please save document before using this command.")     window.close();  } 

Next, we need to verify that an appropriate version of Dreamweaver is running:

graphics/07icon06.gif

NOTE

The reason that Dreamweaver 4.01 or higher is specified rather than just version 4.0 is because 4.01, aside from fixing some bugs , included a function key to this extension, dw.getNaturalSize() , which is used to find the dimensions of the selected image.


Finally, we want to make sure that a capable version of Fireworks is in use. Fireworks 3 will do just fine:

 if (!FWLaunch.validateFireworks(3.0)){     alert("You need Fireworks 3 to run this command.");     window.close(); 

The next bit of code checks to see whether the Just So Picture Window behavior is installed. If it is not, the appropriate radio button is disabled and the other choice is selected. If the behavior is found, the Just So Picture Window radio button is enabled.

graphics/07icon07.gif

During the beta test period, one of my testers decided to see what would happen if the extension was applied to an existing thumbnail. Let me just say it wasn't pretty, and it spawned the following bit of error-checking code. Here, I check to see whether the selected object has an id attribute that starts with tbb_ , an identifier I place in all created thumbnails. If so, I respectfully request that the user make another choice and close the extension.

graphics/07icon08.gif

The next section of initUI() displays the selected image's height and width as discussed earlier. I included it here for the sake of completeness.

 theCurrentSize = dw.getNaturalSize(getFullPath  (theSelObj.getAttribute("src")));  var theHeading = document.getElementsByTagName('currentSize');  theHeading[0].innerHTML = "Current Image: W: " + theCurrentSize[0]  + " &nbsp;&nbsp;H: " + theCurrentSize[1] 

The next couple of code lines represent to me one of the hallmarks of a good extension. In the interest of continued ease of use, most of my extensions "remember" the settings from the previous session whenever possible. I've found that most often, the user applies similar if not the same settings time and again. To this end, I use a series of functions developed by Massimo Foti, which get the previous choices (the "preferences") and store them in a Design Note attached to the extension. Notice that I need only pass the names of the elements to load their prior values.

 getRadioPreferences('sizeRB','targetRB');  getTextPreferences('widthText','heightText','percentageText',  'htmlText','bevelText');  getBoxesPreferences('bevelCB','htmlCB','dropshadowCB'); 

When Thumbnail Builder is executed, a parallel set of functions stores the user's current choices.

Writing the Fireworks Function

Now we come to the heart of the extension: the Fireworks function. This function is exactly as Fireworks; if you were to save this function as a separate .jsf file and supply the necessary arguments, Fireworks would execute it with no problem. This functionality is a major productivity boon. In essence, you can build and test the Fireworks function separately within Fireworks and, when it is completed, copy and paste it its entirety into your Dreamweaver extension.

Although you're under no obligation to do so, Fireworks functions in Dreamweaver traditionally start with fw_ . My function, fw_makeThumbnail() , takes six arguments:

  • theFile The filename of the image that is selected in Dreamweaver, expressed as a file URL (that is, file:/// clients /bigco/images/newproduct.gif )

  • theSizeOption The type of sizing that is chosen; can be width, height, or scale (percentage)

  • theSizeValue The value that is entered into the related text field

  • theBevelOption A boolean indicating whether Bevel was selected

  • theShadowOption A boolean indicating whether Drop Shadow was selected

  • theBG The background color of the Dreamweaver document that is necessary if the Drop Shadow option was chosen

The first line of code in the Fireworks function is extremely simple but vital :

 var theMsg = "OK"; 

No matter what, theMsg is eventually returned to Dreamweaver and, if theMsg is "OK" , the Fireworks processing was successful. If the variable is set to something else, then something went wrongeither an error was encountered or the user cancelled the operation. Dreamweaver knows how to interpret the error codes and reacts appropriately. These provided error-handling routines are all in the FwExec.js and the FwExecDlg.htm files. Needless to say, these routines greatly simplify the programming life of the cross-application developer.

Although the next line might not make such an impact, it is key to writing Fireworks functions. With this code

 theSizeValue = parseInt(theSizeValue); 

the value passed in the argument theSizeValue is converted to a number. All values passed from Dreamweaver to Fireworks are received as strings. If the values are needed as numeric values, they must be converted to a number within the Fireworks function.

Next, we check to see whether the Drop Shadow option was selected and if either the Height or Width size methods were specified. If so, we reduce the dimensions slightly to compensate for the size of the drop shadow.

 if (theShadowOption == "true" && theSizeOption != "scale") {     theSizeValue = theSizeValue - 8;  } 

Frankly, this routine is purely a judgment call. I figure that when someone says he wants the thumbnail to be 125 pixels, he wants the whole image to be 125 pixels, not 133 pixels including the drop shadow. As an extension developer, sometimes you're called on to make a best guess on the desired behavior.

We're ready now to open the selected image in Fireworks and start work. After we load the file, making sure it is not the original but a copy, we set the matte color and determine the dimensions:

graphics/07icon09.gif

Then we calculate theSizeValue variable according to the size option chosen. Note that theSizeValue is actually expressed as a percentage of the overall image. Fireworks requires percentages to properly scale an image.

 if (theSizeOption == "width") {     theSizeValue = theSizeValue / theWidth;  }  if (theSizeOption == "height") {     theSizeValue = theSizeValue / theHeight;  }  if (theSizeOption == "scale") {     theSizeValue = theSizeValue / 100;  } 

Now, we're ready to resize the image to our desired dimensions. After first making sure that our image is selected, we apply the scaling function:

 theDoc.selectAll();  theDoc.scaleSelection(theSizeValue, theSizeValue, "autoTrimImages  transformAttributes"); 

Why is theSizeValue repeated? In the scaleSelection() function, the first two arguments are the percentages of the desired width and height. Although you could scale these dimensions independently in Fireworks, we want to preserve the width to height ratio, so we use the same value.

The following code section makes up the bulk of the fw_makeThumbnail() function. In this series of if-then-else statements, the various effects are applied.

 if (theBevelOption == "true" && theShadowOption == "true") {  //do both     fw.getDocumentDOM().applyEffects({ category:"Untitled",     effects:[ { AngleSoftness:3, BevelContrast:75, BevelType:0,     BevelWidth:10, ButtonState:0, DownBlendColor:"#0000003f",     EdgeThreshold:0, EffectIsVisible:true, EffectMoaID:"{7fe61102-    6ce2-11d1-8c76000502701850}", EmbossFaceColor:"#ffffff00",     GlowStartDistance:0, GlowWidth:0, HiliteColor:"#ffffff",     HitBlendColor:"#ffffff3f", LightAngle:135, LightDistance:100,     MaskSoftness:0, OuterBevelColor:"#df0000",     ShadowColor:"#000000", ShowObject:false, SlopeMultiplier:1,     SlopeType:0, category:"Inner Bevel", name:"Inner Bevel" },     { EffectIsVisible:true, EffectMoaID:"{a7944db8-6ce2-11d1-    8c76000502701850}", ShadowAngle:315, ShadowBlur:4,     ShadowColor:"#000000a5", ShadowDistance:7, ShadowType:0,     category:"Shadow and Glow", name:"Drop Shadow" } ],     name:"Untitled" });  } else if (theBevelOption == "true" && theShadowOption != "true") {     theDoc.applyEffects({ category:"Untitled", effects:[ {     AngleSoftness:3, BevelContrast:75, BevelType:0, BevelWidth:10,     ButtonState:0, DownBlendColor:"#0000003f", EdgeThreshold:0,     EffectIsVisible:true, EffectMoaID:"{7fe61102-6ce2-    11d1-8c76000502701850}", EmbossFaceColor:"#ffffff00",     GlowStartDistance:0, GlowWidth:0, HiliteColor:"#ffffff",     HitBlendColor:"#ffffff3f", LightAngle:135, LightDistance:100,     MaskSoftness:0, OuterBevelColor:"#df0000",     ShadowColor:"#000000", ShowObject:false, SlopeMultiplier:1,     SlopeType:0, category:"Inner Bevel", name:"Inner Bevel" } ],     name:"Untitled" });  } else if (theShadowOption == "true" && theBevelOption != "true") {     theDoc.applyEffects({ category:"Untitled", effects:[ {     EffectIsVisible:true, EffectMoaID:"{a7944db8-6ce2-11d1-    8c76000502701850}", ShadowAngle:315, ShadowBlur:4,     ShadowColor:"#000000a5", ShadowDistance:7, ShadowType:0,     category:"Shadow and Glow", name:"Drop Shadow" } ],     name:"Untitled" });  } 

Although these lines might seem overwhelming, they were among the easiest lines to code. Each applyEffects() function was copied from Fireworks' history panel and pasted directly into this function. To simplify matters, the default values were used; it is entirely possible, however, to substitute user-supplied values for attributes such as ShadowColor and BevelWidth .

Now that the graphic is rescaled and all the effects are applied, we fit the canvas to the image with the next function:

 theDoc.setDocumentCanvasSizeToDocumentExtents(true); 

After a few string manipulations to add a "_small" addendum to the filename

 var theExt = theFile.substr(theFile.lastIndexOf('.'));  theFile = theFile.substr(0,theFile.lastIndexOf('.')) + "_small"; 

we're ready to set the export options. We use the extensions we retrieved to determine whether the file is GIF or JPEG format so that we can export the thumbnail in the same format.

graphics/07icon10.gif

The final four lines of the function are vital. After saving the image in the Fireworks native PNG format (so that modifications can be made easily), we export the image, close the file we created, and return our "OK" message:

graphics/07icon11.gif

Executing the Thumbnail Builder Command

With all of our support function declared, we're ready to write the primary function that executes the Thumbnail Builder command. Because this function is fairly involved, let's take a moment to review its structure before we launch into the details:

In order of appearance, the primary code sections are as follows :

  1. Define the local variables .

  2. Get the image filename.

  3. Set the argument variables according to user selections.

  4. Execute the Fireworks function that is passing the argument variables.

  5. Rewrite the HTML tag for the image if the Fireworks function processes without a problem.

  6. Save the current user settings.

  7. Finish up and close the dialog box.

As with many functions, the opening lines of the runCommand() function define needed local variables:

graphics/07icon12.gif

The second line of code merits a bit of explanation:

 var theBG = theDom.body.bgcolor?theDom.body.bgcolor:"#ffffff"; 

In this line, I am declaring a variable to hold the background color of the current Dreamweaver document. If no background color is defined, in the body tag, I set the background to white ( #ffffff ).

NOTE

You should realize that the background color is not properly picked up if it is set using a CSS style declaration, either internally or in an external style sheet. If the user selects the drop shadow option and has a CSS-type background color declared (other than white), the source PNG file will probably need to be modified to export the proper thumbnail.


The next section of code gets the file path of the selected image and then extracts just the filename. You might notice that I use the filename in two variables: jspwFileBase and theFileBase . The jspwFileBase variable is used to pass the full path of the image to the Just So Picture Window behavior while the Fireworks function uses the other variable.

 var offsets = theDom.getSelection();  var theSelObj = dreamweaver.offsetsToNode(offsets[0],offsets[1]);  var theFileBase = theSelObj.getAttribute("src");  var jspwFileBase = theFileBase;  var theFileURL = getFullPath(theFileBase);  var thePath = "";  if (theFileBase.lastIndexOf("/") != -1) {     thePath = theFileBase.substr(0,theFileBase.lastIndexOf("/"));     theFileBase = theFileBase.substr(theFileBase.lastIndexOf("/"));  } 

The following three sections of code set the local variables to the various user-selected options. In the first section, the size option (width, height, and percentage) is examined. When the selected option is identified, the related value is inserted into theSizeValue variable and the other two values are set to empty strings. If the passed value is not a number, then the user is informed of the error and returned to correct the problem.

 if (theSizeChoice[0].checked) {     theSizeOption = "width";     theSizeValue = findObject("widthText").value;     findObject("heightText").value = "";     findObject("percentageText").value = "";     if (theSizeValue == ""  isNaN(theSizeValue)) {        alert("Please enter a numeric value in the Width field.");        findObject("widthText").focus();        return;     }  }  if (theSizeChoice[1].checked) {     theSizeOption = "height";     theSizeValue = findObject("heightText").value;     findObject("widthText").value = "";     findObject("percentageText").value = "";     if (theSizeValue == ""  isNaN(theSizeValue)) {        alert("Please enter a numeric value in the Height field.");        findObject("heightText").focus();        return;     }  }  if (theSizeChoice[2].checked) {     theSizeOption = "scale";     theSizeValue = findObject("percentageText").value;     findObject("widthText").value = "";     findObject("heightText").value = "";     if (theSizeValue == ""  isNaN(theSizeValue)) {        alert("Please enter a numeric value in the Scale field.");        findObject("scaleText").focus();        return;     }  } 

The second set of user options looks to see what effects, if any, should be passed to Fireworks. The HTML option, if selected, is handled within Dreamweaver.

 if (findObject("bevelCB").checked) {     theBevelOption = "true";  }  if (findObject("dropshadowCB").checked) {     theShadowOption = "true";  } 

The following code block is a bit out of the normbut all the more interesting for it. Here, after assembling the proper arguments, an entirely different extensionthe Just So Picture Window behavioris run via the popupAction() API call. Passing the string of arguments, jspwStr ,is significant because it pre-fills the dialog box for the behavior as shown in Figure 7.7. This technique saves the user from having to select the image filename as well as enter other options. In most cases, the user only needs to verify the selections and click OK.

Figure 7.7. The popupAction API called is used to run a separate extension, automatically filling in the relevant user selections.

graphics/07fig07.gif

graphics/07icon13.gif

The next line of code puts the whole cross-application extension into action:

 result = execFunctionInFireworks(fw_makeThumbnail, theFileURL,  theSizeOption, theSizeValue, theBevelOption, theShadowOption,  theBG); 

You'll recall that the execFunctionInFireworks() call is stored in the included FwExec.js file. If the returned result is anything but "OK" , the user is alerted to the specific error. If we're given the "OK" , the next code block executes setting up the filename for the newly created thumbnail and gathering a few of its attributes (the dimensions and a unique ID).

graphics/07icon14.gif

All of the thumbnail attributes are set next: id , src , width , and height .If the HTML border option is selected, a one-pixel border is added by default. Should the user supply a different border width, that value is substituted. The final line in this section prepares the new <img> tag for the thumbnail to be written:

 theSelObj.setAttribute("id",newID);  theSelObj.setAttribute("src",theNewFile);  theSelObj.setAttribute("width",theSizeArray[0]);  theSelObj.setAttribute("height",theSizeArray[1]);  var theBorder = "0";  if (findObject('htmlCB').checked) {     theBorder = "1";     if (findObject('htmlText').value) {        theBorder = findObject('htmlText').value;     }  }  theSelObj.setAttribute("border",theBorder);  var theCode = theSelObj.innerHTML; 

In addition to the new <img> tag, a link to the original image is needed also and is inserted into the following code:

graphics/07icon15.gif

This link can take one of two forms. If the Just So Picture Window option is selected, the link looks something like this:

 <a href="javascript:;"  onClick="JustSoPicWindow('images/SilverCoral.jpg','296','224','Clic  k to close','#EFEBEF','hug image','20');return  document.MM_returnValue"><img src="images/SilverCoral_small.jpg"  width="133" height="102"></a> 

Otherwise, the link takes this form:

 <a href="images/SilverCoral.jpg"><img src="images/  SilverCoral_small.jpg" width="133" height="102"></a> 

We're almost done. The next section stores all the user's selections in the Design Note so that they are available the next time the command is run.

 saveRadioPreferences('sizeRB','targetRB');  saveTextPreferences('widthText','heightText','percentageText','htm  lText','bevelText');  saveBoxesPreferences('bevelCB','htmlCB','dropshadowCB'); 

The final three lines are pure cleanup. The dialog box is closed, Dreamweaver is brought in front of Fireworks, and we end the function with a return.

TIP

If you work with Fireworks extensions either within Fireworks or combining both Dreamweaver and Fireworksyou'll need to use the Extending Fireworks manual. It's available in PDF format by choosing Help, Extending Fireworks. As of this writing, Macromedia is planning to have hard copy editions available at the online store.


 window.close();  FWLaunch.bringDWToFront();  return; 


Joseph Lowery's Beyond Dreamweaver
Joseph Lowerys Beyond Dreamweaver
ISBN: B000H2MWYS
EAN: N/A
Year: 2001
Pages: 87
Authors: Joseph Lowery

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net