#!/usr/bin/perl
#
#########################################################################
#
# Image Display System 0.41      09/19/2000     
# John Moose    moosejc@muohio.edu
#
#########################################################################
#
# Please see the files "LICENSE," "README," and "INSTALL" for more
# information.
#
# Settings are now stored in "ids.conf".
#
# This software is distributed under the GNU Public License.
#
#########################################################################




# initialization

use CGI qw(:all);
use File::Basename;
use File::Find;
use File::Path;
use Image::Magick;
use Image::Info qw(image_info);

$imageQuality = 90;

readPreferences();

$query = new CGI;
$scaledOverlay = Image::Magick->new;
$preview = Image::Magick->new;



# Global variables:

($base,$path,$type) = fileparse($0, '\.[^.]+\z');
$idscgi= $base . $type; # get name of this script



# Initialize time variables
my($time) = time();

$currentDate = &prettyTime($time, 'date');
$currentTime = &prettyTime($time, 'time');

# Stuff for flock
$lock_sh = 1;
$lock_ex = 2;
$lock_nb = 4;
$lock_un = 8;

if ($displayScaledIcon eq 'y') {
	my($x) = $scaledOverlay->Read('site-images/previewicon.png'); # read in the "scaled" icon to overlay on scaled images
	warn "$x" if "$x";
}

# program main


processData();

if ($mode eq 'home') {
	generateHome();
} elsif ($mode eq 'album') {
	generateAlbum();
} elsif ($mode eq 'image') {
	generateImage();
} elsif ($mode eq 'search') {
	generateSearchResults();
} else {
	bail ("Sorry, invalid mode: $!");
}

openTemplate();
processVarTags();
renderPage();


########################################################################
# subfunctions

sub bail {
	# produces and displays an HTML error message
	#
	my $error = "@_";
	print header, start_html("Error");
	print h1("An error has occurred"), p($error),end_html;
	die "$error \n";
}

sub readPreferences {
	my ($preference);
	open (PREFS,"ids.conf") || bail ("can't open preferences: ($!)");
	line: while (<PREFS>) {
		next line if $_ =~ /^#|^\n/; #skip comments and blank lines
		chomp $_;
		my($tagtitle, $tagvalue) = split(/\s+/, $_);
		$preference{$tagtitle} = $tagvalue;
	}
	close (PREFS) || bail ("can't open preferences: ($!)");
	$maxDimension = $preference{maxDimension};
	$previewMaxDimension = $preference{previewMaxDimension};
	$imagesPerRow = $preference{imagesPerRow};
	$rowsPerPage = $preference{rowsPerPage};
	$scaledImageBorderWidth = $preference{scaledImageBorderWidth};
	$displayScaledIcon = $preference{displayScaledIcon};
	$imageQuality = $preference{imageQuality};
}

sub processData {
	# Interprets any form variables passed to the script. Checks to make sure the input makes sense.
	#
	if ($query->param('mode')) {
		$mode = $query->param('mode');
		chomp $mode;
		unless ($mode =~ /album|image|search/) {$mode = 'home';}
	} else {
		$mode = 'home';
	}
	
	if ($mode eq 'album') {
		$albumtodisplay = $query->param('album') || bail ("Sorry, no album name was provided: $!");
		$startItem =  $query->param('startitem');
		
		unless (-e "albums/$albumtodisplay") { # does this album exist?
			bail ("Sorry, that album doesn't exist: $!");
		}
		if ($albumtodisplay =~ /\.\./) { # hax0r protection...
			bail ("Sorry, invalid directory name: $!");
		}
	}
	
	if ($mode eq 'image') {
		$albumtodisplay = $query->param('album') || bail ("Sorry, no album name was provided: $!"); 
		$imagetodisplay = $query->param('image') || bail ("Sorry, no image name was provided: $!"); 
	}
	
	if ($mode eq 'search') {
		$searchString = $query->param('searchstring') || bail ("Sorry, no search string was provided: $!");
		unless ($searchString =~ /.{3,}/) {
			bail ("Sorry, search string must be 3 or more characters in length: $!");
		}
	}
	
	if ($query->cookie(-name=>'IDS_Cookie_MaxDimension')) {
		unless ($query->param('maxDimension')) {
			$maxDimension = $query->cookie(-name=>'IDS_Cookie_MaxDimension');
		} else {
			$maxDimension = $query->param('maxDimension');
		}
	}
	
	if ($query->cookie(-name=>'IDS_Cookie_LastVisit')) {
		$lastVisit = $query->cookie(-name=>'IDS_Cookie_LastVisit');
	}
	
	$query->delete_all();
}


sub encodeSpecialChars {
	my($textToEncode) = shift(@_);
	$textToEncode =~ s/([^a-z0-9\/\.])/sprintf("%%%02x",ord($1))/gei;
	return $textToEncode;
}

sub decodeSpecialChars {
	my($textToDecode) = shift(@_);
	$textToDecode =~ s/\%([\da-fA-F][\da-fA-F])/chr(hex($1))/gei;
	return $textToDecode;
}

sub accessCounter {
	my($accessFile) = $mode.$albumtodisplay.'_counter.txt';
	open (COUNTER,"+<$accessFile") || bail ("can't open counter file: ($!)"); # open the counter file for read/write
		flock COUNTER, $lock_ex;
		seek COUNTER, 0, 0;
		my($numOfAccesses) = (<COUNTER>);
		seek COUNTER, 0, 0;
		truncate COUNTER, 0;
		print COUNTER ($numOfAccesses + 1);
		flock COUNTER, $lock_un;
		return $numOfAccesses;
	close (COUNTER) || bail ("can't close counter file: ($!)");
}

sub prettyTime {
	my($time) = shift(@_);
	my($timeFunction) = shift(@_);
	my($sec,$min,$hour,$mday,$mon,$year) = localtime($time);
	my(@MoY) = ('Jan.','Feb.','Mar.','Apr.','May','Jun.','Jul.','Aug.','Sep.','Oct.','Nov.','Dec.');
	$year += 1900; #Y2K compliance!
	if ($hour < 10) {$hour = "0".$hour;}
	if ($min < 10) {$min = "0".$min;}
	if ($sec < 10) {$sec = "0".$sec;}
	$mon = $MoY[$mon];
	
	if ($timeFunction eq 'date') {
		my ($dateNTime) = "$mon $mday, $year";
		return $dateNTime;
	} elsif ($timeFunction eq 'time') {
		my ($dateNTime) = "$hour:$min:$sec";
		return $dateNTime;
	} else {
		my ($dateNTime) = "$mon $mday, $year $hour:$min:$sec";
		return $dateNTime;
	}
}

sub fileSize {
	my($item) = shift(@_);
	my($filesize) = ((-s $item) / 1024); #get the file's size in KB.
	if ($filesize > 1024) { # is it larger than a MB?
		$filesize = ($filesize / 1024);
		$filesize =~ s/(\d+\.\d)\d+/$1/;
		$filesize = $filesize."MB";
	} else {
		$filesize =~ s/(\d+)\.\d+/$1/;
		$filesize = $filesize."KB";
	}
	return $filesize;
}

sub getImageDimensions {
	my $imageName = shift(@_);
	my($preview) = Image::Magick->new;
	my($x) = $preview->Read($imageName); # read in the picture
	warn "$x" if "$x";
	my($xSize, $ySize) = $preview->Get('width', 'height'); # Get the pictures dimensions
	return ($xSize, $ySize);
}


sub filenameToPreviewName {
	my $imageName = shift(@_);
	my ($base,$path,$type) = fileparse($imageName, '\.[^.]+\z');
	$path =~ s/^\.\///;
	
	unlink ($path . $base . '_disp' . $previewMaxDimension . '.jpg'); #delete old-fashioned preview images
	
	$path =~ s/\Aalbums/image-cache/;
	my($newPreviewName) = $path . $base . '_disp' . $previewMaxDimension . '.jpg'; # what the thumbnail would be named
	return $newPreviewName;
}

sub generateAlbumEntry {
	my $album = shift(@_);
	
	$album =~ s/\balbums\///; # trims off 'albums/' from the filepath provided by glob.
	$album =~ s/\/\Z//;# trims off the trailing slash from the filepath provided by glob.
	$prettyalbum = $album;
	$prettyalbum =~ s/\#\d+_//g; # trims off numbers used for list ordering. ex: "#02_"
	$prettyalbum =~ s/_/ /g; # replaces underscores with spaces
	$previewName = 'site-images/smalldirectory.jpg';
	my($xSize, $ySize) = &getImageDimensions("$previewName");
	$result = '<span class="smallgreytext"><a href="'.$idscgi.'?mode=album&amp;album='.&encodeSpecialChars($album).'">'."<img src=\"".&encodeSpecialChars($previewName)."\" border=\"0\" width=\"$xSize\" height=\"$ySize\" alt=\"$prettyalbum\"><br>".$prettyalbum.'</a></span>'."\n";
}

sub generatePrevNext {
	my $albumName = shift(@_);
	my $imgName = shift(@_);
	my $title = shift(@_);
	my($base,$path,$type) = fileparse($imgName);
	my($previewName) = &filenameToPreviewName("$imgName");
	&createDisplayImage("$previewName", $previewMaxDimension);
	my($prettyImageTitle) = $base;
	$prettyImageTitle =~ s/\#\d+_//g;
	$prettyImageTitle =~ s/\.(\S+)\Z//;
	$prettyImageTitle =~ s/_/ /g;
	my($xSize, $ySize) = &getImageDimensions("image-cache/$albumName/$previewName");
	my($previewHTML) = "<a href=\"".$idscgi."?mode=image&amp;album=".&encodeSpecialChars($albumName)."&amp;image=".&encodeSpecialChars("$base$type")."\">"
				 . "$title<br>\n<img src=\"".&encodeSpecialChars("image-cache/$albumName/$previewName")."\" border=\"0\" "
				 . "width=\"$xSize\" height=\"$ySize\"><br>\n$prettyImageTitle</a>";
	return $previewHTML;
}

sub createDisplayImage {
	my $itemToDisplay = shift(@_);
	my $maxDimension = shift(@_);
	return if (-d $itemToDisplay); #this is a directory
	return unless ($itemToDisplay =~ /\.jpg\Z|\.jpeg\Z|\.gif\Z|\.png\Z/i); #this is a supported image filetype
	return if ($itemToDisplay =~ /_disp\d*\.jpg\Z/i); #this is a resized file

	my($newDisplayName) = &filenameToDisplayName($itemToDisplay, $maxDimension);
	my($pathToCacheDir) = $newDisplayName;
	$pathToCacheDir =~ s/(.+)\/[^\/]+\Z//;
	$pathToCacheDir = $1;
	
	unless (-e $pathToCacheDir) {
		mkpath($pathToCacheDir, 0, 0777);
	}
		
	# skip display file generation if a display file exists and is newer than image
	if (-e $newDisplayName) {
		warn "skipped $newDisplayName...";
		return if ((-M $itemToDisplay) > (-M $newDisplayName));
	}
		
	my($imageToScale) = Image::Magick->new;
	my($x) = $imageToScale->Read($itemToDisplay); # read in the picture
	warn "$x" if "$x";
	
	$imageToScale->Set(quality=>$imageQuality);

	my($xSize, $ySize) = $imageToScale->Get('width', 'height'); # Get the pictures dimensions
	
	if (($xSize <= $maxDimension) && ($ySize <= $maxDimension)) {  # image is less than maxsize and needs no rescaling
		warn "image is less than maxsize and needs no rescaling";	
		$x = $imageToScale->Border(color=>'black',width=>$scaledImageBorderWidth, height=>$scaledImageBorderWidth);
		warn "$x" if "$x";
		if ($displayScaledIcon eq 'y') {
			$x = $imageToScale->Composite(image=>$scaledOverlay,compose=>'over',geometry=>("+".($displayX - 14)."+".($displayY - 14))); #overlay the scaled icon
			warn "$x" if "$x";
		}
		$x = $imageToScale->Write($newDisplayName); #write out the thumbnail image file in JPEG format
		warn "$x" if "$x";
		return;
	}
	
	# calculate dimensions of display image
	my($scaleFactor);
	my($displayX);
	my($displayY);
	if ($xSize > $ySize) {
		$scaleFactor = $maxDimension / $xSize;
		$displayX = $maxDimension;
		$displayY = int($ySize * $scaleFactor);
		warn "scaling by $scaleFactor to $displayX, $displayY";
	} else {
		$scaleFactor = $maxDimension / $ySize;
		$displayY = $maxDimension;
		$displayX = int($xSize * $scaleFactor);
		warn "scaling by $scaleFactor to $displayX, $displayY";
	}
	
	$x = $imageToScale->Scale(width=>($displayX - (2 * $scaledImageBorderWidth)), height=>($displayY - (2 * $scaledImageBorderWidth))); #scale the image to the correct display size
	warn "$x" if "$x";
	
	$x = $imageToScale->Border(color=>'black',width=>$scaledImageBorderWidth, height=>$scaledImageBorderWidth);
	warn "$x" if "$x";
	
	if ($displayScaledIcon eq 'y') {
		$x = $imageToScale->Composite(image=>$scaledOverlay,compose=>'over',geometry=>("+".($displayX - 14)."+".($displayY - 14))); #overlay the scaled icon
		warn "$x" if "$x";
	}

	$x = $imageToScale->Write($newDisplayName); #write out the thumbnail image file in JPEG format
	warn "$x" if "$x";
	warn "saved as $newDisplayName";
	
	undef $imageToScale;
	
	if ($maxDimension eq $previewMaxDimension) {
		my ($oldpreviewname) = $newDisplayName;
		$oldpreviewname =~ s/_disp$previewMaxDimension/_pre/;
		unlink ($oldpreviewname);
	}
}

sub filenameToDisplayName {
	my $imageName = shift(@_);
	my $maxDimension = shift(@_);
	my ($base,$path,$type) = fileparse($imageName, '\.[^.]+\z');
	$path =~ s/^\.\///;
	
	unlink ($path . $base . '_disp' . $maxDimension . '.jpg'); #delete old-fashioned resized images
	
	$path =~ s/\Aalbums/image-cache/;
	my($newDisplayName) = $path . $base . '_disp' . $maxDimension . '.jpg'; # name for the resized image
	return $newDisplayName;
}

sub generateHome {
	#produces the top-level page, incorporating a list of albums and site news (if available)
	#
	my(@albums) =  glob "albums/*/"; #returns the names of all directories in the 'albums' directory
	my($totalalbums) = ($#albums) + 1;
	my($albumcounter) = 0; # will be used to count the number of albums displayed 
	
	$home = $home.'<table border="0" width="100%"><tr>';
	my($album);
	my($albumcounter) = 1;
	foreach $album (sort @albums) {
		$albumcounter ++;
		$home = $home . '<td width="133" align="center" valign="top">' . &generateAlbumEntry($album) . '<br><br></td>';
		if ($albumcounter > $imagesPerRow) { #create a multi-column list
			$home = $home."</tr><tr>\n";
			$albumcounter = 1;
		}
	}
	
	if (($albumcounter ne ($imagesPerRow + 1))) {
		for ($i = 0; $i < (($imagesPerRow + 1) - $albumcounter); $i++) {
			$home = $home."<td>&nbsp;</td>\n"; #put in cells to finish the row (necessary for Netscape)
		}
	}
	
	$home = $home.'</tr></table>';
	
	openNewsDesc("");	# read in site news
	
	#my($numOfAccesses) = accessCounter();
	$footer = "This page generated at $currentTime on $currentDate by <a href=\"http://ids.sourceforge.net/\">ids 0.41</a>. ". ($lastVisit ne '' ? "Your last visit was on ". &prettyTime($lastVisit, 'date'):"This is your first visit").".";
}

sub generateAlbum {
	#produces an album, displaying thumbnail pictures with names and filesizes. Displays a description of the album (if present).
	#
	my ($previousalbumtemp);
	
	if ($albumtodisplay =~ /\//) {
		($previousalbumtemp, $albumtitle) = $albumtodisplay =~ /^(.+)\/([^\/]+)$/;
		$previousalbum = "<a href=\"".$idscgi."?mode=album&amp;album=".&encodeSpecialChars($previousalbumtemp)."\">&lt; back to album</a>";
	} else {
		($albumtitle) = $albumtodisplay =~ /([^\/]+)$/;
		$previousalbum = "<a href=\"".$idscgi."\">&lt; main page</a>";
	}
	
	$albumtitle =~ s/\#\d+_//g; # trims off numbers used for list ordering. ex: "#02_"
	$albumtitle =~ s/_/ /g; # replaces underscores with spaces
	
	my($imagecounter) = 0;
	if ($startItem eq '') {
		$startItem = '1';
	}
	
	opendir ALBUMDIR, "albums/$albumtodisplay" || bail ("can't open \"albums/$albumtodisplay\" album directory: ($!)");
	my(@filesInAlbum) = grep !/^\.+/, readdir ALBUMDIR;
	closedir ALBUMDIR;
	
	# read in the 'exclude' list
	my(@exclude);
	open (EXCL, "albums/$albumtodisplay/.exclude");
	while (<EXCL>) {
		chomp;
		push(@exclude,$_);
	}
	close EXCL;

	my($fileInAlbum);
	my(@itemsToDisplay);
    my($imagesInAlbum);
    my($albumsInAlbum);
	
	THELIST:
    foreach $fileInAlbum (sort @filesInAlbum) {
    	$fileInAlbum = "albums/$albumtodisplay/" . $fileInAlbum;
		next if ($fileInAlbum =~ /_disp\d*\.jpg\Z/i); # this is a display image- ignore it
		next if ($fileInAlbum =~ /_pre.jpg\Z/i); # this is an old-style preview image- ignore it
		foreach (@exclude) {
			next if (/^$/);
			$excl = "albums/$albumtodisplay/" . $_;
			next THELIST, if ($excl eq $fileInAlbum);
		}
			
		if (-d "$fileInAlbum") { # Is this a subdirectory?
	        	push @itemsToDisplay, $fileInAlbum; # if so, remember its name
	        	$albumsInAlbum ++;
        }
        if ($fileInAlbum =~ /\.jpg\Z|\.jpeg\Z|\.gif\Z|\.png\Z|\.mov\Z|\.mpg\Z|\.mpeg\Z|\.mp3\Z/i) { # is this a known format?
			push @itemsToDisplay, $fileInAlbum; # if so, remember its name
			$imagesInAlbum ++;
		}
	}
	
	$albumitems = '<table border="0" cellpadding="5" cellspacing="0" width="100%">';
	$albumitems = $albumitems."<tr>\n";
	
	my($rowsInAlbum) = 0;
	my($imagecounter) = 0;
	my($startcounter) = 0;
	
	# generate album HTML
	my($itemToDisplay);
	foreach $itemToDisplay (sort @itemsToDisplay) {
		$startcounter ++;
		next if (($startcounter < $startItem) || ($startcounter > ($startItem + int($rowsPerPage * $imagesPerRow))));
		
		my($imageName) = $itemToDisplay;
		$imageName =~ s/([^\/]+)\/?$//; #trim off the directory path returned by glob
		$imageName = $1;
		
		my($base,$path,$type) = fileparse($itemToDisplay, '\.[^.]+\z');
		
		my($filesize) = &fileSize($itemToDisplay);
		
		my($previewImageSize);
		if ($type =~ /mov|mpg|mpeg|mp3/i) { #is this a known movie type?
			$previewName = 'site-images/genericmovie.jpg';
			my($xSize, $ySize) = &getImageDimensions("$previewName");
			my($prettyImageTitle) = $imageName;
			$prettyImageTitle =~ s/\#\d+_//g;
			my($filesize) = &fileSize($itemToDisplay);
			# create a link directly to the movie file
			$albumitems = $albumitems."<td align=\"center\" valign=\"middle\"><a href=\"albums/".&encodeSpecialChars("$albumtodisplay/$imageName")."\"><span class=\"smallgreytext\"><img src=\"".&encodeSpecialChars($previewName)."\" border=\"0\" width=\"$xSize\" height=\"$ySize\" alt=\"$prettyImageTitle\"><br>$prettyImageTitle</span></a><br><span class=\"smallgreytext\">$filesize</span></td>\n";
		} elsif (-d $itemToDisplay) { # this is a directory
			$previewName = 'site-images/directory.jpg';
			my($xSize, $ySize) = &getImageDimensions("$previewName");
			# create a link to the directory
			$dirToDisplay = $itemToDisplay;
			$imageName =~ s/\#\d+_//g; # trims off numbers used for list ordering. ex: "#02_"
			$imageName =~ s/_/ /g;
			$dirToDisplay =~ s/\Aalbums\///;
			$albumitems = $albumitems."<td align=\"center\" valign=\"middle\"><a href=\"".$idscgi."?mode=album&amp;album=".&encodeSpecialChars($dirToDisplay)."\"><span class=\"smallgreytext\"><img src=\"".&encodeSpecialChars($previewName)."\" border=\"0\" width=\"$xSize\" height=\"$ySize\" alt=\"$imageName\"><br>$imageName</span></a></td>\n";
		} else {
			#this must be an image file
			
			&createDisplayImage("$itemToDisplay", $previewMaxDimension);
			
			my($previewName) = &filenameToPreviewName($itemToDisplay);
			my($xSize, $ySize) = &getImageDimensions("$previewName");
			my($prettyImageTitle) = $imageName;
			$prettyImageTitle =~ s/\#\d+_//g;
			$albumitems = $albumitems."<td align=\"center\" valign=\"middle\"><a href=\"".$idscgi."?mode=image&amp;album=".&encodeSpecialChars($albumtodisplay)."&amp;image=".&encodeSpecialChars($imageName)."\"><span class=\"smallgreytext\"><img src=\"".&encodeSpecialChars($previewName)."\" border=\"0\" width=\"$xSize\" height=\"$ySize\" alt=\"$prettyImageTitle\"><br>$prettyImageTitle</span></a></td>\n";
		}
		
		$imagecounter ++;
		if ($imagecounter == $imagesPerRow) { #is it time to go to the next row?
			$albumitems = $albumitems."</tr><tr>\n";
			$imagecounter = 0;
			$rowsInAlbum ++;
		}
		
		last if ($rowsInAlbum eq $rowsPerPage);
	}
	
	if (($imagecounter ne $imagesPerRow) && ($imagecounter ne '0')) {
		for ($i = 0; $i < ($imagesPerRow - $imagecounter); $i++) {
			$albumitems = $albumitems."<td>&nbsp;</td>\n"; #put in cells to finish the row (necessary for Netscape)
		}
	}
	$albumitems = $albumitems."</tr>\n";

	for ($i = 0; $i < (($imagesInAlbum + $albumsInAlbum)/(int($rowsPerPage * $imagesPerRow))); $i++) {
		$linksToPages = $linksToPages .($i ne 0 ? " | " : "<br>");
		if (($i * (int($rowsPerPage * $imagesPerRow)) + 1) eq $startItem) {
			unless (($rowsPerPage * $imagesPerRow) >= ($imagesInAlbum + $albumsInAlbum)) {	
				$linksToPages = $linksToPages ."page " . ($i + 1);
			}
		} else {
			$linksToPages = $linksToPages . "<a href=\"$idscgi?mode=album&amp;startitem=".(($i * int($rowsPerPage * $imagesPerRow)) + 1)."&amp;album=".&encodeSpecialChars($albumtodisplay)."\">page " . ($i + 1) . "</a>";
		}
	}
	
	my ($lastItem) = $startItem + (int($rowsPerPage * $imagesPerRow)) - 1;
	if ($lastItem > ($imagesInAlbum + $albumsInAlbum)) {
		$lastItem = ($imagesInAlbum + $albumsInAlbum);
	}
	
	$albumitems = $albumitems . "<tr><td colspan=\"$imagesPerRow\" align=\"right\"><table width=\"100%\" align=\"right\"><tr><td valign=\"top\" align=\"left\"><span class=\"smallgreytext\"><br>";
	
	if ((($imagesInAlbum + $albumsInAlbum) == 1) || ($startItem == $lastItem)) {
		$albumitems = $albumitems . ' item ';
	} elsif (($imagesInAlbum + $albumsInAlbum) != 0) {
		$albumitems = $albumitems . ' items ';
	}
	
	if ((($imagesInAlbum + $albumsInAlbum) != 0)) {
		$albumitems = $albumitems . (($startItem ne $lastItem) ? ($startItem . "-") : "").$lastItem." of ".($imagesInAlbum + $albumsInAlbum)."</span></td><td valign=\"top\" align=\"right\"><span class=\"smallgreytext\">$linksToPages</span></td></tr></table></td></tr>";
	} else {
		$albumitems = $albumitems . "O items</span></td><td valign=\"top\" align=\"right\"><span class=\"smallgreytext\">$linksToPages</span></td></tr></table></td></tr>";
	}
	
	
	$albumitems = $albumitems.'</table>';
	
	#if (($imagesInAlbum eq '') and ($albumsInAlbum eq '')) {$imagesInAlbum = "0";}
	#$totalitems = $imagesInAlbum + $albumsInAlbum;
	#$totalitems = $totalitems.($totalitems == 1 ? ' item' : ' items') . ' in album'; # correct grammar!
	
	$description = openItemDesc("albums/$albumtodisplay");	# read in album desc

	$lastModified = &prettyTime((stat("albums/$albumtodisplay"))[9]);
	
	$footer = "The album \"$albumtitle\" displayed at $currentTime on $currentDate by <a href=\"http://ids.sourceforge.net/\">ids 0.41</a>.";

}

sub generateImage {
	#produces the a page to display an image. Provides image size, dimensions, type, and date uploaded. Can display a description (if present).
	#
	$albumtitle = $albumtodisplay;
	my($origXSize, $origYSize) = my($displayXSize, $displayYSize) = &getImageDimensions("albums/$albumtodisplay/$imagetodisplay");
	
	if ($origXSize >= $origYSize) {
		$origMaxSize = $origXSize;
	} else {
		$origMaxSize = $origYSize;
	}
	
	my($startItem) = 1;
	
	opendir ALBUMDIR, "albums/$albumtodisplay" || bail ("can't open \"albums/$albumtodisplay\" album directory: ($!)");
	my(@filesInAlbum) = grep !/^\.+/, readdir ALBUMDIR;
	closedir ALBUMDIR;
	
	# read in the 'exclude' list
	my(@exclude);
	open (EXCL, "albums/$albumtodisplay/.exclude");
	while (<EXCL>) {
		chomp;
		push(@exclude,$_);
	}
	close EXCL;

	my($fileInAlbum);
	my(@itemsToDisplay);
    my($imagesInAlbum);
    my($albumsInAlbum);
	
	THELIST2:
    foreach $fileInAlbum (sort @filesInAlbum) {
    	$fileInAlbum = "albums/$albumtodisplay/" . $fileInAlbum;
		next if ($fileInAlbum =~ /_disp\d*\.jpg\Z/i); # this is a display image- ignore it
		next if ($fileInAlbum =~ /_pre.jpg\Z/i); # this is an old-style preview image- ignore it
		foreach (@exclude) {
			next if (/^$/);
			$excl = "albums/$albumtodisplay/" . $_;
			next THELIST2, if ($excl eq $fileInAlbum);
		}
			
		if (-d "$fileInAlbum") { # Is this a subdirectory?
	        push @itemsToDisplay, $fileInAlbum; # if so, remember its name
	        $imagesInAlbum ++;
        }
        if ($fileInAlbum =~ /\.jpg\Z|\.jpeg\Z|\.gif\Z|\.png\Z|\.mov\Z|\.mpg\Z|\.mpeg\Z|\.mp3\Z/i) { # is this a known format?
			push @itemsToDisplay, $fileInAlbum; # if so, remember its name
			$albumsInAlbum ++;
		}
	}
	
	my ($imageCounter) = 0;
	
	foreach $itemToDisplay (sort @itemsToDisplay) {
		$imageCounter ++;
		my($imageName) = $itemToDisplay;
		$imageName =~ s/([^\/]+)\/?$//; #trim off the directory path returned by glob
		$imageName = $1;
		last if ($imageName eq $imagetodisplay);
	}
	
	for ($i = 0; $i < (($imagesInAlbum + $albumsInAlbum)/($rowsPerPage * $imagesPerRow)); $i++) {
		if (($imageCounter >= ($i * ($rowsPerPage * $imagesPerRow))) && ($imageCounter <= (($i * ($rowsPerPage * $imagesPerRow)) + ($rowsPerPage * $imagesPerRow)))) {
			$startItem = 1 + ($i * ($rowsPerPage * $imagesPerRow));
		}
	}
	
	
	
	$previousalbum = "<a href=\"".$idscgi."?mode=album&amp;album=".&encodeSpecialChars($albumtodisplay)."&amp;startitem=$startItem\">&lt; back to album</a>";
	
	opendir ALBUMDIR, "albums/$albumtodisplay" || bail ("can't open \"albums/$albumtodisplay\" album directory: ($!)");
	my(@filesInAlbum) = grep !/^\.+/, readdir ALBUMDIR;
	closedir ALBUMDIR;
	
	# read in the 'exclude' list
	my(@exclude);
	open (EXCL, "albums/$albumtodisplay/.exclude");
	while (<EXCL>) {
		chomp;
		push(@exclude,$_);
	}
	close EXCL;

	THELIST2:
	foreach $fileInAlbum (sort @filesInAlbum) {
		next if ($fileInAlbum =~ /_pre\.jpg\Z/i); # this is an old-style preview image- ignore it
		next if ($fileInAlbum =~ /_disp\d*\.jpg\Z/i); # this is a display image- ignore it
        foreach (@exclude) {
                next if (/^$/);
                $excl = "albums/$albumtodisplay/" . $_;
                next THELIST2, if ($excl eq $fileInAlbum);
        }

		if ($fileInAlbum =~ /\.jpg\Z|\.jpeg\Z|\.gif\Z|\.png\Z|\.mov\Z|\.mpg\Z|\.mpeg\Z|\.mp3\Z/i) { # is this a known format?
			push @imagesInAlbum, $fileInAlbum; # if so, remember its name
			unless ($fileInAlbum =~ /\.mov\Z|\.mpg\Z|\.mpeg\Z|\.mp3\Z/i) {
				push @imagesForPrevNext, $fileInAlbum; # for prev/next thumbs
			}
		}
	}

	undef $where;
	for ($[ .. $#imagesForPrevNext) {
		$where = $_, last if ($imagesForPrevNext[$_] eq $imagetodisplay);
	}

	if ($where > 0)	{ $prevthumb = &generatePrevNext($albumtodisplay,$imagesForPrevNext[$where-1],"&lt; previous"); }
	&createDisplayImage("albums/$albumtodisplay/$imagesForPrevNext[$where-1]", $previewMaxDimension);
	if ($where < $#imagesForPrevNext) { $nextthumb = &generatePrevNext($albumtodisplay,$imagesForPrevNext[$where+1],"next &gt;"); }
	&createDisplayImage("albums/$albumtodisplay/$imagesForPrevNext[$where+1]", $previewMaxDimension);


	if (( -r "albums/$albumtodisplay/.ids_size")  && ($maxDimension eq '')) {
		open (ALBUMSIZE, "<albums/$albumtodisplay/.ids_size") || warn "can't open albums/$albumtodisplay/.ids_size";
		$maxDimension = <ALBUMSIZE>;
		close (ALBUMSIZE);
		$maxDimension =~ s/\n//; # remove trailing newline
		unless ($maxDimension =~ /^\d+\Z/) {
			$maxDimension = '';
			warn "ignoring bad size constraint $maxDimension";
		}
	}

	my($filesize) = my($newFilesize) = &fileSize("albums/$albumtodisplay/$imagetodisplay");
	
	if (($maxDimension =~ /^\d+\Z/) && ($maxDimension < $origMaxSize)) {
		warn "Using smaller image.";
		&createDisplayImage("albums/$albumtodisplay/$imagetodisplay", $maxDimension);
		my $imagetoreallydisplay = &filenameToDisplayName("albums/$albumtodisplay/$imagetodisplay", $maxDimension);
		($displayXSize, $displayYSize) = &getImageDimensions("$imagetoreallydisplay");
		$image = "<img src=\"".&encodeSpecialChars("$imagetoreallydisplay")."\" width=\"$displayXSize\" height=\"$displayYSize\">";
		$newFilesize = &fileSize("$imagetoreallydisplay");
	} else {
		$image = "<img src=\"".&encodeSpecialChars("albums/$albumtodisplay/$imagetodisplay")."\" width=\"$displayXSize\" height=\"$displayYSize\">";
	}
	
	my($imageNameTrimmed) = $imagetodisplay;
	$imageNameTrimmed =~ s/\.(\S+)\Z//;
	my($fileExtension) = $1;
	
	my($filesize) = &fileSize("albums/$albumtodisplay/$imagetodisplay");
	
	my($image) = Image::Magick->new;
	my($x) = $image->Read("albums/$albumtodisplay/$imagetodisplay"); # read in the picture
	warn "$x" if "$x";
	
	$description2 = $image->Get('comment'); # Get comments embedded in the image file. Some digital cameras do this.
	
	$description2 =~ s/ignored tags.+//i; #removes garbage tags from Canon Powershot cameras
	$description2 =~ s/\$9000:.+//i; #removes garbage tags from Sony cameras

	$description = openItemDesc("albums/$albumtodisplay/$imageNameTrimmed");
	
	if ($description2 ne '') {
		$description = (($description ne '') ? $description . "<hr noshade size=\"1\" width=\"50\">" : '').$description2."\n<br>(embedded)";	
	}
	
	if ($description eq '') {
		$description = "<i>none</i>";
	}

	my($daysSinceMod) = (-M "albums/$albumtodisplay/$imagetodisplay");
	if ($daysSinceMod < 1) {
		$daysSinceMod = ((int($daysSinceMod * 24)) + 1)." hour".(((int($daysSinceMod * 24)) + 1) <= 1 ? '' : 's')." ago";
	} else {
		$daysSinceMod = int($daysSinceMod + .5)." day".((int($daysSinceMod + .5) < 2) ? '' : 's').' ago';
	}
	
	if ($fileExtension =~ /jpg|jpeg/i) {
		$fileExtension = "JPEG";
	} elsif ($fileExtension =~ /gif/i) {
		$fileExtension = "GIF";
	} elsif ($fileExtension =~ /png/i) {
		$fileExtension = "PNG";
	} 
	
	my($camerainfo) = image_info("albums/$albumtodisplay/$imagetodisplay");
	if (defined($camerainfo->{'ISOSpeedRatings'})) {
		$cam_iso = $camerainfo->{'ISOSpeedRatings'};
	} else {
		$cam_iso = "<i>unknown</i>";
	}
	
	if (defined($camerainfo->{'ExposureTime'})) {
			@exp_n = $camerainfo->{'ExposureTime'};
			$top = $exp_n[0][1] / $exp_n[0][0], unless ($exp_n[0][1] == 0);
	} elsif (defined($camerainfo->{'ShutterSpeedValue'})) {
			@frac = $camerainfo->{'ShutterSpeedValue'};
			if ($frac[0][1] == 0) {
				$time = $frac[0];
			} else {
				$time = $frac[0][0] / $frac[0][1];
			}
			$top = int(0.5 + exp($time * log(2)));
	}
	$cam_exp = "1/" . int($top) . "s";
	if (defined($camerainfo->{'FNumber'})) {
		@f_n = $camerainfo->{'FNumber'};
	} elsif (defined($camerainfo->{'ApertureValue'})) {
		@f_n = $camerainfo->{'ApertureValue'};
	}

    if ($f_n[0][1] == 0) {
    	$cam_f = "f" . $f_n[0];
	} else {
		$cam_f = "f" . $f_n[0][0] / $f_n[0][1], 
	}
	@fl_n = $camerainfo->{'FocalLength'};
	if (defined($camerainfo->{'FocalLength'})) {
			if ($fl_n[0][1] == 0) {
					$cam_flen = $fl_n[0]*5 . "mm";
			} else {
					$cam_flen = 5*$fl_n[0][0]/$fl_n[0][1] . "mm", unless ($fl_n[0][1] == 0);
			}
	} else {
		$cam_flen = "<i>unknown</i>";
	}
	
	my($cam_flash);
	my($cam_date);
	my($cam_make);
	my($cam_model);
	
	if (defined($camerainfo->{'Flash'})) {
		$cam_flash = $camerainfo->{'Flash'};
	} else {
		$cam_flash = "<i>unknown</i>";
	}
	if (defined($camerainfo->{'DateTimeOriginal'})) {
		$cam_date = $camerainfo->{'DateTimeOriginal'};
	} else {
		$cam_date = "<i>unknown</i>";
	}
	if (defined($camerainfo->{'Make'})) {
		$cam_make = $camerainfo->{'Make'};
		$cam_make =~ s/;\Z//; # Some cameras end this field with a semi-colon
	}
	if (defined($camerainfo->{'Model'})) {
		$cam_model = $camerainfo->{'Model'};
		$cam_model =~ s/;\Z//; # Some cameras end this field with a semi-colon
	} else {
		$cam_model = "<i>unknown</i>";
	}
	
	$caminfo = "<div class=\"smalltext\">Make: "."$cam_make"."<br>Model: $cam_model<br>Flash used: $cam_flash<br>Taken on: $cam_date<br></div></td><td><div class=\"smalltext\">ISO: $cam_iso<br>Focal length: $cam_flen<br>Shutter: $cam_exp<br>Aperture: $cam_f<br></div>", unless ($cam_make =~ /^$/);
	
	
	$pictureinfo = "Image type: $fileExtension<br>File size: $newFilesize ($filesize)<br>Image size: $displayXSize x $displayYSize ($origXSize x $origYSize)<br>Uploaded: $daysSinceMod";
	
	$imagetitle = $imageNameTrimmed;
	$imagetitle =~ s/\#\d+_//g;
	$imagetitle =~ s/_/ /g;
	
	$imageResizer = "
		<form action=\"$idscgi\" method=\"get\">
		<input type=\"hidden\" value=\"image\" name=\"mode\">
		<input type=\"hidden\" value=\"$albumtodisplay\" name=\"album\">
		<input type=\"hidden\" value=\"$imagetodisplay\" name=\"image\">
		<select name=\"maxDimension\" size=\"1\">
			".(512 < $origMaxSize ? "<option value=\"512\"".($maxDimension eq '512' ? ' selected' : '').">tiny (512)" : '')."
			".(640 < $origMaxSize ? "<option value=\"640\"".($maxDimension eq '640' ? ' selected' : '').">small (640)" : '')."
			".(800 < $origMaxSize ? "<option value=\"800\"".($maxDimension eq '800' ? ' selected' : '').">medium (800)" : '')."
			".(1024 < $origMaxSize ? "<option value=\"1024\"".($maxDimension eq '1024' ? ' selected' : '').">large (1024)" : '')."
			".(1600 < $origMaxSize ? "<option value=\"1600\"".($maxDimension eq '1600' ? ' selected' : '').">x-large (1600)" : '')."
			<option value=\"9999\"";
	if ($maxDimension >= $origMaxSize) {
		$imageResizer = $imageResizer . ' selected';
	}
	$imageResizer = $imageResizer . ">original
		</select> <input type=\"submit\" value=\"&nbsp;&nbsp;OK&nbsp;&nbsp;\">
		</form>";
	
	$footer = "\"$imagetitle\" presented at $currentTime on $currentDate by <a href=\"http://ids.sourceforge.net/\">ids 0.41</a>.";

}

sub generateSearchResults {
	$newSearchString = $searchString;
	$newSearchString =~ s/\./\\./g;  # turn '.' into '\.' (dot is regex wildcard)
	$newSearchString =~ s/\?/\\S/g;  # turn '?' into '\S' (perl's way of doing a one char match)
	$newSearchString =~ s/\*/\\S+/g;  # turn '*' into '\S+' (perl's way of doing a many char match)
	
	my (@filenames) = split (/\s+/, $newSearchString);
	
	find (\&searchForFiles, "albums/");
	
	$searchResults = $searchResults . '<table border="0" cellpadding="5" cellspacing="0" width="100%">';
	$searchResults = $searchResults . "<tr>\n";
	
	
	my ($imagesInAlbum);
	
	# generate album HTML
	my($itemToDisplay);
	foreach $itemToDisplay (sort @searchResultFiles) {
		($trash, $itemToDisplay, $descriptionTmp) = split (/:::/, $itemToDisplay);
		my($pathtofile) = $itemToDisplay;
		$itemToDisplay =~ s/\A.+\/\/\///;
		&createDisplayImage("$itemToDisplay", $previewMaxDimension); #create preview
		$imagesInAlbum ++;
		unless ($imagesInAlbum eq 1) {
			$searchResults = $searchResults."<td colspan=\"2\"><hr noshade size=\"1\" width=\"200\">\n</td></tr><tr>";
		}
		
		my($base,$path,$type) = fileparse($itemToDisplay, '\.[^.]+\z');
		
		my($imageName) = $itemToDisplay;
		$imageName =~ s/([^\/]+)\/?$//; #trim off the directory path returned by glob
		$imageName = $1;
		
		$albumtodisplay = $itemToDisplay;
		$albumtodisplay =~ /^(.+)\/([^\/]+)$/;
		$albumtodisplay = $1;
		$albumtodisplay =~ s/albums\///;
		$dirToDisplay = $albumtodisplay;
		
		my($previewImageSize);
		my($prettyImageTitle);
		if ($type =~ /mov|mpg|mpeg|mp3/i) { #is this a known movie type?
			$previewName = 'site-images/genericmovie.jpg';
			my($xSize, $ySize) = &getImageDimensions("$previewName");
			$prettyImageTitle = $imageName;
			$prettyImageTitle =~ s/\#\d+_//g;
			$prettyImageTitle =~ s/($newSearchString)/<span class="redtext">$1<\/span>/isg;
			# create a link directly to the movie file
			$searchResults = $searchResults."<td align=\"center\" valign=\"middle\"><a href=\"albums/".&encodeSpecialChars("$albumtodisplay/$imageName")."\"><img src=\"".&encodeSpecialChars($previewName)."\" border=\"0\" width=\"$xSize\" height=\"$ySize\" alt=\"$imageName\"></a></td>\n";
		} elsif (-d $itemToDisplay) { # this is a directory
			$previewName = 'site-images/directory.jpg';
			my($xSize, $ySize) = &getImageDimensions("$previewName");
			# create a link to the directory
			$dirToDisplay = $itemToDisplay;
			$imageName =~ s/\#\d+_//g; # trims off numbers used for list ordering. ex: "#02_"
			$imageName =~ s/_/ /g;
			$prettyImageTitle = $imageName;
			$prettyImageTitle =~ s/\#\d+_//g;
			$prettyImageTitle =~ s/($newSearchString)/<span class="redtext"><b>$1<\/b><\/span>/isg;
			$dirToDisplay =~ s/\Aalbums\///;
			$dirToDisplay =~ s/\/\Z//;
			$searchResults = $searchResults."<td align=\"center\" valign=\"middle\"><a href=\"".$idscgi."?mode=album&amp;album=".&encodeSpecialChars($dirToDisplay)."\"><img src=\"".&encodeSpecialChars($previewName)."\" border=\"0\" width=\"$xSize\" height=\"$ySize\" alt=\"$imageName\"></a></td>\n";
			$dirToDisplay =~ s/[\/]*$albumtodisplay//;
		} else {
			#this must be an image file
			my($previewName) = &filenameToPreviewName($itemToDisplay);
			my($xSize, $ySize) = &getImageDimensions("$previewName");
			my($imageNameTrimmed) = $imageName;
			$imageNameTrimmed =~ s/\.(\S+)\Z//;
			$prettyImageTitle = $imageName;
			$prettyImageTitle =~ s/\#\d+_//g;
			$prettyImageTitle =~ s/($newSearchString)/<span class="redtext"><b>$1<\/b><\/span>/isg;
			$searchResults = $searchResults."<td align=\"center\" valign=\"middle\"><a href=\"".$idscgi."?mode=image&amp;album=".&encodeSpecialChars($albumtodisplay)."&amp;image=".&encodeSpecialChars($imageName)."\"><img src=\"".&encodeSpecialChars($previewName)."\" border=\"0\" width=\"$xSize\" height=\"$ySize\" alt=\"$imageName\"></a></td>\n";
		}
		
		$descriptionTmp = '>'.$descriptionTmp;
		$descriptionTmp =~ s/(>[^<]*)($newSearchString)/$1<span class="redtext"><b>$2<\/b><\/span>/isg;
		$descriptionTmp =~ s/\A>//;
		
		if ($descriptionTmp eq '') {
			$descriptionTmp = "<i>no description provided</i>";
		}
		
		$searchResults = $searchResults."<td valign=\"top\" width=\"300\"><div class=\"smallgreytext\">$prettyImageTitle<p>Description: $descriptionTmp </p><p>Location: <a href=\"".$idscgi. ($dirToDisplay ne '' ? "?mode=album&amp;album=".&encodeSpecialChars($dirToDisplay) : "")."\">/".$dirToDisplay."</a><br>Last modified: ".&prettyTime((stat("$pathtofile"))[9])."</p></div><br></td>";
		
		$searchResults = $searchResults."</tr><tr>\n";
	}
	
	if (($imagesInAlbum eq '') and ($albumsInAlbum eq '')) {$searchResults = $searchResults. "<td colspan=\"2\"><span class=\"misctext\">Sorry, please try again.</span></td>";}
	
	$searchResults = $searchResults."</tr>\n";
	$searchResults = $searchResults.'</table>';
	
	if (($imagesInAlbum eq '') and ($albumsInAlbum eq '')) {$imagesInAlbum = "0";}
	$totalitems = $imagesInAlbum + $albumsInAlbum;
	$totalitems = $totalitems.($totalitems == 1 ? ' item' : ' items').' found'; # correct grammar!
	
	$previousalbum = "<a href=\"".$idscgi."\">&lt; main page</a>";
	$footer = "Search results generated at $currentTime on $currentDate by <a href=\"http://ids.sourceforge.net/\">ids 0.41</a>.";
}

sub searchForFiles {
	my ($fileNameTemp) = $File::Find::name;
	
	return if ($fileNameTemp =~ /_disp\d*\.jpg\Z/i); #this is an IDS scaled image
	return if ($fileNameTemp eq 'albums/');
	
	unless ($fileNameTemp =~ /\/.+\.\S\S\S\S?\Z/) { 
		$fileNameTemp = $fileNameTemp . '/'; 
	}
	
	my ($textToSearch) = $fileNameTemp;

	$textToSearch =~ /\/([^\/]+\/?)$/; #trim off directory path
	$textToSearch = $1;
	
	return unless ($fileNameTemp =~ /\.jpg\Z|\.jpeg\Z|\.gif\Z|\.png\Z|\.mov\Z|\.mpg\Z|\.mpeg\Z|\.mp3\Z|\/\Z/i); #this is not a supported file type or directory
	return if ($fileNameTemp =~ /^albums\/\Z/); #this is just the albums directory.
	
	my($base,$path,$type) = fileparse($fileNameTemp, '\.[^.]+\z');
		
	my($descfilepath) = $textToSearch;
	
	$descfilepath =~ s/\/\Z//;
	$descfilepath =~ s/\.\S\S\S\S?//;
	my($descriptionTmp) = openItemDesc($descfilepath);
		
	return unless (($textToSearch =~ /$newSearchString/ig) || ($descriptionTmp =~ /$newSearchString/ig));
	
	$description =~ s/::://;
	
	push (@searchResultFiles, ($textToSearch. ':::' .$fileNameTemp . ':::' . $descriptionTmp));
}

sub openTemplate {
	#opens an html template file
	#
	open (TEMPLATE,'site-templates/'.$mode.'.html') || bail ("cannot open $mode template for reading: ($!)");
		$pageContent = (join "", <TEMPLATE>);
	close (TEMPLATE) || bail ("can't close $mode template: ($!)");
}

sub openNewsDesc {
	# open news file found at given path
	#
	# each line of an IDS newsfile takes the following format:
	#          news date:::news subject:::news body
	#
	my $path = shift(@_);
	my $file;

	if ( $path ne "" ) { $path = "$path/"; }

	# try to open "local_news.html", "site_news.html", "site_news.txt"
	# first come, first serve

	$file = $path.'site_news.txt';
	
	$sitenews = '<table border="0">';
	
	if (open (NEWS, $file)) {
		line2: 
		while (<NEWS>) {
			next line2 if $_ =~ /^#|^\n/; #skip comments and blank lines
			chomp $_;
			my($newsDate, $newsSubject, $newsBody) = split(/:::/, $_);
			$newsBody =~ s/\|\|\|/\n/g;
			$newsDate =~ s/\A(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)\d\d\Z/$2\/$3\/$1<br>$4:$5/;
			$sitenews = $sitenews . "<tr><td valign=\"top\" align=\"center\"><span class=\"misctext\">$newsDate</span></td><td valign=\"top\"><span class=\"misctext\"><b>$newsSubject</b></span></td></tr><tr><td></td><td><div class=\"misctext\">$newsBody<br><br></div></td></tr>";
		}
		close (NEWS) || bail ("can't close $file: ($!)");
	} else {
		$sitenews = "<tr><td>Sorry, no news file found at \"$file\".</td></tr>";
	}
	
	$sitenews = $sitenews . '</table>';
}

sub openItemDesc {
	my $path = shift(@_);
	my $file;
	my ($description) = "";
	
	$file = $path.'_desc.txt';
	
	if (-e $file) {
		open (DESC, $file) || bail ("can't open $file: ($!)");;
		$description = (join "", <DESC>);
		close (DESC) || bail ("can't close $file: ($!)");
	}
	
	return $description;
}


sub processVarTags {
	# Prepare the HTML needed to create a searchbox 
	$searchBox = '<form action="' . $idscgi . '" method="get"><input type="hidden" value="search" name="mode"><input type="text" name="searchstring" size="24" value="' . $searchString . '"><br><input type="submit" value="&nbsp;Search&nbsp;"></form>';
	
	
	# remove relative site-path and ids_style-path from template
	$pageContent =~ s/\=\"\.\.\/[\.\/]*(site-)/\=\"$1/sg;
	$pageContent =~ s/\=\"\.\.\/[\.\/]*(ids_style)/\=\"$1/sg;

	# Replaces variable tags from the template with content.
	# <!tag> and comment style <!-- tag --> are found
	# use /sg , so tag is replaced all times

	#  first replace news and desc, so they can also contain var tags
	#
	$sitenews =~ s/.*<body.*?>(.*?)<\/body.*/$1/is;
	$description =~ s/.*<body.*?>(.*?)<\/body.*/$1/is;
	$pageContent =~ s/<![- ]*sitenews[- ]*>/$sitenews/sg;
	$pageContent =~ s/<![- ]*description[- ]*>/$description/sg;
	
	$pageContent =~ s/<![- ]*lastmodified[- ]*>/$lastModified/sg;
	$pageContent =~ s/<![- ]*albumlist[- ]*>/$home/sg;
	$pageContent =~ s/<![- ]*albumtitle[- ]*>/$albumtitle/sg;
	$pageContent =~ s/<![- ]*footer[- ]*>/$footer/sg;
	$pageContent =~ s/<![- ]*albumitems[- ]*>/$albumitems/sg;
	$pageContent =~ s/<![- ]*totalpictures[- ]*>/$totalitems/sg;	
	$pageContent =~ s/<![- ]*imagetitle[- ]*>/$imagetitle/sg;
	$pageContent =~ s/<![- ]*image[- ]*>/$image/sg;
	$pageContent =~ s/<![- ]*prevthumb[- ]*>/$prevthumb/sg;
	$pageContent =~ s/<![- ]*nextthumb[- ]*>/$nextthumb/sg;
	$pageContent =~ s/<![- ]*previousalbum[- ]*>/$previousalbum/sg;
	$pageContent =~ s/<![- ]*pictureinfo[- ]*>/$pictureinfo/sg;
	$pageContent =~ s/<![- ]*caminfo[- ]*>/$caminfo/sg;
	$pageContent =~ s/<![- ]*imageresizer[- ]*>/$imageResizer/sg;
	$pageContent =~ s/<![- ]*searchresults[- ]*>/$searchResults/sg;
	$pageContent =~ s/<![- ]*searchstring[- ]*>/$searchString/sg;
	$pageContent =~ s/<![- ]*searchbox[- ]*>/$searchBox/sg;
}

sub renderPage {
	# Spits out a page of HTML.
	#
	$cookie1 = $query->cookie(-name=>'IDS_Cookie_LastVisit',
                             -value=>time(), #the current time
                             -expires=>'+2M'); #expires in 2 months
    $cookie2 = $query->cookie(-name=>'IDS_Cookie_MaxDimension',
                             -value=>$maxDimension, #the current max image dimension
                             -expires=>'+2M'); #expires in 2 months
	
	print $query->header(-type=>'text/html',
						 -expires=>'now',
						 -cookie=>[$cookie1,$cookie2]);
	print $pageContent;
}



