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 or (v6) custom title set for this directory |
description |
Description for album directory (v6) |
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 |
folder |
Only defined if curent file is a folder/directory. Use in ja:if exists tests (v6) |
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 not having a thumbnail image and other non-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.
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 or ja:fileiterator 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:
<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>
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>
Starting from JAlbum 6.1 there is also the new ja:fileiterator element which acts as a ja:coliterator nested inside a ja:rowiterator, but with the addition of two handy optional attributes: dirs and nodirs. If "dirs" is specified, only directories will be included, if "nodirs" is specified, only plain files will be included:
<ja:fileiterator nodirs>
$label
</ja:fileiterator>
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>
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 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.
|