]> git.defcon.no Git - plsgen/commitdiff
Added first release code version plsgen-0.1
authorJon Langseth <jon.langseth@lilug.no>
Tue, 14 Dec 2010 12:44:57 +0000 (13:44 +0100)
committerJon Langseth <jon.langseth@lilug.no>
Tue, 14 Dec 2010 12:44:57 +0000 (13:44 +0100)
README [new file with mode: 0644]
full.tpl [new file with mode: 0644]
index.tpl [new file with mode: 0644]
nav.js [new file with mode: 0644]
plsgen [new file with mode: 0755]
plsgen.cfg [new file with mode: 0644]
style.css [new file with mode: 0644]

diff --git a/README b/README
new file mode 100644 (file)
index 0000000..5384598
--- /dev/null
+++ b/README
@@ -0,0 +1,106 @@
+Perl Simple Gallery Generator v 0.1
+==============================================
+
+Requirements
+------------------------
+
+Perl modules
+ * Image::ExifTool
+   http://search.cpan.org/~exiftool/Image-ExifTool-8.15/
+ * AppConfig
+   http://search.cpan.org/~abw/AppConfig/
+ * Getopt::Long (shipped with perl5->)
+   http://search.cpan.org/~jv/Getopt-Long-2.38/
+
+Programs:
+ * convert from ImageMagick
+   http://www.imagemagick.org/
+ * jhead
+   http://www.sentex.net/~mwandel/jhead/
+
+And, of course you probably want to publish whis on
+a Web server. The generated result is static HTML,
+so no requirements at all exist in regard to type
+and functionality of HTTP daemon.
+
+Installation
+-------------------------
+
+* Place the navigation JavaScript and the CSS file
+somewhere convenient on your Web server.
+
+* Place the template files (index.tpl, full.tpl)
+where You want, I prefer having them located in
+/usr/loca/share/plsgen
+
+* Place the executable script so that execution
+is practical. I place it as /usr/local/bin/plsgen so 
+that I have it in my search path.
+
+* Take a look at the shipped configuration file.
+plsgen looks for a config file as /etc/plsgen.cfg,
+as ./plsgen.cfg or ../plgen.cfg relative to where
+it is executed. Alternatively, the --config option
+allows you to override the default locations.
+The shipped config lists all configuration options,
+along with their default values (used if no config present).
+
+Execution / Use
+-------------------------
+
+plsgen is written to be ececuted in a directory filled
+with image files, and will create all its data
+in this directory. The resulting album will list
+all present image files of types JPG, PNG and GIF.
+A display-size and thumbnail image will be created
+for each image, and will be stored in the sub-directories
+view and thumbs (respectively).
+
+Recognized commandline options are:
+    --title='Your album title'
+        Sets the album title. Title will be stored in .title
+        If no title is given, it will be read from .title, if present
+    --htmlonly
+        Add this option to only generate HTML files
+        No image operations will be performed with this option
+    --config=/path/to/config
+        Overrides default config file location.
+        Default is to look for ./plsgen.cfg, then ../plsgen.cfg
+        and finally /etc/plsgen.cfg.
+     --help
+        Displays a list of available commandline options
+
+Todo-list
+-------------------------
+# TODO: filename.txt for file comments
+# TODO: Templating of EXIF
+# TODO: Priority/sorting of EXIF tags
+# TODO: Possibility for hide/show EXIF
+# TODO: RSS support? Delegate that to frontend?
+# TODO: Save reference to main-index thumbnail.
+# TODO: Clear old generated files and meta on regen
+# TODO: Use perlmagick et. al instead of convert/jhead..
+
+Lisencing
+-------------------------
+Copyright (c) 2010, Jon Langseth
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL JON LANGSETH BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/full.tpl b/full.tpl
new file mode 100644 (file)
index 0000000..9e2889a
--- /dev/null
+++ b/full.tpl
@@ -0,0 +1,44 @@
+<html>
+  <head>
+    <title>%{title}</title>
+    %{main_meta}
+    %{navigation_script}
+  </head>
+<body bgcolor=white>
+<center>
+
+<h2>%{title}</h2>
+
+<div id="indexlink">%{index}</div>
+<div id="position">Image %{position}.</div>
+
+<div id="prev">
+%{previous}
+</div>
+
+<div id="next">
+%{next}
+</div>
+
+<div id="display">
+<a href="%{current}" title="Full size">
+<img src="%{current_display}" alt="%{current}" />
+</a>
+</div>
+
+<div id="exifinfo">
+%{exif}
+</div>
+
+<div id="timestamp">
+Generated %{gallery_timestamp}
+</div>
+<div id="footer">%{footer_tag}</div>
+
+%{navscript}
+
+</center>
+</body>
+</html>
+
+
diff --git a/index.tpl b/index.tpl
new file mode 100644 (file)
index 0000000..a3be2bb
--- /dev/null
+++ b/index.tpl
@@ -0,0 +1,27 @@
+<html>
+  <head>
+    <title>%{title}</title>
+    %{main_meta}
+    %{navigation_script}
+  </head>
+<body>
+
+<h2>%{title}</h2>
+
+%{previous}
+&nbsp;
+Page %{position}
+&nbsp;
+%{next}
+
+<br/>
+
+%{thumbnails}
+
+Album generated %{gallery_timestamp}
+
+<div id="footer">%{footer_tag}</div>
+%{navscript}
+
+</body>
+</html>
diff --git a/nav.js b/nav.js
new file mode 100644 (file)
index 0000000..0be7da1
--- /dev/null
+++ b/nav.js
@@ -0,0 +1,55 @@
+var prev = null;
+var next = null;
+var index = null;
+
+var preloaded = [];
+
+function key_handler ( e )
+{
+       var key = (window.event) ? event.keyCode : e.keyCode;
+       if ( (key == 37) && (prev != null) )
+       {
+               window.location = prev + '.html';
+       }
+       if ( (key == 39) && (next != null) )
+       {
+               window.location = next + '.html';
+       }
+       if (key == 27) window.location = "index.html";
+       if ( (key == 38) && (index != null) ) window.location = index;
+       return;
+}
+function img_preload ( image )
+{
+       var image_thumb = new Image();
+       image_thumb.src = 'thumb/' + image;
+       preloaded.push(image_thumb);
+
+       var image_view  = new Image();
+       image_view.src  = 'view/' + image;
+       preloaded.push(image_view);
+}
+
+function nav_set_target ( target, direction )
+{
+       if ( target )
+       {
+               if ( direction == 0 ) prev = target;
+               if ( direction == 1 ) next = target;
+               var img_regx = /.*\.(jpg|JPG|jpeg|JPEG|png|PNG|gif|GIF)$/;
+               if ( target.match( img_regx ) )
+                       img_preload( target );
+       }
+}
+
+function nav_reg_prev ( target )
+{ nav_set_target(target, 0); }
+
+function nav_reg_next ( target )
+{ nav_set_target(target, 1); }
+
+function nav_reg_index( target )
+{ index = target; }
+
+function nav_reg_onkeypress()
+{ document.onkeyup = key_handler; }
diff --git a/plsgen b/plsgen
new file mode 100755 (executable)
index 0000000..0f04fb1
--- /dev/null
+++ b/plsgen
@@ -0,0 +1,373 @@
+#!/usr/bin/perl
+use Image::ExifTool;
+use POSIX;
+use AppConfig;
+use Getopt::Long;
+use strict;
+
+# TODO: filename.txt for file comments
+# TODO: Templating of EXIF
+# TODO: Priority/sorting of EXFI tags
+# TODO: Possibility for hide/show EXIF
+# TODO: RSS support? Delegate that to frontend?
+# TODO: Save reference to main-index thumbnail.
+# TODO: Clear old generated files and meta on regen
+# TODO: Use perlmagick et. al instead of convert/jhead..
+
+# Runtime data
+my $title = undef;
+my $htmlonly = 0;
+my $configfile = undef;
+my $halp = undef;
+
+GetOptions (
+       "title=s"       => \$title,
+       "htmlonly!"     => \$htmlonly,
+       "config=s"      => \$configfile,
+       "help"          => \$halp
+);
+if ( $halp )
+{
+       print "\nplsgen version 0.1\n";
+       print "Copyright Jon Langseth, BSD lisence\n\n";
+       print " --title='Your album title'\n";
+       print "     Sets the album title. Title will be stored in .title\n";
+       print "     If no title is given, it will be read from .title, if present\n";
+       print " --htmlonly\n";
+       print "     Add this option to only generate HTML files\n";
+       print "     No image operations will be performed with this option\n";
+       print " --config=/path/to/config\n";
+       print "     Overrides default config file location.\n";
+       print "     Default is to look for ./plsgen.cfg, then ../plsgen.cfg\n";
+       print "     and finally /etc/plsgen.cfg.\n";
+       print "\n";
+       exit;
+}
+
+# Configuration data
+my $config = get_config( $configfile );
+my $full_tpl_file      = $config->full_tpl_file;
+my $index_tpl_file     = $config->index_tpl_file;
+my $css_file           = $config->css_file;
+my $navigation_script  = $config->navigation_script;
+my $columns            = $config->columns;
+my $rows               = $config->rows;
+my $thumb_pre          = $config->thumb_pre;
+my $thumb_post                 = $config->thumb_post;
+my $idx_prev_text      = $config->idx_prev_text;
+my $idx_next_text      = $config->idx_next_text;
+my $idx_ret_text       = $config->idx_ret_text;
+my $footer_tag                 = $config->footer_tag;
+
+# Get or save the title, depending..
+if ( (not $title) && ( -f ".title" ) )
+{
+       open TF, "<.title";
+       $title = <TF>;
+       chomp($title);
+       close TF;
+}
+elsif ( $title )
+{
+       open TF, ">.title";
+       print TF $title . "\n";
+       close TF;
+}
+
+mkdir "thumb";
+mkdir "view";
+
+# Glob file names to an array
+my @images = glob("*png *.jpg *.JPG *.gif *.GIF");
+# Keep count of total number of images
+my $imagecount = $#images;
+
+my ($current, $previous, $next);
+my $indexcount = 1;
+my $indexfile;
+
+my $main_meta = "<link rel='stylesheet' href='" . $css_file . "' type='text/css' />";
+my $navigation_header = "<script type='text/javascript' src='" . $navigation_script . "'></script>";
+
+my $thumbs = "<div class='thumbnails'>";
+
+my $indexes = ceil( $imagecount/($rows*$columns) );
+my $gentime = localtime;
+
+# In groups of N ( X rows x Y colums of index):
+# - Step through image array:
+for ( my $i = 1; $i <= $imagecount; $i++)
+{
+       $current = $images[$i-1];
+# - -   Previous image is previous location in array, unless cur=first
+       $previous = $images[$i-2] if $i > 1;
+       $previous = undef if  $i == 1;
+# - -   Next image is next location in array, unless cur=last.
+       $next = $images[$i] if $i < ( $imagecount);
+       $next = undef if $i >= ( $imagecount);
+
+       #Set up text-strings for template replacement
+       if ( $indexcount == 1 ) { $indexfile = "index.html"; } 
+       else { $indexfile = "index" . $indexcount . ".html"; }
+       my $navscript = gen_navscript( $previous, $next, $indexfile );
+       my $position = $i . " of " . $imagecount;
+       my $prev_text = "<a href='" . $previous . ".html'><img src='thumb/" . $previous . "' /></a>";
+       my $next_text = "<a href='" . $next . ".html'><img src='thumb/" . $next . "' /></a>";
+       my $cur_index_text = "<a href='". $indexfile ."'>" . $idx_ret_text . "</a>";
+       my $current_display = "view/" . $current;
+
+       printf ("Processing image %s: %s\n", $position, $current);
+
+# - - If rotated according to EXIF, do rotation
+       my $exif = get_exifdata( $current );
+       if ( $exif->{'Orientation'} =~ m/rotate/i )
+       {
+               system("jhead -autorot " . $current . ">/dev/null") unless $htmlonly;
+       }
+       my $exif_text = get_exifblock($exif);
+
+# - - Create thumbnail image (resize to new image)
+       system("convert " . $current . " -geometry '160x120>' thumb/" . $current) unless $htmlonly;
+# - - Create normal display image (resize to new image)
+       system("convert " . $current . " -geometry '800x600>' view/" . $current) unless $htmlonly;
+
+# - - Create full view HTML file
+       my $cur_html;
+       open TEMPLATE, "<" . $full_tpl_file or die "UNABLE TO LOAD TEMPLATE $full_tpl_file\n";
+       while (<TEMPLATE>)
+       {
+               if ( $previous ) { $_ =~ s/%\{previous\}/$prev_text/; } 
+               else { $_ =~ s/%\{previous\}//; } 
+
+               if ( $next ) { $_ =~ s/%\{next\}/$next_text/; }
+               else { $_ =~ s/%\{next\}//; }
+
+               $_ =~ s/%\{index\}/$cur_index_text/;
+               $_ =~ s/%\{title\}/$title/;
+               $_ =~ s/%\{main_meta\}/$main_meta/;
+               $_ =~ s/%\{navigation_script\}/$navigation_header/;
+               $_ =~ s/%\{position\}/$position/;
+               $_ =~ s/%\{current\}/$current/;
+               $_ =~ s/%\{current_display\}/$current_display/;
+               $_ =~ s/%\{exif\}/$exif_text/;
+               $_ =~ s/%\{gallery_timestamp\}/$gentime/;
+               $_ =~ s/%\{navscript\}/$navscript/;
+               $_ =~ s/%\{footer_tag\}/$footer_tag/;
+               $cur_html .= $_;
+       }
+       close TEMPLATE;
+       open HTML, ">" . $current . ".html" or die "UNABLE TO WRITE\n";
+       print HTML $cur_html;
+       close HTML;
+
+# - -   Append image thumbnail code to current index content
+       $thumbs .= $thumb_pre . "<a href='" . $current . ".html'><img src='thumb/" . $current . "' /></a>" . $thumb_post;
+       if ( $i % ($rows*$columns) == 0 )
+       {
+               $thumbs .= "</div>";
+# - - On each Y, terminate index file/group:
+               make_index( $index_tpl_file, $indexcount, $indexes, $thumbs);
+               $thumbs = "<div class='thumbnails'>";
+               $indexcount++;
+       }
+       elsif ( $i % ($columns) == 0 )
+       {
+# - - On each X, terminate index content row 
+               $thumbs .= "\n</div>\n<div class='thumbnails'>";
+       }
+}
+$thumbs .= "</div>";
+make_index( $index_tpl_file, $indexcount, $indexes, $thumbs);
+# Done.
+
+
+# -------------- Functions supporting above code -----------------------
+sub make_index
+{
+       my $tpl = shift;
+       my $idxcount = shift;
+       my $lastidx = shift;
+       my $thumbs = shift;
+
+       my $gentime = localtime;
+       my $html;
+
+       my $prev_file;
+       my $prev_text;
+       my $next_file;
+       my $next_text;
+       my $indexfile;
+
+       # If index counter is 1, filename is index.html
+       $indexfile = ( $indexcount ==1 ) ? "index.html" : "index" . $idxcount . ".html";
+
+       # If index counter is > 1, add previous index link.
+       if ( $idxcount > 1 ) 
+       {
+               $prev_file = "index" . ($idxcount-1) . ".html";
+               $prev_file = "index.html" if ( $idxcount == 2 );
+               $prev_text = "<a href='$prev_file'>" . $idx_prev_text . "</a>";
+       } 
+
+       # If current image is last in array, do not add nex-index link
+       if ( $idxcount < $lastidx ) 
+       {
+               $next_file = "index" . ($idxcount+1) . ".html";
+               $next_text = "<a href='" . $next_file . "'>" . $idx_next_text . "</a>";
+       }
+
+       my $position = $indexcount . " of " . $lastidx;
+       my $navscript = gen_navscript( $prev_file, $next_file );
+
+       open TEMPLATE, "<" . $tpl or die "UNABLE TO LOAD TEMPLATE\n";
+       while (<TEMPLATE>)
+       {
+               $_ =~ s/%\{previous\}/$prev_text/;
+               $_ =~ s/%\{next\}/$next_text/;
+               $_ =~ s/%\{title\}/$title/;
+               $_ =~ s/%\{position\}/$position/;
+               $_ =~ s/%\{main_meta\}/$main_meta/;
+               $_ =~ s/%\{navigation_script\}/$navigation_header/;
+               $_ =~ s/%\{thumbnails\}/$thumbs/;
+               $_ =~ s/%\{gallery_timestamp\}/$gentime/;
+               $_ =~ s/%\{navscript\}/$navscript/;
+               $_ =~ s/%\{footer_tag\}/$footer_tag/;
+               $html .= $_;
+       }
+       close TEMPLATE;
+       open HTML, ">" . $indexfile or die "UNABLE TO WRITE\n";
+       print HTML $html;
+       close HTML;
+
+}
+
+sub get_exifdata ($)
+{
+       my $image = shift;
+       my $exifTool = new Image::ExifTool;
+       $exifTool->Options(Unknown => 1);
+
+       my $exif;
+       my $info = $exifTool->ImageInfo($image);
+       $exif->{'Make'} = $info->{'Make'};
+       $exif->{'Model'} = $info->{'Model'};
+       #$exif->{'Orientation'} = $info->{'Orientation'};
+       $exif->{'ExposureTime'} = $info->{'ExposureTime'};
+       $exif->{'FNumber'} = $info->{'FNumber'};
+       $exif->{'ISO'} = $info->{'ISO'};
+       $exif->{'CreateDate'} = $info->{'CreateDate'};
+       $exif->{'ExposureCompensation'} = $info->{'ExposureCompensation'};
+       $exif->{'Flash'} = $info->{'Flash'};
+       $exif->{'FocalLength'} = $info->{'FocalLength'};
+       #$exif->{'ColorSpace'} = $info->{'ColorSpace'};
+       #$exif->{'FileSource'} = $info->{'FileSource'};
+       $exif->{'ExposureMode'} = $info->{'ExposureMode'};
+       $exif->{'Macro'} = $info->{'Macro'};
+       $exif->{'LensType'} = $info->{'LensType'};
+       return $exif;
+}
+
+sub get_exifblock 
+{
+       my $exif = shift;
+       my $exifTool = new Image::ExifTool;
+       my $block = "<table id='exifdata' cellspacing='0'>\n";
+       $block .= "<tr class='exifhead'><td>EXIF Parameter</td><td>Value</td></tr>\n";
+       my $tagcount = 0;
+       my $flipflop = 0;
+       foreach my $tag ( keys %$exif )
+       {
+               my $val = $exif->{$tag};
+               next unless $val;
+               $block .= "<tr class='exiflight'>" if $flipflop;
+               $block .= "<tr class='exifdark'>"  if not $flipflop;
+               $block .= "\t<td>";
+               $block .= $exifTool->GetDescription($tag);
+               $block .= "</td>\n";
+               $block .= "\t<td>";
+               $block .= $val;
+               $block .= "</td>\n";
+               $block .= "</tr>\n";
+               $flipflop = not $flipflop;
+               $tagcount++;
+       }
+       $block .= "</table>";
+       $block = undef if not $tagcount;
+       return $block;
+}
+
+sub gen_navscript
+{
+       my $prev = shift;
+       my $next = shift;
+       my $index = shift;
+
+       $prev =~ s/\.html//;
+       $next =~ s/\.html//;
+
+       my $scriptbuffer = "<script type='text/javascript'>\n";
+       $scriptbuffer .= "\tnav_reg_prev('" . $prev . "');\n" if $prev;
+       $scriptbuffer .= "\tnav_reg_next('" . $next . "');\n" if $next;
+       $scriptbuffer .= "\tnav_reg_index('" . $index . "');\n" if $index;
+       $scriptbuffer .= "\tnav_reg_onkeypress();\n";
+       $scriptbuffer .= "</script>\n";
+       return $scriptbuffer;
+}
+
+sub get_config
+{
+       # My standard way of implementing AppConfig use ...
+       my $filename = shift;
+       if ( not $filename )
+       {
+               my @files = ( "./plsgen.cfg", "../plsgen.cfg", "/etc/plsgen.cfg" );
+               foreach my $file ( @files ) {
+                       $filename = $file;
+                       last if ( -f $filename );
+               }
+       }
+
+       my $cfg = AppConfig->new(
+               {
+                   CASE   => 1,
+                   ERROR  => \&cfg_error,
+                   GLOBAL => {
+                       DEFAULT  => "<unset>",
+                       ARGCOUNT => AppConfig::ARGCOUNT_ONE
+                   },
+               }
+
+       );
+       $cfg->define('full_tpl_file');
+       $cfg->full_tpl_file("/usr/local/share/plsgen/full.tpl");
+       $cfg->define('index_tpl_file');
+       $cfg->index_tpl_file("/usr/local/share/plsgen/index.tpl");
+       $cfg->define('css_file');
+       $cfg->css_file("../style.css");
+       $cfg->define('navigation_script');
+       $cfg->navigation_script("../nav.js");
+       $cfg->define('columns');
+       $cfg->columns(4);
+       $cfg->define('rows');
+       $cfg->rows(3);
+       $cfg->define('thumb_pre');
+       $cfg->thumb_pre("<div class='thumb'>");
+       $cfg->define('thumb_post');
+       $cfg->thumb_post("</div>");
+       $cfg->define('idx_prev_text');
+       $cfg->idx_prev_text("&larr; Back");
+       $cfg->define('idx_next_text');
+       $cfg->idx_next_text("Next &rarr;");
+       $cfg->define('idx_ret_text');
+       $cfg->idx_ret_text("To index");
+       $cfg->define('footer_tag');
+       $cfg->footer_tag('plsgen : Perl Simple Gallery Generator 0.1');
+
+       $cfg->file($filename) if -f $filename;
+       return $cfg;
+}
+
+sub cfg_error {
+    die "Error occured in config-handling: $_\n";
+}
+
diff --git a/plsgen.cfg b/plsgen.cfg
new file mode 100644 (file)
index 0000000..84a3b7a
--- /dev/null
@@ -0,0 +1,31 @@
+# This configuration file contains the default values
+# used if no configuration file is present.
+#
+# Adjust as appropriate. Make special note of the 
+# location of the template files, and the CSS/script.
+
+# Template file locations, absolute paths on the local filesystem
+full_tpl_file          = /usr/local/share/plsgen/full.tpl
+index_tpl_file         = /usr/local/share/plsgen/index.tpl
+
+# CSS and script files are given paths as presented by your web-server.
+css_file               = ../style.css
+navigation_script      = ../nav.js
+
+# The layout of the thumbnails in the index. Each row will be wrapped
+# in a <div class='thumnails'>
+rows                   = 3
+columns                        = 4
+
+# HTML code to wrap each thumbnail with. The default allows simple
+# styling. If you wish to span this over multiple lines, use the
+# end-of-line continuation mark \
+thumb_pre              = <div class='thumb'>
+thumb_post             = </div>
+
+# Text-strings that can easily be replaced with more complex HTML.
+idx_prev_text          = &larr; Back
+idx_next_text          = Next &rarr;
+idx_ret_text           = To index
+footer_tag             = plsgen : Perl Simple Gallery Generator 0.1
+
diff --git a/style.css b/style.css
new file mode 100644 (file)
index 0000000..9149f23
--- /dev/null
+++ b/style.css
@@ -0,0 +1,95 @@
+body {
+       color: white;
+       background-color: black;
+       text-align: center;
+}
+a, a:visited {
+       color: wheat;
+       text-decoration: none;
+       border: 0px;
+}
+
+a img { border: 0px; }
+
+#prev a, #next a  {
+       width: 160px;
+       background: #2c2c29;
+       display: block;
+       padding: 3px;
+}
+#prev {
+       position: absolute;
+       top: 40px;
+       left: 10px;
+       width: 160px;
+       text-align: center;
+       background: transparent;
+}
+
+#next {
+       position: absolute;
+       top: 40px;
+       right: 10px;
+       text-align: center;
+       background: transparent;
+}
+
+#display {
+       display: block;
+       float: center;
+}
+
+div.thumbnails {
+       clear: both;
+       display: block;
+}
+.thumb {
+       width: 170px;
+       height: 135px;
+       margin: 5px;
+       padding: 0px;
+        display: -moz-inline-stack;
+       display: inline-block;
+        /* zoom: 1;*/
+        *display: inline;
+        _height: 135px;
+
+       background-color: #2c2c29;
+       vertical-align: top;
+}
+.thumb img
+{
+       background-color: #2c2c29;
+       margin: 0px;
+       margin-top: 5px;
+       padding: 0px;
+}
+.thumb a img
+{
+  border:1px solid #2c2c29;
+}
+.thumb a:hover img
+{
+  border:1px solid #black;
+}
+
+#exifdata {
+       margin-top: 20px;
+       width: 450px;
+       font-size: 8pt;
+       font-family: sans;
+}
+tr.exifhead { background: #2c2c29; }
+tr.exiflight { background: #5c5c59; }
+tr.exifdark { background: #3c3c30;}
+td { 
+margin: 0px; 
+padding: 2px 0 2px 10px; 
+}
+
+#footer {
+       font-family: serif;
+       font-size: 7pt;
+       color: #444;
+       margin-top: 20px;
+}