importing initial source batch
authorEugene Crosser <crosser@average.org>
Sun, 26 Feb 2006 21:19:14 +0000 (21:19 +0000)
committerEugene Crosser <crosser@average.org>
Sun, 26 Feb 2006 21:19:14 +0000 (21:19 +0000)
include/close.gif [new file with mode: 0644]
include/gallery.css [new file with mode: 0644]
include/gallery.js [new file with mode: 0644]
include/lightbox.css [new file with mode: 0644]
include/lightbox.js [new file with mode: 0644]
include/loading.gif [new file with mode: 0644]
include/overlay.png [new file with mode: 0644]
mkgallery.pl [new file with mode: 0755]
mkindex.pl [new file with mode: 0755]

diff --git a/include/close.gif b/include/close.gif
new file mode 100644 (file)
index 0000000..773013b
Binary files /dev/null and b/include/close.gif differ
diff --git a/include/gallery.css b/include/gallery.css
new file mode 100644 (file)
index 0000000..5764706
--- /dev/null
@@ -0,0 +1,31 @@
+img {
+ clear:both;
+ border: 1px solid green;
+}
+table.slide {
+ float: left;
+ width: 210px;
+ height: 210px;
+ margin: 0.3ex;
+ border: 1px solid black;
+ background-color: #e0c080;
+ text-align: center;
+ font-size: 10px
+}
+tr {
+ margin: 0px;
+}
+td {
+ margin: 0px;
+ vertical-align: middle;
+}
+div.ibox {
+ background-color: #ffffc0;
+ border: 1px solid black;
+ text-align: left;
+ position: absolute;
+ margin: 0px;
+ padding: 1ex;
+ display: none;
+ z-index: 1000;
+}
diff --git a/include/gallery.js b/include/gallery.js
new file mode 100644 (file)
index 0000000..3c2e1f3
--- /dev/null
@@ -0,0 +1,35 @@
+function showIbox(iboxid) {
+ var ibox = document.getElementById(iboxid);
+ var wwidth;
+ var wheight;
+ var bwidth = 400;
+ var bheight = 300;
+ if (self.innerWidth)
+ {
+  wwidth = self.innerWidth;
+  wheight = self.innerHeight;
+ }
+ else if (document.documentElement && document.documentElement.clientWidth)
+ {
+  wwidth = document.documentElement.clientWidth;
+  wheight = document.documentElement.clientHeight;
+ }
+ else if (document.body)
+ {
+  wwidth = document.body.clientWidth;
+  wheight = document.body.clientHeight;
+ }
+ ibox.style.width = bwidth + "px";
+ ibox.style.height = bheight + "px";
+ ibox.style.left = ((wwidth - bwidth) / 2) + "px";
+ ibox.style.top = ((wheight - bheight) / 2) + "px";
+ // alert('wwidth='+wwidth+'; bwidth='+bwidth+'; wheight='+wheight+'; bheight='+bheight);
+ ibox.zIndex = '0';
+ ibox.style.display = 'block';
+ return false;
+}
+function HideIbox(iboxid) {
+ var ibox = document.getElementById(iboxid);
+ ibox.zIndex = '1000';
+ ibox.style.display = 'none';
+}
diff --git a/include/lightbox.css b/include/lightbox.css
new file mode 100644 (file)
index 0000000..2e4fc70
--- /dev/null
@@ -0,0 +1,26 @@
+#lightbox{
+       background-color:#eee;
+       padding: 10px;
+       border-bottom: 2px solid #666;
+       border-right: 2px solid #666;
+       }
+#lightboxDetails{
+       font-size: 0.8em;
+       padding-top: 0.4em;
+       }       
+#lightboxCaption{ float: left; }
+#keyboardMsg{ float: right; }
+#closeButton{ top: 5px; right: 5px; }
+
+#lightbox img{ border: none; clear: both;} 
+#overlay img{ border: none; }
+
+#overlay{ background-image: url(overlay.png); }
+
+* html #overlay{
+       background-color: #333;
+       back\ground-color: transparent;
+       background-image: url(blank.gif);
+       filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="overlay.png", sizingMethod="scale");
+       }
+       
\ No newline at end of file
diff --git a/include/lightbox.js b/include/lightbox.js
new file mode 100644 (file)
index 0000000..277ec59
--- /dev/null
@@ -0,0 +1,437 @@
+/*
+       Lightbox JS: Fullsize Image Overlays 
+       by Lokesh Dhakar - http://www.huddletogether.com
+
+       For more information on this script, visit:
+       http://huddletogether.com/projects/lightbox/
+
+       Licensed under the Creative Commons Attribution 2.5 License - http://creativecommons.org/licenses/by/2.5/
+       (basically, do anything you want, just leave my name and link)
+       
+       Table of Contents
+       -----------------
+       Configuration
+       
+       Functions
+       - getPageScroll()
+       - getPageSize()
+       - pause()
+       - getKey()
+       - listenKey()
+       - showLightbox()
+       - hideLightbox()
+       - initLightbox()
+       - addLoadEvent()
+       
+       Function Calls
+       - addLoadEvent(initLightbox)
+
+*/
+
+
+
+//
+// Configuration
+//
+
+// If you would like to use a custom loading image or close button reference them in the next two lines.
+var loadingImage = 'loading.gif';              
+var closeButton = 'close.gif';         
+
+
+
+
+
+//
+// getPageScroll()
+// Returns array with x,y page scroll values.
+// Core code from - quirksmode.org
+//
+function getPageScroll(){
+
+       var yScroll;
+
+       if (self.pageYOffset) {
+               yScroll = self.pageYOffset;
+       } else if (document.documentElement && document.documentElement.scrollTop){      // Explorer 6 Strict
+               yScroll = document.documentElement.scrollTop;
+       } else if (document.body) {// all other Explorers
+               yScroll = document.body.scrollTop;
+       }
+
+       arrayPageScroll = new Array('',yScroll) 
+       return arrayPageScroll;
+}
+
+
+
+//
+// getPageSize()
+// Returns array with page width, height and window width, height
+// Core code from - quirksmode.org
+// Edit for Firefox by pHaez
+//
+function getPageSize(){
+       
+       var xScroll, yScroll;
+       
+       if (window.innerHeight && window.scrollMaxY) {  
+               xScroll = document.body.scrollWidth;
+               yScroll = window.innerHeight + window.scrollMaxY;
+       } else if (document.body.scrollHeight > document.body.offsetHeight){ // all but Explorer Mac
+               xScroll = document.body.scrollWidth;
+               yScroll = document.body.scrollHeight;
+       } else { // Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari
+               xScroll = document.body.offsetWidth;
+               yScroll = document.body.offsetHeight;
+       }
+       
+       var windowWidth, windowHeight;
+       if (self.innerHeight) { // all except Explorer
+               windowWidth = self.innerWidth;
+               windowHeight = self.innerHeight;
+       } else if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict Mode
+               windowWidth = document.documentElement.clientWidth;
+               windowHeight = document.documentElement.clientHeight;
+       } else if (document.body) { // other Explorers
+               windowWidth = document.body.clientWidth;
+               windowHeight = document.body.clientHeight;
+       }       
+       
+       // for small pages with total height less then height of the viewport
+       if(yScroll < windowHeight){
+               pageHeight = windowHeight;
+       } else { 
+               pageHeight = yScroll;
+       }
+
+       // for small pages with total width less then width of the viewport
+       if(xScroll < windowWidth){      
+               pageWidth = windowWidth;
+       } else {
+               pageWidth = xScroll;
+       }
+
+
+       arrayPageSize = new Array(pageWidth,pageHeight,windowWidth,windowHeight) 
+       return arrayPageSize;
+}
+
+
+//
+// pause(numberMillis)
+// Pauses code execution for specified time. Uses busy code, not good.
+// Code from http://www.faqts.com/knowledge_base/view.phtml/aid/1602
+//
+function pause(numberMillis) {
+       var now = new Date();
+       var exitTime = now.getTime() + numberMillis;
+       while (true) {
+               now = new Date();
+               if (now.getTime() > exitTime)
+                       return;
+       }
+}
+
+//
+// getKey(key)
+// Gets keycode. If 'x' is pressed then it hides the lightbox.
+//
+
+function getKey(e){
+       if (e == null) { // ie
+               keycode = event.keyCode;
+       } else { // mozilla
+               keycode = e.which;
+       }
+       key = String.fromCharCode(keycode).toLowerCase();
+       
+       if(key == 'x'){ hideLightbox(); }
+}
+
+
+//
+// listenKey()
+//
+function listenKey () {        document.onkeypress = getKey; }
+       
+
+//
+// showLightbox()
+// Preloads images. Pleaces new image in lightbox then centers and displays.
+//
+function showLightbox(objLink)
+{
+       // prep objects
+       var objOverlay = document.getElementById('overlay');
+       var objLightbox = document.getElementById('lightbox');
+       var objCaption = document.getElementById('lightboxCaption');
+       var objImage = document.getElementById('lightboxImage');
+       var objLoadingImage = document.getElementById('loadingImage');
+       var objLightboxDetails = document.getElementById('lightboxDetails');
+
+       
+       var arrayPageSize = getPageSize();
+       var arrayPageScroll = getPageScroll();
+
+       // center loadingImage if it exists
+       if (objLoadingImage) {
+               objLoadingImage.style.top = (arrayPageScroll[1] + ((arrayPageSize[3] - 35 - objLoadingImage.height) / 2) + 'px');
+               objLoadingImage.style.left = (((arrayPageSize[0] - 20 - objLoadingImage.width) / 2) + 'px');
+               objLoadingImage.style.display = 'block';
+       }
+
+       // set height of Overlay to take up whole page and show
+       objOverlay.style.height = (arrayPageSize[1] + 'px');
+       objOverlay.style.display = 'block';
+
+       // preload image
+       imgPreload = new Image();
+
+       imgPreload.onload=function(){
+               objImage.src = objLink.href;
+
+               // center lightbox and make sure that the top and left values are not negative
+               // and the image placed outside the viewport
+               var lightboxTop = arrayPageScroll[1] + ((arrayPageSize[3] - 35 - imgPreload.height) / 2);
+               var lightboxLeft = ((arrayPageSize[0] - 20 - imgPreload.width) / 2);
+               
+               objLightbox.style.top = (lightboxTop < 0) ? "0px" : lightboxTop + "px";
+               objLightbox.style.left = (lightboxLeft < 0) ? "0px" : lightboxLeft + "px";
+
+
+               objLightboxDetails.style.width = imgPreload.width + 'px';
+               
+               if(objLink.getAttribute('title')){
+                       objCaption.style.display = 'block';
+                       //objCaption.style.width = imgPreload.width + 'px';
+                       objCaption.innerHTML = objLink.getAttribute('title');
+               } else {
+                       objCaption.style.display = 'none';
+               }
+               
+               // A small pause between the image loading and displaying is required with IE,
+               // this prevents the previous image displaying for a short burst causing flicker.
+               if (navigator.appVersion.indexOf("MSIE")!=-1){
+                       pause(250);
+               } 
+
+               if (objLoadingImage) {  objLoadingImage.style.display = 'none'; }
+
+               // Hide select boxes as they will 'peek' through the image in IE
+               selects = document.getElementsByTagName("select");
+        for (i = 0; i != selects.length; i++) {
+                selects[i].style.visibility = "hidden";
+        }
+
+       
+               objLightbox.style.display = 'block';
+
+               // After image is loaded, update the overlay height as the new image might have
+               // increased the overall page height.
+               arrayPageSize = getPageSize();
+               objOverlay.style.height = (arrayPageSize[1] + 'px');
+               
+               // Check for 'x' keypress
+               listenKey();
+
+               return false;
+       }
+
+       imgPreload.src = objLink.href;
+       
+}
+
+
+
+
+
+//
+// hideLightbox()
+//
+function hideLightbox()
+{
+       // get objects
+       objOverlay = document.getElementById('overlay');
+       objLightbox = document.getElementById('lightbox');
+
+       // hide lightbox and overlay
+       objOverlay.style.display = 'none';
+       objLightbox.style.display = 'none';
+
+       // make select boxes visible
+       selects = document.getElementsByTagName("select");
+    for (i = 0; i != selects.length; i++) {
+               selects[i].style.visibility = "visible";
+       }
+
+       // disable keypress listener
+       document.onkeypress = '';
+}
+
+
+
+
+//
+// initLightbox()
+// Function runs on window load, going through link tags looking for rel="lightbox".
+// These links receive onclick events that enable the lightbox display for their targets.
+// The function also inserts html markup at the top of the page which will be used as a
+// container for the overlay pattern and the inline image.
+//
+function initLightbox()
+{
+       
+       if (!document.getElementsByTagName){ return; }
+       var anchors = document.getElementsByTagName("a");
+
+       // loop through all anchor tags
+       for (var i=0; i<anchors.length; i++){
+               var anchor = anchors[i];
+
+               if (anchor.getAttribute("href") && (anchor.getAttribute("rel") == "lightbox")){
+                       anchor.onclick = function () {showLightbox(this); return false;}
+               }
+       }
+
+       // the rest of this code inserts html at the top of the page that looks like this:
+       //
+       // <div id="overlay">
+       //              <a href="#" onclick="hideLightbox(); return false;"><img id="loadingImage" /></a>
+       //      </div>
+       // <div id="lightbox">
+       //              <a href="#" onclick="hideLightbox(); return false;" title="Click anywhere to close image">
+       //                      <img id="closeButton" />                
+       //                      <img id="lightboxImage" />
+       //              </a>
+       //              <div id="lightboxDetails">
+       //                      <div id="lightboxCaption"></div>
+       //                      <div id="keyboardMsg"></div>
+       //              </div>
+       // </div>
+       
+       var objBody = document.getElementsByTagName("body").item(0);
+       
+       // create overlay div and hardcode some functional styles (aesthetic styles are in CSS file)
+       var objOverlay = document.createElement("div");
+       objOverlay.setAttribute('id','overlay');
+       objOverlay.onclick = function () {hideLightbox(); return false;}
+       objOverlay.style.display = 'none';
+       objOverlay.style.position = 'absolute';
+       objOverlay.style.top = '0';
+       objOverlay.style.left = '0';
+       objOverlay.style.zIndex = '90';
+       objOverlay.style.width = '100%';
+       objBody.insertBefore(objOverlay, objBody.firstChild);
+       
+       var arrayPageSize = getPageSize();
+       var arrayPageScroll = getPageScroll();
+
+       // preload and create loader image
+       var imgPreloader = new Image();
+       
+       // if loader image found, create link to hide lightbox and create loadingimage
+       imgPreloader.onload=function(){
+
+               var objLoadingImageLink = document.createElement("a");
+               objLoadingImageLink.setAttribute('href','#');
+               objLoadingImageLink.onclick = function () {hideLightbox(); return false;}
+               objOverlay.appendChild(objLoadingImageLink);
+               
+               var objLoadingImage = document.createElement("img");
+               objLoadingImage.src = incPrefix + loadingImage;
+               objLoadingImage.setAttribute('id','loadingImage');
+               objLoadingImage.style.position = 'absolute';
+               objLoadingImage.style.zIndex = '150';
+               objLoadingImageLink.appendChild(objLoadingImage);
+
+               imgPreloader.onload=function(){};       //      clear onLoad, as IE will flip out w/animated gifs
+
+               return false;
+       }
+
+       imgPreloader.src = incPrefix + loadingImage;
+
+       // create lightbox div, same note about styles as above
+       var objLightbox = document.createElement("div");
+       objLightbox.setAttribute('id','lightbox');
+       objLightbox.style.display = 'none';
+       objLightbox.style.position = 'absolute';
+       objLightbox.style.zIndex = '100';       
+       objBody.insertBefore(objLightbox, objOverlay.nextSibling);
+       
+       // create link
+       var objLink = document.createElement("a");
+       objLink.setAttribute('href','#');
+       objLink.setAttribute('title','Click to close');
+       objLink.onclick = function () {hideLightbox(); return false;}
+       objLightbox.appendChild(objLink);
+
+       // preload and create close button image
+       var imgPreloadCloseButton = new Image();
+
+       // if close button image found, 
+       imgPreloadCloseButton.onload=function(){
+
+               var objCloseButton = document.createElement("img");
+               objCloseButton.src = incPrefix + closeButton;
+               objCloseButton.setAttribute('id','closeButton');
+               objCloseButton.style.position = 'absolute';
+               objCloseButton.style.zIndex = '200';
+               objLink.appendChild(objCloseButton);
+
+               return false;
+       }
+
+       imgPreloadCloseButton.src = incPrefix + closeButton;
+
+       // create image
+       var objImage = document.createElement("img");
+       objImage.setAttribute('id','lightboxImage');
+       objLink.appendChild(objImage);
+       
+       // create details div, a container for the caption and keyboard message
+       var objLightboxDetails = document.createElement("div");
+       objLightboxDetails.setAttribute('id','lightboxDetails');
+       objLightbox.appendChild(objLightboxDetails);
+
+       // create caption
+       var objCaption = document.createElement("div");
+       objCaption.setAttribute('id','lightboxCaption');
+       objCaption.style.display = 'none';
+       objLightboxDetails.appendChild(objCaption);
+
+       // create keyboard message
+       var objKeyboardMsg = document.createElement("div");
+       objKeyboardMsg.setAttribute('id','keyboardMsg');
+       objKeyboardMsg.innerHTML = 'press <kbd>x</kbd> to close';
+       objLightboxDetails.appendChild(objKeyboardMsg);
+
+
+}
+
+
+
+
+//
+// addLoadEvent()
+// Adds event to window.onload without overwriting currently assigned onload functions.
+// Function found at Simon Willison's weblog - http://simon.incutio.com/
+//
+function addLoadEvent(func)
+{      
+       var oldonload = window.onload;
+       if (typeof window.onload != 'function'){
+       window.onload = func;
+       } else {
+               window.onload = function(){
+               oldonload();
+               func();
+               }
+       }
+
+}
+
+
+
+addLoadEvent(initLightbox);    // run initLightbox onLoad
diff --git a/include/loading.gif b/include/loading.gif
new file mode 100644 (file)
index 0000000..fbe57be
Binary files /dev/null and b/include/loading.gif differ
diff --git a/include/overlay.png b/include/overlay.png
new file mode 100644 (file)
index 0000000..7cee298
Binary files /dev/null and b/include/overlay.png differ
diff --git a/mkgallery.pl b/mkgallery.pl
new file mode 100755 (executable)
index 0000000..baa7cea
--- /dev/null
@@ -0,0 +1,277 @@
+#!/usr/bin/perl
+
+use strict;
+use Carp;
+use POSIX qw/getcwd/;
+use CGI qw/:html *table *center *div/;
+use Image::Info qw/image_info dim/;
+use Image::Magick;
+
+my $ask=1;
+my $startdir=getcwd;
+
+######################################################################
+
+&processdir($startdir);
+
+sub processdir {
+       my ($start,$dir)=@_;
+       my $dn=$start;
+       $dn .= "/".$dir if ($dir);
+       unless ( -d $dn ) {
+               warn "not a directory: $dn";
+               return;
+       }
+       my $D;
+       unless (opendir($D,$dn)) {
+               warn "cannot opendir $dn: $!";
+               return;
+       }
+
+# recurse into subdirectories BEFORE opening index file
+
+       &iteratedir($D,$start,$dir,sub {
+               my ($start,$dir,$base)=@_;
+               my $ndir = $dir;
+               $ndir .= "/" if ($ndir);
+               $ndir .= $base;
+               return unless ( -d $start."/".$ndir );
+               &processdir($start,$ndir);
+       });
+
+# fill in title
+
+       my $title=&gettitle($dn,$dir);
+
+# get include prefix
+
+       my $inc=&getinclude($dn);
+
+# generate directory index unless suppressed
+
+       if ( -e $dn."/.noindex" ) {
+               open(STDOUT,">/dev/null");
+       } else {
+               open(STDOUT,">".$dn."/index.html");
+       }
+
+# write HTML header
+
+       print start_html(-title => $title,
+                       -style=>{-src=>[$inc."gallery.css",
+                                       $inc."lightbox.css"]},
+                       -script=>[{-code=>"var incPrefix='$inc';"},
+                               {-src=>$inc."gallery.js"},
+                               {-src=>$inc."lightbox.js"}]),"\n";
+       print a({-href=>"../"},"UP");
+       print start_center,"\n";
+       print h1($title),"\n";
+
+# create list of sub-albums
+
+       my $hassubdirs=0;
+       &iteratedir($D,$start,$dir,sub {
+               my ($start,$dir,$base)=@_;
+               my $en=sprintf("%s/%s/%s",$start,$dir,$base);
+               return unless ( -d $en );
+               unless ($hassubdirs) {
+                       print hr,h2("Albums"),start_table,"\n";
+                       $hassubdirs=1;
+               }
+               &subalbum($base,&gettitle($en,$dir."/".$base));
+       });
+       print end_table,hr,"\n" if ($hassubdirs);
+
+# create picture gallery
+
+       my $haspics=0;
+       &iteratedir($D,$start,$dir,sub {
+               my ($start,$dir,$base)=@_;
+               my $en=sprintf("%s/%s/%s",$start,$dir,$base);
+               return unless ( -f $en );
+               $haspics=1 if (&processfile($start,$dir,$base,$en));
+       });
+
+# write HTML footer
+
+       print br({-clear=>"all"}),"\n";
+       print hr,"\n" if ($haspics);
+       print end_center,"\n";
+       print end_html,"\n";
+
+       close(STDOUT);
+       closedir($D);
+}
+
+#############################################################
+# helper functions
+#############################################################
+
+sub iteratedir {
+       my ($D,$start,$dir,$prog)=@_;
+       my @list=();
+       while (my $de=readdir($D)) {
+               next if ($de =~ /^\./);
+               push(@list,$de);
+       }
+       foreach my $de(sort @list) {
+               &$prog($start,$dir,$de);
+       }
+       rewinddir($D);
+}
+
+sub getinclude {
+       my ($dn)=@_;
+
+       my $depth=20;
+       my $str="";
+       #print STDERR "start include ",$dn."/".$str.".include","\n";
+       while ( ! -d $dn."/".$str.".include" ) {
+               #print STDERR "not include ",$dn."/".$str.".include","\n";
+               $str.="../";
+               last unless ($depth--);
+       }
+       #print STDERR "end include ",$dn."/".$str.".include","\n";
+       if ( -d $dn."/".$str.".include" ) {
+               #print STDERR "return include ".$str.".include/".$fn,"\n";
+               return $str.".include/";
+       } else {
+               return ""; # won't work anyway but return something
+       }
+}
+
+sub gettitle {
+       my ($dir,$dflt)=@_;
+
+       my $F;
+       my $str;
+       if (open($F,"<".$dir."/.title")) {
+               $str=<$F>;
+               chop $str;
+               close($F);
+       } else {
+               print STDERR "enter title for $dir\n";
+               $str=<>;
+               if ($str =~ /^\s*$/) {
+                       $str=$dflt;
+               }
+               if (open($F,">".$dir."/.title")) {
+                       print $F $str,"\n";
+                       close($F);
+               } else {
+                       print STDERR "cant open .title in $dir for writing: $!";
+               }
+       }
+       return $str;
+}
+
+sub subalbum {
+       my ($base,$title)=@_;
+
+       print Tr({-bgcolor=>"#c0c0c0"},
+               td(a({-href=>$base."/"},$base)),
+               td(a({-href=>$base."/"},$title))),"\n";
+}
+
+sub processfile {
+       my ($start,$dir,$base,$fn)=@_;
+
+       my $info = image_info($fn);
+       if (my $error = $info->{error}) {
+               if (($error !~ "Unrecognized file format") &&
+                   ($error !~ "Can't read head")) {
+                       print STDERR "File \"$fn\": $error\n";
+               }
+               return 0;
+       }
+       my ($w,$h) = dim($info);
+       my $title=$info->{'Comment'};
+       $title=$base unless ($title);
+       my $thumb=&scale($start,$dir,$base,$fn,160,$info);
+       my $medium=&scale($start,$dir,$base,$fn,640,$info);
+       print &infobox($info,$base,$fn),"\n";
+       print table({-class=>'slide'},Tr(td(
+               a({-href=>".info/$base.html",
+                       -onClick=>"return showIbox('$base');"},$title),
+               br,
+               a({-href=>$medium,-rel=>"lightbox",-title=>$title},
+                       img({-src=>$thumb})),
+               br,
+               a({-href=>$base},"($w x $h)"),
+               br))),"\n";
+       #for my $k(keys %$info) {
+       #       print "\t$k:\t$info->{$k}<br>\n";
+       #}
+       return 1;
+}
+
+sub infobox {
+       my ($info,$base,$fn)=@_;
+
+       my @infokeys=(
+               'DateTime',
+               'ExposureTime',
+               'FNumber',
+               'Flash',
+               'ISOSpeedRatings',
+               'MeteringMode',
+               'ExposureProgram',
+               'FocalLength',
+               'FileSource',
+               'Make',
+               'Model',
+               'Software',
+       );
+
+       my $msg=start_div({-class=>'ibox',-id=>$base,-OnClick=>"HideIbox('$base');"});
+       $msg.=span({-style=>'float: left;'},"Info for $base").
+               span({-style=>'float: right;'},
+                       a({-href=>"#",-OnClick=>"HideIbox('$base');"},"Close"));
+       $msg.=br({-clear=>'all'});
+       $msg.=start_table;
+       foreach my $k(@infokeys) {
+               $msg.=Tr(td($k.":"),td($info->{$k}));
+       }
+       $msg.=end_table;
+       $msg.=end_div;
+       return $msg;
+}
+
+sub scale {
+       my ($start,$dir,$base,$fn,$tsize,$info)=@_;
+       my ($w,$h) = dim($info);
+       my $max=($w>$h)?$w:$h;
+       my $factor=$tsize/$max;
+
+       return $base if ($factor >= 1);
+
+       my $tdir=sprintf "%s/%s/.%s",$start,$dir,$tsize;
+       mkdir($tdir,0755) unless ( -d $tdir );
+       my $tbase=sprintf ".%s/%s",$tsize,$base;
+       my $tfn=sprintf "%s/%s",$tdir,$base;
+       my @sstat=stat($fn);
+       my @tstat=stat($tfn);
+       return $tbase if (@tstat && ($sstat[9] < $tstat[9])); # [9] -> mtime
+
+       print STDERR "scale by $factor from $fn to $tfn\n";
+       &doscaling($fn,$tfn,$factor,$w,$h);
+       return $tbase;
+}
+
+sub doscaling {
+       my ($src,$dest,$factor,$w,$h)=@_;
+
+       my $im=new Image::Magick;
+       my $err;
+       #print STDERR "doscale $src -> $dest by $factor\n";
+       $err=$im->Read($src);
+       unless ($err) {
+               $im->Scale(width=>$w*$factor,height=>$h*$factor);
+               $err=$im->Write($dest);
+               warn "ImageMagic: write \"$dest\": $err" if ($err);
+       } else {
+               warn "ImageMagic: read \"$src\": $err";
+               system("djpeg \"$src\" | pnmscale \"$factor\" | cjpeg >\"$dest\"");
+       }
+       undef $im;
+}
diff --git a/mkindex.pl b/mkindex.pl
new file mode 100755 (executable)
index 0000000..99a9061
--- /dev/null
@@ -0,0 +1,62 @@
+#!/usr/bin/perl
+
+use strict;
+use Carp;
+use CGI qw/:html *table *Tr *center/;
+
+my @years=();
+my @subdirs=();
+
+opendir(D,'.') || die "cannot open current directory: $!";
+while (my $de=readdir(D)) {
+       next if ($de =~/^\./);
+       next unless (-d $de);
+       if ($de =~ /^\d\d\d\d$/) {
+               push(@years,$de);
+       } else {
+               push(@subdirs,$de);
+       }
+}
+closedir(D);
+
+my @mn=(
+       '',
+       'Jan',
+       'Feb',
+       'Mar',
+       'Apr',
+       'May',
+       'Jun',
+       'Jul',
+       'Aug',
+       'Sep',
+       'Oct',
+       'Nov',
+       'Dec',
+);
+
+print start_html(-title=>'Gallery'),"\n";
+print start_center,"\n";
+print h1("Gallery Index"),"\n";
+print start_table({-cellspacing=>3}),"\n";
+foreach my $yr(sort @years) {
+       print start_Tr,"\n";
+       print td({-bgcolor=>"#ffc0ff"},$yr);
+       for (my $mo=1;$mo<=12;$mo++) {
+               my $dir=sprintf "%04d/%02d",$yr,$mo;
+               if (-d $dir) {
+                       print td({-bgcolor=>"#ffffc0"},a({-href=>$dir.'/'},$mn[$mo]));
+               } else {
+                       print td({-bgcolor=>"#c0c0c0"},$mn[$mo]);
+               }
+       }
+       print end_Tr,"\n";
+}
+print end_table,p,"\n";
+
+foreach my $sub(sort @subdirs) {
+       print td(a({-href=>$sub.'/'},$sub));
+}
+
+print end_center,"\n";
+print end_html,"\n";