| # Practical scripts This section will show examples on how scripts can enhance your album in really cool ways. The examples here makes use of the huge library of ready-made java classes that Sun has provided for free and are directly accessible. To better understand the examples and to assist in the writing of your own scripts I strongly recommend that you first read the creating skins section, then bookmark the basic Java API from Sun and use it to look up classes and their methods. If you are new to Java I recommend tutorials and that you then concentrate on the "java.lang", "java.util" and "java.io" packages. They are the ones that are most commonly used. Remenber that you can turn to the forum to get help and help others! If you don't have any programming ambitions, fine. Just copy and paste the examples below to the slide.htt and index.htt files of the skin you wish to enhance. There is a tutorial that covers editing these files that you can check out first. Extra slide pages for original images Alternative album titles Adding voice annotations Converting focal length to 35mm equivalent Google-like links to images Multilevel parent links Reading captions/comments from separate text files Thumbnail to indicate sub album ### Extra slide pages for original images When clicking on an image in a slide show, you may get to the original image, but it is not displayed in a html page of its own. The downside of this is that the surroundings for the image doesn't match the skin (usually displayed on white background) and you have to use the back navigation button to return. Take a look at the new "Smart" skin in the extras section that addresses this issue by some lines of BeanShell scripting. The skin has an extra template file called "originalslide.htt". The following simple adjustment to the "slide.htt" file will make sure that the "originalslide.htt" file gets processed if needed:
    <!-- Image, maybe with link to original -->
    <ja:if exists=originalPath>
    <!-- Create a slide page for the original image too
    and link to that one instead of linking to an image -->
    <%
      String originalPage = originalPath;  // Default if no extra template page
      File template = new File(skinDirectory, "originalslide.htt");
      if (template.exists()) {
        originalPage = label+"_orig"+engine.getPageExtension();
        engine.processTemplateFile(template,
         new File(outputDirectory,"slides/"+originalPage), current);
      }
    %>
      <A HREF="<%=originalPage%>">
        <IMG SRC="$imagePath" WIDTH="$imageWidth" HEIGHT="$imageHeight" BORDER=0>
      </A>
    </ja:if>
### Alternative titles for album directories If you are not happy with JAlbum selecting the directory name as title for your albums, go to the "Advanced" tab and set `title = your alternative title` as a user defined variable there. This works fine for one flat album, but if you have a deep hierarchy of directories, you will then end up having the same title on each sub album. The following script can be put at the top of the index.htt skin template file in order to have JAlbum grabbing the title from a "meta.properties" text file that you put in the directory of choice. Inside the meta.properties file you simply put `title = your alternative title` to have that title instead. If the mapping isn't present, JAlbum will revert to use the name of the directory instead.
    <%
      import se.datadosen.util.*;
      // If meta.properties exists in the image directory and contains
      // an alternative title, use it instead of the directory name
      File propsFile = new File(imageDirectory, "meta.properties");
      if (propsFile.exists()) {
        Properties props = IO.readPropertyFile(propsFile);
        String newTitle = props.get("title");
        if (newTitle != null) title = newTitle;
      }
    %>
There is one final thing to this: You will have to replace all occurances of $title with <%=title%> in that file too. (This is because $variables are expanded before scripts are being executed) ### Adding voice annotations Many digital cameras allow you to add voice annotations to images. The camera usually puts a wav file next to the image bearing the same base name as the image. This script will make JAlbum look for these wav files and insert a BGSOUND tag if there is an annotation. Put the script just after the body tag of a slide.htt file.
    <!-- add and play voice annotations (.wav files) if they exist -->
    <%
      import se.datadosen.util.IO;
      File sound = new File(imageDirectory, label+".WAV");
      if (sound.exists()) {
        // Make a copy if needed
        String soundPath;
        if (!outputDirectory.equals(imageDirectory) && engine.isCopyOriginals()) {
          IO.copyFile(sound.getAbsolutePath(), outputDirectory, true);
          soundPath = "../" + sound.getName();
        }
        else soundPath = IO.relativePath(sound, new File(outputDirectory, "slides"));
        out.println("<BGSOUND SRC=\"" + soundPath + "\">");
      }
    %>
### Converting focal length to 35mm equivalent Some users prefer to list the focal length of the camera ($focalLength) as its 35mm equivalent value. Here are two scripts that does the conversion for you. In theory, converting is just a matter of muliplying the focal length with a factor that you can get from your camera manual (the factor varies between camera models), but in JAlbum the focalLength variable is a formatted string including "mm" at the end which can't take part in a multiplication. The follwing script takes care of that by stripping non-numeric parts of a value and converting the result to a floating point value. After that, converting is simply a matter of multiplication and rounding the result. Put these scripts into the slide.htt file of the skin you wish to use.
    <%
    // Strip non-numeric ending on strings and convert result to float
    float numericPart(String s) {
      int i;
      for (i=0; i<s.length(); i++) {
        char c = s.charAt(i);
        if (!Character.isDigit(c) && c != '.') break;
      }
      return Float.parseFloat(s.substring(0,i));
    }
    %>
...And this is the code that displays the converted value (example for a Canon PowerShot G1 camera having 4.857 as conversion factor).
    <ja:if exists="focalLength">
    35 mm equivalent: <%= (int)(numericPart(focalLength) * 4.857 + 0.5) /* PowerShot G1 */ %>
    </ja:if>
### Google-like links to images For quick navigation between images, a list of numbered links might be preferred to just having previous and next buttons. Put this script inside a slide.htt file for the Google effect. The "Bluepro" skin makes use of this effect. Script now updated to show only a set of links at a time.
    <% // Produce links to all other pages, cooler than just next, previous links
      int maxLinks = 20;  // Max number of links to display at the same time
      int start = (imageNum-1)/maxLinks*maxLinks - 1;
      if (start < 0) start = 0;
      for (int i=start; i<files.length && i<start+maxLinks+2; i++) {
        Map vars = (Map)fileVariables.get(files[i]);
        if (i+1 != imageNum) out.print("<a href=\"" + vars.get("currentPage") + "\">");
        else out.print("<b>");
        out.print(" " + (i+1));
        if (i+1 != imageNum) out.print("</a>");
        else out.print("</b>");
      }
    %>
### Multilevel parent links You might have an album with many nested folders (animals/mamals/cats...) In this case it helps a lot to have all folder names displayed like this: animals » mamals » cats (being in the "cats" folder), with links to each parent folder. Copy and paste the two scriptlets below for this effect. The first goes into index.htt and the second into slide.htt:
    <!-- Multilevel index links for index page -->
    <%
      void writePath(File dir, String prefix) {
        boolean topLevel = fileVariables.get(dir) == null;
        if (!topLevel)
          writePath(dir.getParentFile(), prefix + "../");
        if (!topLevel) out.print(" » ");
        if (prefix.length() > 0)
          out.print("<a href=\"" + prefix + engine.getIndexPageName()
           + engine.getPageExtension() + "\">");
        else out.print("<a href=\"" + indexPage + "\">");
        out.print((topLevel ? album.get("rootName") : dir.getName()) + "</a>");
      }
      if (album.get("rootName") == null) album.put("rootName", title);
      writePath(imageDirectory, "");
    %>
    <!-- Multilevel index links for slide pages -->
    <%
      void writePath(File dir, String prefix) {
        boolean topLevel = fileVariables.get(dir) == null;
        if (!topLevel)
          writePath(dir.getParentFile(), prefix + "../");
        if (!topLevel) out.print(" » ");
        if (prefix.length() > 0)
          out.print("<a href=\"../" + prefix + engine.getIndexPageName()
           + engine.getPageExtension() + "\">");
        else out.print("<a href=\"../" + indexPage + "\">");
        out.print((topLevel ? album.get("rootName") : dir.getName()) + "</a>");
      }
      if (album.get("rootName") == null) album.put("rootName", title);
      writePath(imageDirectory, "");
    %>
### Reading captions/comments from separate text files Some people wonder if it is possible to have JAlbum insert the contents of a text file having the same base name as an image but with ".txt" extension, for example "hiking.jpg" will get text from "hiking.txt". This is simple to do with the following scriptlet:
    <!-- Extract text from textfiles carrying the same base name as this image -->
    <ja:include page="<%= new File(imageDirectory, label+".txt") %>" />
### Thumbnail to indicate sub album Usually JAlbum will pick a folder icon to indicate the existence of a sub album (an album in a sub directory), but it might be better to have a thumbnail image from that directory to represent that sub album instead (randomly picked or choosen). The following script does just that. It goes in between the ja:coliterator elements of an index.htt file. The script will pick a random1 image from the sub directory unless there is a "meta.properties" file in that directory with a line indicating what file to pick instead. The format of the file is currently like this:
    folderIcon=<filename of image to use as folder icon>
If there are only movie files in the specific directory/album, the ordinary folder icon will be used instead. Excited? Here is the code to do this (requires at least JAlbum 2.8):
    <%
    // Try to replace folder icons with prepresentative image from subdirectory
    import se.datadosen.util.*;

    if (files[imageNum - 1].isDirectory()) {
      File subDir = files[imageNum - 1];
      File iconFile = null;

    // If meta.properties exists and points to an icon file, use it
      File propsFile = new File(subDir, "meta.properties");
      if (propsFile.exists()) {
        Properties props = IO.readPropertyFile(propsFile);
        String folderIcon = props.get("folderIcon");
        if (folderIcon != null && folderIcon.length() > 0) {
          iconFile = new File(subDir, folderIcon);
        }
      }
      if (iconFile == null) {
    // Try to use the first image as folder icon
        File[] list = subDir.listFiles();
        for (int i = 0; i < list.length; i++) {
          if (FileFilters.isFileSupported(list[i])) {
            iconFile = list[i];
            break;
          }
        }
      }
      if (iconFile != null) {
    // Process this image so we can extract width and height
        setAccessibility(true); // Turn on access to private methods
        engine.registerVariables(iconFile, outputDirectory); // Private = might change
        Map vars = fileVariables.get(iconFile);
    // Redefine variables
        iconPath = engine.encode(subDir.getName() + "/thumbs/" +
                                 engine.jpegName(iconFile.getName()));
        thumbPath = iconPath;
        thumbWidth = vars.get("thumbWidth");
        thumbHeight = vars.get("thumbHeight");
      }
    }
    %>
I want to add a thank you to Mark Fyvie for debugging this script for me. There is one more thing to this, usually an index.htt file contains something similar to this: `<img src="$iconPath"...`. That has to be changed to the following:
    <IMG SRC="<%=iconPath%>" WIDTH="<%=thumbWidth%>" HEIGHT="<%=thumbHeight%>" BORDER=0><BR>
The reason for this is that dollar-variables ($iconPath etc) are expanded before script execution occurs and this script redefines existing variables so we have to grab the variables using scriptlet syntax instead. 1 With random I mean that the script picks the first image file that the file system delivers when I ask for a list of files. It can be any file.