| # Extending JAlbum with new skins
You can fully customize the look and behaviour of albums generated by JAlbum by creating new "skins". A skin is a directory containing definition files and images that JAlbum will use as base for album creation. JAlbum searches for skins in the skins directory under its program directory and display them in its window for selection. Don't be put off by the technical look of this reference section. Creating or modifying skins basically requires only html skills and you may even use a web page editor if you are't that skilled in html. If you are interrested in creating our own skins but haven't read the tutorial on this. I recommend you do so first. |
### Organization of a skin The file and directory structure of a skin directory is as follows: index.htt -Template file for index pages slide.htt -Template file for slide show pages frameset.htt -Optional template file to describe albums that uses frames hints.jap -An optional layout hints file if the skin is intended to be used with a certain image size etc. File format is simply a subset of a JAlbum project file init.bsh -Optional file containing BeanShell script that is to be executed on album generation onload.bsh -Optional file containing BeanShell script that is to be executed on skin selection/loading (since v.4.1) preview.jpg -Optional preview image texts.properties -Optional file. Stores default text strings for multilingual skins res/ folder.gif -Icon used for directory links movie.gif -Icon used for movie links (additional images and files that you need. All are copied to the res directory of generated albums) styles/ (style sheet files. Will appear for selection in JAlbum. The selected file will be copied as style.css to the res directory of the generated album). Example "bright.css" Optional style-specific resource directories (same base name as style file. Example "bright"). Resource files put here have precedence over those put in the general "res" directory. (Since v4.3) Optional style specific preview images (same base name as style file. Example "bright.jpg") (Since v4.3) texts/ texts_language.properties -Optional file(s). Stores text strings for a certain language for multilingual skins. The language part of the file name is a lowercase ISO two character code Was located in the root directory of a skin prior to v4.5 plugins/ Optional Java class files specific to this skin (compiled scripts in order to gain speed for instance. Since v5.2) The template files are html files that contain certain $variables and special jalbum elements that gets expanded and interpreted as each page is produced during album creation. Note. Prior to v3.4 there was no "res" ("resources") directory, but a "gifs" directory instead. This directory was copied into each and every generated album directory for albums with directory hierarchies. As duplicating information is unnecessary, the gifs directory has been replaced with the res directory which is only copied to the root of the album directory (JAlbum first copies files from the general "res" directory, then from the optional style-specific resource directories). The gifs directory is still supported for backward compatibility. ### Steps to making a skin Copy an existing skin directory as base for your work (For example the "Minimal" skin). Rename the skin directory to something cool (the directory name is the name of the skin). Edit the template files according to the instructions in the next section Edit the images in the res directory to suit your taste Make color and font variations to your skin by adding style files to the styles directory (requires knowledge in cascading style sheets) Select the newly created skin in JAlbum (it will be detected automatically) and check the result by making an album. ### Template file variables For the template files, the following variables are defined (case is significant). Add a $ sign in front of the variables in order to have them expanded to their value. Do not add a $ sign in front of them when used for existence testing (see below).
General | Meaning
--- | ---
cols | Number of image columns on index pages
currentRows | Number of image rows on current index page
generator | Name and version of JAlbum
generatorUrl | Address to the JAlbum homepage (v4.3)
imageNum | Number of the current image within a slide
indexImageCount | Number of images on current index page
indexNum | The number of the current index page 1
internalVersion | Internal version number (since v4.1)
level | Level of album directory (0 meaning root level)
maxImageWidth | Max image width as set by user
maxImageHeight | Max image height as set by user
maxThumbWidth | Max thumbnail width as set by user
maxThumbHeight | Max thumbnail height as set by user
rows | Max number of image rows on index pages
skin | Name of current skin
style | Name of current style sheet
title | Name of album directory
totalIndexes | Total number of index pages 1
totalAlbumImages | Total number of images in an album (subdirectory images included)
totalImages | Total number of images in a directory
textEncoding | Character set and encoding of generated pages and comments
language | Define as user defined variable to explicitly set a language for a multilingual skin (ISO two character language code)
Navigation | Meaning
firstIndexPage | Filename of the first index page
lastIndexPage | Filename of the last index page
previousIndexPage | Filename of the previous index page 2
nextIndexPage | Filename of the next index page 2
parentIndexPage | Filename of parent index page 2
closeupPath | Path to get from index page to closeup image (or slide)
iconPath | Path to icon for directories and movie files. Not defined for image files
thumbPath | Path to thumbnail image
rootPath | Path to get back to the top of a multi directory level album
imagePath | Path to the image that is shown in slides
originalPath | Path to original file. Only defined if linking is "Link to originals via scaled images". If linking is "Link to originals" then imagePath links to the original image
stylePath | Path to the selected style file
styleFile | Filename of the selected style file. Deprecated. Please use stylePath instead
resPath | Path to the "res" directory containing album resources (support files like gif buttons etc)
firstPage | Filename of the first slide page
lastPage | Filename of the last slide page
previousPage | Filename of the previous slide page 2
currentPage | Filename of the current slide page
nextPage | Filename of the next slide page 2
indexPage | Filename of index page for current slide
File metadata | Meaning
label | Base name (without extension) of image/movie or folder
fileName | Full name of image/movie or folder
thumbWidth | Width of thumbnail image in pixels
thumbHeight | Height of thumbnail image in pixels
imageWidth | Width of image in pixels
imageHeight | Height of image in pixels
originalWidth | Width of original image in pixels
originalHeight | Height of original image in pixels
originalWidthDpi |
originalHeightDpi |
formatName | Type of image
compressionLevel |
fileSize | Size of original file
fileDate | Date of original file
originalDate | Date written by camera 3
currentDate | Date of album generation
resolution | Original image resolution as written by camera 3
flash | If flash was used 3
focalLength | Focal length 3
exposureTime | Exposure time 3
isoEquivalent | ISO equivalent 3
aperture | Aperture 3
focusDistance | Focus distance 3
meteringMode | Metering mode 3
cameraMake | Camera make 3
cameraModel | Camera model 3
sensorType | Camera sensor type 3
comment | Comment. Will either be extracted from (as soon as found): An external "texts.properties" file. The JPEG comment section of an image file. The IPTC caption section of an image file.3 (v4.3) The EXIF user comment secion3
keywords | IPTC keywords3 (v4.3)
category | IPTC category3 (v4.3)
author | IPTC author3 (v4.3)
copyright | IPTC copyright3 (v4.3)
Footnotes: 1 Not defined if only one index page exist 2 Only defined if such page exist 3 Only exists for images containing EXIF/IPTC data and if EXIF extraction is turned on You can list these variables and their current values by writing `<pre><%=current%></pre>` into a slide.htt template file. There are more variables accessible through special objects, among those the "meta" object, but more on that later. ### Special JAlbum elements Apart from using variables to construct dynamic content there are some special JAlbum elements that assist: #### Testing for variable existence Not all variables are available at all times. If a variable is unavailable, it will show up unexpanded in the generated page. To prevent this from happening when one can't be certain if a variable exists, test for existence with the following construct. The example adds a link to the next slide in a slide show if there are more slides, if not, the text "at last page" is displayed instead:
    <ja:if exists="nextPage">
      <A HREF="$nextPage">Next</A>
    </ja:if>
    <ja:else>
      At last page
    </ja:else>
This example test for existence of camera flash information:
    <ja:if exists="flash">Flash used: $flash</ja:if>
    <ja:else>No flash information found</ja:else>
Note: Since v3.5 the if exists construct also ensure that the variable is non null and non empty. #### General testing Any condition can be tested with the follwing syntax:
    <ja:if test="true">This will be included</ja:if>
    <ja:if test="false">This will NOT be included</ja:if>
    <ja:if test="$booleanVariable">Recommended way to test a boolean variable</ja:if>
    <ja:if test="<%=expression%>">General expression test (slower but flexible)</ja:if>
The examples above look stupid, but can be used to totally exclude a block of code. It is however more common to use a scriptlet as test attribute value (more on scripting below):
    <ja:if test="<%= engine.isHighQualityScaling() %>">
      These images have been scaled with the high quality setting
    </ja:if>
#### Page inclusion It is common to want headers/footers/navigation bars or other web elements added to the generated album so that the album better fits to the existing site. This is easily done with the include element:
    <ja:include page="C:\mysite\header.inc" />
    <ja:include page="header.inc">This text will show if the page does not exist</ja:include>
The page attribute can either be an absolute file path or a relative path. If it is relative, JAlbum will first search the image directory, then the current skin directory and finally the common "includes" directory (v4.1). To include pages relative to the output directory, use scriptlets like this:
    <ja:include page="<%=new File(outputDirectory, "header.inc")%>" />
Included pages may contain variables, scriptlets and other include elements. This example includes a text file carrying the same base name as an image. Image "foo.jpg" will get text from file "foo.txt" etc. This allows for simple comment inclusion or for adding special html to some images:
    <ja:include page="<%= new File(imageDirectory, label+".txt") %>" />
Starting from v4.5, there is also the new "once" attribute to the include element. This makes sure that the page is included one time only. This is suitable for including scriptlets with common code definitions. By only evaluating these scriptlets once, big speed gains can be achieved. Here's an example:
    <ja:include once page="commonScripts.inc" />
#### Iterator elements The index page usually have a nested pair of so called iterator elements. The content of the outer "row iterator" is repeated for each row of images and the content of the inner "column iterator" is repeated for each image. Image specific variables only exists within the column iterator for index templates. To make a minimal index page for testing, write the following code (the code below outputs the name of all images in a directory):
    <ja:rowiterator>
       <ja:coliterator>
         $label
       </ja:coliterator>
    </ja:rowiterator>
The row and column iterators are however usually used in a table context like this:
    <!-- Iterate through images and produce an index table -->
    <TABLE>
    <ja:rowiterator>
      <TR>
      <ja:coliterator>
        <TD VALIGN="bottom">
          <A HREF="$closeupPath">
            <ja:if exists="iconPath">
              <!-- No frames around icons like folders and movie files -->
              <IMG SRC="$iconPath" WIDTH="$thumbWidth" HEIGHT="$thumbHeight" BORDER=0>
              <BR>
            </ja:if>
            <ja:else>
              <IMG CLASS="image" SRC="$thumbPath"
               WIDTH="$thumbWidth" HEIGHT="$thumbHeight" BORDER=0><BR>
            </ja:else>
            <SMALL>$label</SMALL>
          </A>
        </TD>
      </ja:coliterator>
      </TR>
    </ja:rowiterator>
    </TABLE>
#### Linking correctly JAlbum tries to allow flexible linking of images. The user interface allows three types of linkings: Link to original images from index pages or slide pages Link to original images via scaled down slide page Link to scaled down images only When designing a template, it's important that you use correct variables and existence tests in order for the linking to make sense. The code above shows an example of correct linking from an index page to a slide page or image by using the $closeupPath variable. For the slide page template, the linking should be like this:
    <!-- Image, maybe with link to original -->
    <ja:if exists=originalPath>
      <A HREF="$originalPath">
        <IMG SRC="$imagePath" WIDTH="$imageWidth" HEIGHT="$imageHeight"
         BORDER=0 ALT="Original image">
      </A>
    </ja:if>
    <ja:else>
      <IMG SRC="$imagePath" WIDTH="$imageWidth" HEIGHT="$imageHeight">
    </ja:else>
### Adding multilingual text JAlbum has a mechanism to simplifiy writing skins that support texts in several languages (usually navigation strings like "Next" and "Previous" etc). Look at this example:
    $text.previousPage
Given the example above, JAlbum will look for a mapping for the "previousPage" "key" in the current language of the user running JAlbum. JAlbum will look into certain property files (see top of page). The format of such a file is simply a list of key=value mappings, one for each row. Please see the "standard" skin for a full working example. Here is a sample Swedish property file (texts_sv.properties):
    up=Upp en nivå
    previousPage=Förra sidan
    nextPage=Nästa sida
    firstPage=Första sidan
    lastPage=Sista sidan
    atFirstPage=Vid första sidan
    atLastPage=Vid sista sidan
    indexPage=Till index sidan
    originalImage=Originalbild
    cameraInfo=Kamerainformation
To support a new language, say German, simply copy and paste such a file, rename it to texts_de.properties and translate the text strings inside. Note: You can force the use of a certain language by setting the "language" user defined variable to an ISO two character code that the current skin has support for. # Scripting JAlbum is equipped with BeanShell, a very powerful java-like scripting language. Scripting gives you the ability to add any imaginable features to generated albums. Within special <% scriptlets %> you can construct just about any expression you like that is also valid Java. Scriptlets can be embedded into the template files of jalbum skins (index.htt and slide.htt) or inside style sheet files of skins. Here are some examples. Copy and paste them into a skin to have them executed during album creation.
    Ordinary calculations: <%= 5*(3+2) %> --> 25

    Access variables too: <%= thumbWidth*2 %>

    Warn for large images and use variables in strings easily:
    <%
      if (fileSize > 1500000)
        out.print("Original file is large ($fileSize bytes).");
    %>

    Produce fast Google-like numbered links to other index pages:
    <%
      if (totalIndexes == void) return;  // totalIndexes only exist if several index pages
      for (i=1; i<=totalIndexes; i++) {
        if (i == indexNum) out.print(" <b>" + i + "</b>");  // Emphasize current index page
        else {
          out.print("<a href=\"" + engine.getIndexPageName() + (i==1?"":i)
           + engine.getPageExtension() + "\">" + " " + i + "</a>");
        }
      }
    %>
Scripting can be performed in four syntaxes: `
    Ordinary scriptlet: <% ... %>
    Value of this scriptlet is outputted to the page: <%= ... %>
    Old/alternative syntax for the above: <eval> ... </eval>
    Function definition scriptlet (Only evaluated once. Since v4.5): <%! ... %>
` Use scripting to construct advanced skins containing cool dynamic image borders etc. You have access to the full power and class hierarchy of java + there are also a number of practical "implicit objects" to assist you (see below). #### Bypassing scriptlets If you are generating album files containing scriptlets that are to be processed by an ASP or JSP engine instead of JAlbum, you need to tell JAlbum to ignore those scriptlets and pass them on to the generated pages. This is done like this: `
    <ja:ignore>
      <% This could be some ASP code %>
    </ja:ignore>
` ### Implicit objects Apart from having access to ordinary variables when scripting, JAlbum also provide some special objects (Java type within parenthesis): out -Page writer. Printing to out will generate output on the resulting page (PrintWriter) application -Container for variables bound to the current instance of JAlbum (Map) album -Container for variables bound to the current album generation (Map) current -Container for all variables bound to the current image (Map) previous -Container for all variables bound to the previous image (Map) next -Container for all variables bound to the next image (Map) meta -Container for all metadata variables (EXIF etc) found inside an image (Map) programDirectory -File object representing the current JAlbum directory (File) skinDirectory -File object representing the current skin directory (File) skinResDirectory -File object representing the res directory of the current skin (File) imageDirectory -File object representing the current image directory (File) rootImageDirectory -File object representing the root image directory (File) outputDirectory -File object representing the current output directory (File) rootOutputDirectory -File object representing the root output directory (File) resDirectory -File object representing the output resource directory (File) files -Array of File objects for the images and mediafiles in the current image directory (File[]) currentFile -File object representing the currently processed file. A more convenient shorthand for files[imageNum-1] (File) fileVariables -Container of containers for variables bound to each image. Use File objects from files as key. current for example is a shorthand for `fileVariables.get(files[imageNum-1])` (Map) engine -The album generating JavaBean inside JAlbum. See API (AlbumBean) window -The window of JAlbum, useful when creating modal popup windows like color pickers etc (Frame) Note: "current", "previous" and "next" are accessible for slide pages or within a ja:coliterator on index pages The "meta" object usually contains more EXIF metadata than accessible from ordinary $variables, but possibly in a more raw format. You may list all metadata found inside an image by putting the following code inside a slide.htt file (JAlbum v2.8):
    <ja:if exists="meta">
      <pre><%= meta %></pre>
    </ja:if>
It is very important to check for existence of an object prior to accessing it. If there is no metadata in an image, the meta object doesn't exist. Referencing it will then lead to a script error. The following example picks the "Owner Name" variable out of the meta object (assuming it exists, which it does for some Canon cameras):
    <ja:if exists="meta">
      <%= meta.get("Owner Name") %>
    </ja:if>
### Understanding variables Template file variables can be used anywhere where you can put text and html tags (HTML mode) and within <% scriptlets %> (BeanShell mode). In HTML mode, you have to add a dollar sign ($) in front to indicate that this is a variable, in BeanShell mode this is optional but there is another difference: It is important to understand that $variables gets expanded to their value before script execution begins (compare this expansion to a search and replace operation in a text editor). This means that a script cannot change the value of a $variable, but it can change the value of its "shadow" variable (same name but without the dollar sign). The "shadow" variable can later on be referenced from HTML mode like this: <%= variable %> #### User defined variables JAlbum users can define new variables using the "User defined variables" table in the advanced section. These variables behave like other skin template variables and can also override existing variables. You can use user defined variables to add even more flexibility to the look of the skins you create. Usually, user defined variables become variables of String type nomatter what value they have (for backward compatibility). This isn't very convenient if you wish to work a lot with boolean or integer variables (requires type conversion). Automatic type conversion to boolean, integer and double is however performed if JAlbum senses that the particular skin has a custom user interface (see below). The type conversion is performed like this: Numeric string with decimals -> double Numeric string -> integer The string true or false -> boolean Any other string -> no conversion # Creating a custom user interface Advanced skins usually rely hevily on user defined variables to control their features. It may however feel rather cumbersome for the "common user" to edit user defined variables using the table in the advanced section of JAlbum. JAlbum 4.1 therefore allows skin writers to create a custom graphical user interface which will show in a tab of its own. The screenshot below illustrates this. Custom user interface for the "ShowOff" skin As the skin writer you have the full power of Java/BeanShell at your hands to create any imaginable user interface and the code behind to make it tick. Here is how you do it: Put a "onload.bsh" script file in the skin directory. JAlbum will read this file every time that skin is selected/loaded. Let the script construct the user interface and finally install it inside JAlbum. Here is a minimal example taken from the "Camera Geek" skin.
    /**
     * This script is being run when a skin is selected
     * The script produces a simple custom user interface.
     * Please look at the "ShowOff" skin for a full blown example
     * Author David Ekholm
     */

    import se.datadosen.component.*;

    // Controls that are to be imported into JAlbum as variables
    ControlPanel ui = new ControlPanel() {

       JTextField copyright = new JTextField("");
       JCheckBox showLabels = new JCheckBox("Show labels");
       JCheckBox showDates = new JCheckBox("Show dates", true);
       JCheckBox showImageNum = new JCheckBox("Show image numbers", true);
    };

    // Layout controls easily similar to how text is added in a word processor
    ui.add("p", new JLabel("This is a small example of a custom user interface."));
    ui.add("p", new JLabel("Copyright"));
    ui.add("tab hfill", ui.copyright);
    ui.add("p", ui.showLabels);
    ui.add("br", ui.showDates);
    ui.add("br", ui.showImageNum);

    // Finally install components into JAlbum
    window.setSkinUI(ui);
JAlbum will create BeanShell variables out of the values of the controls you put inside a "ControlPanel" class. Project settings that apply to skin specific controls are prefixed with "skin.". The controls need to be of "Swing" type so use "JButtons" and "JLabels" not "Button" or "Label" classes! ## Layout of controls The example above uses the "RiverLayout" layout engine of the ControlPanel to lay out controls. RiverLayout works similar to how text is being added to a word processor. Just adding controls will have them flow from left to right (no line wrapping), but adding certain "constraint strings" like "br" "p" and "tab" will affect the layout. Here is the full set of constraint strings: br - Add a line break p - Add a paragraph break tab - Add a tab stop (handy for constructing forms with labels followed by fields) hfill - Extend component horizontally vfill - Extent component vertically (currently only one allowed) left - Align following components to the left (default) center - Align following components horizontally centered right - Align following components to the right vtop - Align following components vertically top aligned vcenter - Align following components vertically centered (default) ### What's next? I now recommend you study the "Camera Geek" and "ShowOff" skins and also check out some scripting examples so you see the power that lies within BeanShell.