From: Martin Mares Date: Sun, 8 Feb 2015 20:16:51 +0000 (+0100) Subject: The big rename X-Git-Url: http://mj.ucw.cz/gitweb/?a=commitdiff_plain;h=d94da8d601fa080700f1d610089ec5f602d5ee88;p=gallery.git The big rename Everything moved to the top-level directory, except for the Perl modules, which now reside in lib/... --- diff --git a/FORMAT b/FORMAT new file mode 100644 index 0000000..059f2af --- /dev/null +++ b/FORMAT @@ -0,0 +1,58 @@ +List of photos (gallery.list) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +One photo per line, tab-separated columns: + + File name (relative to OrigDir in config) + Identifier (16 hex digits) + Orientation: one of "l", "r", "d", "." + Transformation -- sequence of: + n normalize contrast + s sharpen + h equalize histogram + Title (UTF-8 string) + +Lines starting with "#" are ignored. + +Lines starting with a tab add further attributes to the previous photo. +The following attributes are recognized: + + lat geographic latitude in degrees north of equator + lon geographic longitude in degrees east of Greenwich + alt geographic altitude in meters + t time when the photo was taken (2014-01-25 09:40:12) + + +Importing meta-data from other sources +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Occasionally, you want to add photos from a source which already provides +some meta-data. In this case, you can construct a gallery.list with a subset +of fields, or feed the stdin of "gal scan" with such a list. + +For convenience, orientation, transformation and title can be also given +as named attributes. They are called "orientation", "xf", and "title". + + +Photo meta-data (PhotoDir/gallery.meta or CacheDir/cache.meta) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Perl Storable containing a single hash. + +$meta->{photo}->{$identifier} is a hash of: + o orientation + xf transformation applied + w width after scaling + h height after scaling + w0, h0 width and height of original image + title photo title + fmt photo format (png/jpg; defaults to jpg) + lat geographic latitude in degrees north of equator + lon geographic longitude in degrees east of Greenwich + alt geographic altitude in meters + t time when the photo was taken (2014-01-25 09:40:12) + +The rest is present in cache.meta only: + +$meta->{sequence} is an array of photo IDs as they appear in the gallery. + +$meta->{thumb}->{$format}->{$identifier} is a hash of: + w thumbnail width + h thumbnail height diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f7d01c2 --- /dev/null +++ b/Makefile @@ -0,0 +1,18 @@ +$(eval $(dir-setup)) + +$(call lib-copy, UCW/Gallery.pm) +$(call lib-copy, $(addprefix UCW/Gallery/, Web.pm Hashes.pm Archive.pm)) +$(call lib-copy, $(addprefix UCW/Gallery/Web/, Plain.pm NrtBlue.pm HighSlide.pm)) + +$(call copy, $(addprefix nrt-blue/,back.png bot.png left.png next.png prev.png right.png top.png style.css)) +$(call copy, $(addprefix plain/,back.png next.png prev.png style.css)) +$(call copy, $(addprefix highslide/nav/,back.png next.png prev.png)) + +$(call copy, $(addprefix highslide/, highslide.css highslide-ie6.css highslide-with-gallery.js custom.css custom.js)) +$(call copy, $(addprefix highslide/graphics/, \ + close.png closeX.png controlbar-black-border.gif controlbar-text-buttons.png controlbar-white-small.gif \ + controlbar-white.gif controlbar2.gif controlbar3.gif controlbar4-hover.gif controlbar4.gif fullexpand.gif \ + geckodimmer.png icon.gif loader.big.black.gif loader.big.white.gif loader.black.gif loader.white.gif \ + resize.gif scrollarrows.png zoom.png zoomin.cur zoomout.cur)) +$(call copy, $(addprefix highslide/graphics/outlines/, \ + beveled.png custom.png drop-shadow.png glossy-dark.png outer-glow.png rounded-black.png rounded-white.png)) diff --git a/README b/README new file mode 100644 index 0000000..0a5d7c7 --- /dev/null +++ b/README @@ -0,0 +1,88 @@ +================================================================================ + + UCW::Gallery v2.0 + + (c) 2004--2015 Martin Mares + +================================================================================ + +This is a simple photo gallery for my web pages, or rather a set of bricks +from which you can create one. It can be freely used and distributed according +to the terms of the GNU GPL v2 or newer. + +Requirements +~~~~~~~~~~~~ +Perl with the following non-core modules: + + o UCW::CGI (see http://www.ucw.cz/libucw/) + o Image::Magick + o Image::Exif + o Archive::Zip + o Digest::SHA + +Files and directories +~~~~~~~~~~~~~~~~~~~~~ + + o gal -- a front-end script for maintenance of galleries. All other programs + are run as sub-commands of this script. Try "gal --help". + + o gallery.cf -- all programs expect that the current directory contains + a configuration file. In fact, the config file is a perl script, whose + sole purpose is to set up paths, construct a gallery object and set its + options. + + A simple example looks as follows: + + use UCW::Gallery; + + my $gal = UCW::Gallery->new; + $gal->set(Title => 'A Gallery', SubTitle => '(an example)'); + return $gal; + + o gallery.list -- a list of original photos together with per-photo parameters. + Usually created by "gal scan", then tweaked manually. See `FORMATS' for + description of file syntax. + + o gallery.cache -- used internally by "gal scan" to store cached image hashes. + + o Originals directory -- original images, from which the gallery is generated. + + o Photo directory -- keeps processed (usually down-scaled) versions of the original + images. These are the images shown to the user. + + o Cache directory -- keeps various cached data, like thumbnails of all photos. + + o gallery.cgi -- interfaces the gallery to your web server. E.g.: + + #!/usr/bin/perl + + use lib "../path/to/gallery/modules"; + use UCW::Gallery; + use UCW::Gallery::Web::Plain; + + my $gal = UCW::Gallery->load_config(); + UCW::Gallery::Web::Plain->run($gal); + +Workflow +~~~~~~~~ + + o vi gallery.cf + + o gal scan /path/to/originals -- this creates gallery.list, populates it with + descriptions of all photos and reads rotation from EXIF tags. + + o vi gallery.list -- edit image descriptions and other options. + + o gal gen -- generate photos from the originals (after this, the originals are no + longer needed). + + o gal cache -- generate cached data. + + o Later, the gallery can be updated: + + - When you edit image descriptions, re-run "gal cache". + - When you edit image options (rotation etc.), re-run "gal gen". + - When you want to add new images, re-run "gal scan". Give it the new list + of images and it will try to re-use as much information from the previous + gallery.list as possible. + - When you modify existing images, run "gal scan --update". diff --git a/bin/gal-cache b/bin/gal-cache new file mode 100755 index 0000000..73c97db --- /dev/null +++ b/bin/gal-cache @@ -0,0 +1,92 @@ +#!/usr/bin/perl +# UCW Gallery: Prepare cache +# (c) 2004--2012 Martin Mares + +use strict; +use warnings; + +use UCW::Gallery; +use Image::Magick; +use IO::Handle; +use File::Spec; +use File::Path; + +STDOUT->autoflush(1); + +my $gal = UCW::Gallery->load_config; + +print "Reading gallery.list\n"; +my $orig_list = $gal->read_list('gallery.list') or die "Cannot read gallery.list: $!\n"; + +my $photo_meta = $gal->photo_meta_name; +print "Reading meta-data from $photo_meta\n"; +-f $photo_meta or die "Cannot load $photo_meta\n"; +my $meta = $gal->read_meta($photo_meta); + +my $cache_dir = $gal->get('CacheDir'); +if (-d $cache_dir) { + print "Deleting old cache directory: $cache_dir\n"; + File::Path::remove_tree($cache_dir); +} +print "Creating cache directory: $cache_dir\n"; +File::Path::mkpath($cache_dir) or die "Unable to create $cache_dir: $!\n"; + +# Construct sequence and store photo titles +$meta->{sequence} = []; +for my $f (@$orig_list) { + push @{$meta->{sequence}}, $f->{id}; + my $m = $meta->{photo}->{$f->{id}} or die; + $m->{title} = $f->{title}; +} + +for my $thumb_fmt (@{$gal->get('ThumbFormats')}) { + print "Generating $thumb_fmt thumbnails\n"; + my ($tw, $th) = $gal->thumb_fmt_to_size($thumb_fmt); + my $thumb_meta = {}; + $meta->{thumb}->{$thumb_fmt} = $thumb_meta; + my $thumb_dir = File::Spec->catfile($cache_dir, $thumb_fmt); + -d $thumb_dir or File::Path::mkpath($thumb_dir) or die "Unable to create $thumb_dir: $!\n"; + + for my $id (@{$meta->{sequence}}) { + my $m = $meta->{photo}->{$id} or die; + print "\t$id: "; + + my $p = new Image::Magick; + my $photo = $gal->photo_name($m, $id); + my $e; + $e = $p->Read($photo) and die "Error reading $photo: $e"; + + my $w = $m->{w}; + my $h = $m->{h}; + if ($w > $tw) { + my $s = $tw / $w; + $w = $w * $s; + $h = $h * $s; + } + if ($h > $th) { + my $s = $th / $h; + $w = $w * $s; + $h = $h * $s; + } + $w = int($w + .5); + $h = int($h + .5); + print "${w}x${h} "; + $p->Resize(width=>$w, height=>$h); + + my $out = File::Spec->catfile($thumb_dir, "$id.jpg"); + my $tmp = "$photo.new"; + $e = $p->Write($tmp) and die "Unable to write $tmp: $e"; + rename $tmp, $out or die "Unable to rename $tmp to $out: $!\n"; + + $thumb_meta->{$id} = { + 'w' => $w, + 'h' => $h, + }; + + print "... OK\n"; + } +} + +my $cache_meta = $gal->cache_meta_name; +print "Writing meta-data to $cache_meta\n"; +$gal->write_meta($cache_meta, $meta); diff --git a/bin/gal-date b/bin/gal-date new file mode 100755 index 0000000..451201c --- /dev/null +++ b/bin/gal-date @@ -0,0 +1,98 @@ +#!/usr/bin/perl +# UCW Gallery: Symlink photos according to their EXIF timestamps +# (c) 2013 Martin Mares + +use strict; +use warnings; + +use Image::EXIF; +use Getopt::Long; + +if (@ARGV && $ARGV[0] eq '--help') { + die <] + or: gal date [] + +Options: +-d, --destdir= Create symlinks in specified directory (default: current dir) +-n, --dry-run Perform a trial run with no changes made +-o, --offset= Adjust timestamps by a given offset ([:[:]]) + --suffix= Add suffix to all names +-s, --symbolic Create symbolic links (default: hardlinks) +AMEN +} + +my $destdir = '.'; +my $dry_run = 0; +my $offset = 0; +my $suffix = ""; +my $symbolic = 0; +Getopt::Long::Configure('bundling'); +GetOptions( + 'destdir|d=s' => \$destdir, + 'dry-run|n!' => \$dry_run, + 'offset|o=s' => \$offset, + 'suffix=s' => \$suffix, + 'symbolic|s!' => \$symbolic, +) or die "Try gal date --help\n"; + +my @src = @ARGV; +if (!@src) { + while () { + chomp; + push @src, $_; + } +} + +if ($offset =~ m{^([+-])?(\d)+(:(\d+)(:(\d+))?)?$}) { + $offset = $2 * 3600 + ($4 // 0) * 60 + ($6 // 0); + if ($1 eq '-') { $offset = -$offset; } +} else { + die "Invalid offset: $offset\n"; +} + +foreach my $f (@src) { + my $e = new Image::EXIF($f); + my $i = $e->get_all_info(); + if ($e->error) { + print STDERR "EXIF error on $f: ", $e->error, "\n"; + next; + } + # print STDERR Dumper($i), "\n"; + + my $d = $i->{'image'}->{'Image Created'}; + if (!defined $d) { + print STDERR "No date for $f\n"; + next; + } + my ($ty, $tm, $td, $tH, $tM, $tS) = ($d =~ m{^(\d{4}):(\d{2}):(\d{2}) (\d{2}):(\d{2}):(\d{2})$}) or die "EXIF date parse error: $d\n"; + my $t = 3600*$tH + 60*$tM + $tS; + $t = int($t + $offset); + $tH = int($t / 3600); + $tM = int(($t % 3600) / 60); + $tS = $t % 60; + + my $retry = 0; + my $dest = ""; + do { + $dest = sprintf("%s/%04d-%02d-%02d-%02d:%02d:%02d%s%s.jpg", + $destdir, + $ty, $tm, $td, + $tH, $tM, $tS, + $suffix, + $retry ? sprintf("-%d", $retry) : ""); + $retry++; + } while (-f $dest); + + print "$f $dest\n"; + + unless ($dry_run) { + if ($symbolic) { + symlink $f, $dest or die "Cannot symlink $f to $dest: $!\n"; + } else { + link $f, $dest or die "Cannot hardlink $f to $dest: $!\n"; + } + } +} + +STDOUT->autoflush(1); diff --git a/bin/gal-dump-config b/bin/gal-dump-config new file mode 100755 index 0000000..600a9d5 --- /dev/null +++ b/bin/gal-dump-config @@ -0,0 +1,17 @@ +#!/usr/bin/perl +# UCW Gallery: Show configuration variables +# (c) 2004--2012 Martin Mares + +use strict; +use warnings; + +use UCW::Gallery; +use Data::Dumper; + +my $gal = UCW::Gallery->load_config(); + +for my $k (sort $gal->get_config_keys) { + my $d = Data::Dumper->new([ $gal->get($k) ]); + $d->Terse(1); + print "$k=", $d->Dump; +} diff --git a/bin/gal-dump-meta b/bin/gal-dump-meta new file mode 100755 index 0000000..1734183 --- /dev/null +++ b/bin/gal-dump-meta @@ -0,0 +1,15 @@ +#!/usr/bin/perl +# UCW Gallery: Dump meta-data +# (c) 2004--2012 Martin Mares + +use strict; +use warnings; + +use UCW::Gallery; +use Data::Dumper; + +@ARGV == 1 or die "Usage: $0 \n"; + +my $gal = UCW::Gallery->load_config; +my $meta = $gal->read_meta($ARGV[0]); +print Dumper($meta); diff --git a/bin/gal-from-gqview b/bin/gal-from-gqview new file mode 100755 index 0000000..138af5a --- /dev/null +++ b/bin/gal-from-gqview @@ -0,0 +1,17 @@ +#!/usr/bin/perl +# UCW Gallery: Extract image list from GQview collection +# (c) 2004--2012 Martin Mares + +use strict; +use warnings; + +while (<>) { + chomp; + /^#/ && next; + /^$/ && next; + if (/^"(.*)"$/) { + print "$1\n"; + } else { + die "Error parsing collection: $_"; + } +} diff --git a/bin/gal-gen b/bin/gal-gen new file mode 100755 index 0000000..1ee10b0 --- /dev/null +++ b/bin/gal-gen @@ -0,0 +1,212 @@ +#!/usr/bin/perl +# UCW Gallery: Generate published photos +# (c) 2004--2015 Martin Mares + +use strict; +use warnings; + +use UCW::Gallery; +use Image::EXIF; +use Image::Magick; +use IO::Handle; +use File::Spec; +use File::Path; + +STDOUT->autoflush(1); + +my $gal = UCW::Gallery->load_config; + +my $orig_list = $gal->read_list('gallery.list') or die "Cannot read gallery.list: $!\n"; + +my $photo_dir = $gal->get('PhotoDir'); +if (-d $photo_dir) { + print "Using existing output directory: $photo_dir\n"; +} else { + print "Creating output directory: $photo_dir\n"; + File::Path::mkpath($photo_dir) or die "Unable to create $photo_dir: $!\n"; +} + +my $photo_meta = $gal->photo_meta_name; +my $old_meta = {}; +if (-f $photo_meta) { + print "Reading old meta-data\n"; + $old_meta = $gal->read_meta($photo_meta); + # use Data::Dumper; print "Read old meta: ", Dumper($old_meta), "\n"; +} +my $meta = { 'photo' => {} }; + +sub get_meta_basic($$$) { + my ($f, $m, $p) = @_; + my $rotate = $f->{orientation}; + + my ($orig_w, $orig_h, $orig_size, $orig_format) = $p->PingImage($f->{orig}) or die "Error reading " . $f->{orig} . "\n"; + print "${orig_w}x${orig_h} "; + + my ($w0, $h0) = ($rotate eq "l" || $rotate eq "r") ? ($orig_h, $orig_w) : ($orig_w, $orig_h); + my ($w, $h) = ($w0, $h0); + if ($w > $gal->get('PhotoMaxWidth')) { + my $s = $gal->get('PhotoMaxWidth') / $w; + $w = $w * $s; + $h = $h * $s; + } + if ($h > $gal->get('PhotoMaxHeight')) { + my $s = $gal->get('PhotoMaxHeight') / $h; + $w = $w * $s; + $h = $h * $s; + } + $w = int($w + .5); + $h = int($h + .5); + + $m->{o} = $rotate; + for my $k (keys %UCW::Gallery::list_attrs) { + next if $UCW::Gallery::list_attrs{$k} < 2; + $m->{$k} = $f->{$k} if defined $f->{$k}; + } + $m->{w0} = $w0; + $m->{h0} = $h0; + $m->{w} = $w; + $m->{h} = $h; +} + +sub parse_geo($) { + my ($g) = @_; + defined $g or return; + if ($g =~ m{^([NEWS]) (\d+)\xb0 ([0-9.]+)'$}) { + $g = $2 + $3/60; + $g = -$g if $1 eq 'W' || $1 eq 'S'; + } elsif ($g =~ m{^([NEWS]) (\d+)\xb0 (\d+)' ([0-9.]+)$}) { + $g = $2 + $3/60 + $4/3600; + $g = -$g if $1 eq 'W' || $1 eq 'S'; + } else { + print "[EXIF: unable to parse coordinate $g] "; + return; + } + return sprintf "%.6f", $g; +} + +sub get_meta_exif($$) { + my ($f, $m) = @_; + $gal->get('CacheExif') or return; + + my $e = new Image::EXIF($f->{orig}); + my $i = $e->get_all_info(); + if ($e->error) { + print "[EXIF error: ", $e->error, "] "; + return; + } + # use Data::Dumper; print Dumper($i); + + my $lat = parse_geo($i->{image}->{'Latitude'}); + my $lon = parse_geo($i->{image}->{'Longitude'}); + + my $alt = $i->{image}->{'Altitude'}; + if ($alt) { + if ($alt =~ m{^([0-9.]+) m$}) { + $alt = $1; + } else { + print "[EXIF: unable to parse altitude $alt] "; + $alt = undef; + } + } + + # printf "[GEO: lat=%s lon=%s alt=%s] ", $lat // '?', $lon // '?', $alt // '?'; + if ($lat && $lon) { + $m->{lat} //= $lat; + $m->{lon} //= $lon; + } + $m->{alt} //= $alt if $alt; + + my $time = $i->{image}->{'Image Created'}; + if ($time) { + if ($time =~ m{^(\d{4}):(\d{2}):(\d{2}) (\d{2}:\d{2}:\d{2})$}) { + $m->{t} //= "$1-$2-$3 $4"; + # print "[TIME: ", $m->{t}, "] "; + } else { + print "[EXIF: unable to parse time $time] "; + } + } +} + +sub generate_photo($$$) { + my ($f, $m, $p) = @_; + + my $e; + $e = $p->Read($f->{orig}) and die "Error reading " . $f->{orig} . ": $e"; + $p->Strip; + $p->SetAttribute(quality=>90); + + my $xfrm = $m->{xf}; + if ($xfrm =~ /s/) { + print "-> sharpen "; + $p->Sharpen(1); + } + if ($xfrm =~ /h/) { + print "-> equalize "; + $p->Equalize(); + } + if ($xfrm =~ /n/) { + print "-> normalize "; + $p->Normalize(); + } + + my $rotate = $m->{o}; + my $rot = 0; + if ($rotate eq "l") { $rot = 270; } + elsif ($rotate eq "r") { $rot = 90; } + elsif ($rotate eq "u") { $rot = 180; } + if ($rot) { + print "-> rotate $rot "; + $p->Rotate(degrees=>$rot); + } + + my ($w, $h) = ($m->{w}, $m->{h}); + if ($w != $m->{w0} || $h != $m->{h0}) { + print "-> ${w}x${h} "; + $p->Resize(width=>$w, height=>$h); + } + + my $photo = $gal->photo_name($m, $f->{id}); + my $tmp = "$photo.new"; + $e = $p->Write($tmp) and die "Unable to write $tmp: $e"; + rename $tmp, $photo or die "Cannot rename $tmp to $photo: $!\n"; +} + +for my $f (@$orig_list) { + my $id = $f->{id}; + print "$id: "; + + my $m = { }; + $meta->{photo}->{$id} = $m; + $f->{orig} = File::Spec->rel2abs($f->{file}, $gal->get('OrigDir')); + + my $p = new Image::Magick; + get_meta_basic($f, $m, $p); + get_meta_exif($f, $m); + + my $om = $old_meta->{photo}->{$id}; + if ($om && + $om->{o} eq $m->{o} && + $om->{xf} eq $m->{xf} && + $om->{w} eq $m->{w} && + $om->{h} eq $m->{h}) { + print "... uptodate\n"; + next; + } + + generate_photo($f, $m, $p); + print "... OK\n"; +} + +print "Cleaning up stale files\n"; +for my $f (<$photo_dir/*.jpg>) { + my ($vv, $dd, $id) = File::Spec->splitpath($f); + $id =~ s{\..*$}{}; + unless (defined $meta->{photo}->{$id}) { + print "$id: removing\n"; + unlink $f; + } +} + +print "Writing meta-data\n"; +$gal->write_meta($photo_meta, $meta); +exit 0; diff --git a/bin/gal-mj-digikam b/bin/gal-mj-digikam new file mode 100755 index 0000000..8576f89 --- /dev/null +++ b/bin/gal-mj-digikam @@ -0,0 +1,71 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use Cwd; +use DBI; +use Getopt::Long; + +if (@ARGV && $ARGV[0] eq '--help') { + die <] +AMEN +} + +my $photos_root = $ENV{HOME} . '/photos'; + +my $album = $ARGV[0]; +if (!defined $album) { + my $cwd = getcwd; + $cwd =~ m{/photos/(.*)} or die "Cannot identify album from current directory, need to specify maunally.\n"; + $album = $1; +} + +if (! -f "gallery.cf") { + system 'gal', 'mj-init'; die if $?; +} + +my $dbfile = "$photos_root/digikam4.db"; +my $dbh = DBI->connect("dbi:SQLite:dbname=$dbfile", "", "") or die "Cannot access $dbfile\n"; + +my %alba = (); +for my $r (@{$dbh->selectall_arrayref( + 'SELECT a.id AS id, r.label AS label, a.relativePath AS rpath FROM Albums a JOIN AlbumRoots r ON (r.id = a.albumRoot)', + { Slice => {} })}) { + my $name = $r->{label} . $r->{rpath}; + # print "$name\n"; + $alba{$name} = $r->{id}; +} + +my $album_id = $alba{$album} // die "Unknown album $album\n"; +print "## Album $album: id=$album_id\n"; + +my ($tag_id) = $dbh->selectrow_array('SELECT id FROM Tags WHERE pid=0 AND name="web"'); +$tag_id // die "Cannot find web tag\n"; +print "## Tag ID: $tag_id\n"; + +my $res = $dbh->selectall_arrayref( < {} }, + SELECT + i.name AS name, + p.latitudeNumber AS lat, + p.longitudeNumber AS lon, + p.altitude AS alt + FROM Images i + JOIN ImageTags t ON (i.id = t.imageid) + LEFT JOIN ImagePositions p ON (i.id = p.imageid) + WHERE i.album=? AND t.tagid=? + ORDER BY i.modificationDate +AMEN + $album_id, + $tag_id, + ); + +open OUT, '|-', $ENV{GALLERY_ROOT} . '/bin/gal-scan' or die "Cannot feed gal scan\n"; +for my $r (@$res) { + print OUT "$photos_root/$album/" . $r->{name}, "\n"; + for my $k (qw(lat lon alt)) { + print OUT "\t$k=", $r->{$k}, "\n" if defined $r->{$k}; + } +} +close OUT; diff --git a/bin/gal-mj-init b/bin/gal-mj-init new file mode 100755 index 0000000..1105254 --- /dev/null +++ b/bin/gal-mj-init @@ -0,0 +1,20 @@ +#!/bin/sh +set -e + +if [ -f gallery.cf ] ; then + echo >&2 'gallery.cf already present, giving up!' + exit 1 +fi + +cat >gallery.cf <<'AMEN' +use strict; +use warnings; +use utf8; + +my $gal = require '../../default.cf'; +$gal->set( + Title => 'Unnamed', +); + +return $gal; +AMEN diff --git a/bin/gal-mj-map b/bin/gal-mj-map new file mode 100755 index 0000000..b9f48d9 --- /dev/null +++ b/bin/gal-mj-map @@ -0,0 +1,26 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use Digest::SHA; +use File::Find; + +STDOUT->autoflush(1); + +find({ + wanted => sub { + my $name = $File::Find::name; + $name =~ s{^\./}{}; + $name =~ m{^\d} or return; + $name =~ m{\.jpe?g$}i or return; + -f $name or return; + print "$name\t"; + + my $sha = Digest::SHA->new(1); + $sha->addfile($name) or die "Cannot hash $name\n"; + my $id = substr($sha->hexdigest, 0, 16); + print "$id\n"; + }, + no_chdir => 1, +}, "."); diff --git a/bin/gal-mj-migrate-check b/bin/gal-mj-migrate-check new file mode 100755 index 0000000..5936f32 --- /dev/null +++ b/bin/gal-mj-migrate-check @@ -0,0 +1,35 @@ +#!/usr/bin/perl +# Check that thumbnail aspect ratios match pre-migration data + +use strict; +use warnings; + +use UCW::Gallery; + +my $gal = UCW::Gallery->load_config; +my $meta = $gal->read_meta($gal->cache_meta_name); + +open T, "gallery.thumb" or die "Cannot read gallery.thumb\n"; +my @thumbs = (); +while () { + chomp; + my ($name, $tw, $th) = split /\s+/; + push @thumbs, [ $tw, $th ]; +} +close T; + +for my $id (@{$meta->{sequence}}) { + my ($tw, $th) = @{shift @thumbs} or die; + my $ta = $tw / $th; + my $m = $meta->{photo}->{$id} or die; + my $pw = $m->{w}; + my $ph = $m->{h}; + my $pa = $pw / $ph; + if (abs($ta - $pa) > 0.05) { + if (abs($ta - 1/$pa) > 0.05) { + print STDERR "$id: Mismatched aspect ratio: orig $ta (${tw}x${th}) new $pa (${pw}x${ph})\n"; + } else { + print STDERR "$id: Mismatched rotation\n"; + } + } +} diff --git a/bin/gal-mj-upgrade b/bin/gal-mj-upgrade new file mode 100755 index 0000000..66c260f --- /dev/null +++ b/bin/gal-mj-upgrade @@ -0,0 +1,154 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use UCW::Gallery; +use File::Spec; +use Image::Magick; + +my $photos_root = $ENV{HOME} . '/photos'; + +my $album = $ARGV[0] // die "Usage: gal mj-upgrade \n"; +print "### $album ###\n"; + +### Scan photos.map ### + +my $year = $album; +$year =~ s{/.*}{}; +if ($album eq '2009/Fireworks') { $year = 2008; } +print "Loading map for $year\n"; +open M, "$photos_root/photos.map" or die "No map file found\n"; +my %map = (); +while () { + chomp; + m{^$year} or next; + my ($path, $hash) = split /\t/; + my $name = $path; + $name =~ s{.*/}{}; + if (defined $map{$name}) { + my $prev = $map{$name}; + if ($prev->{hash} ne $hash) { + print STDERR "Collision for $name: ", $prev->{path}, " vs. ", $path, "\n"; + } else { + # print STDERR "Harmless collision for $name: ", $prev->{path}, " vs. ", $path, "\n"; + } + } else { + $map{$name} = { path => $path, hash => $hash }; + } +} +close M; + +### Scan static list (if any) ### + +my @src = (); +if (open S, "../static/photos/$album/x") { + print "Found static list\n"; + while () { + chomp; + my @fields = split /\t/; + if (@fields == 4) { + push @src, { name => $fields[0], rotate => $fields[2], xform => $fields[3] }; + } elsif (@fields == 2) { + push @src, { name => $fields[0], rotate => $fields[1], xform => '.' }; + } else { + die "Error parsing gallery list: $_\n"; + } + } + close S; +} + +### Parse index.cgi and produce gallery.new ### + +open I, "$album/index.cgi" or die "Cannot find $album/index.cgi\n"; +open W, ">$album/gallery.new" or die "Cannot create $album/gallery.new\n"; +open T, ">$album/gallery.thumb" or die "Cannot create $album/gallery.thumb\n"; +my %opt = (); +my %found_dirs = (); +my @items = (); +while () { + chomp; + if (/^\s+"(\w+)" => "(.*)",?$/) { + $opt{$1} = $2; + print "Option: $1 = $2\n"; + } elsif (/^img\("([^"]+)(\.jpe?g)", "([^"]*)"\);\s*# (\S+)/) { + my $nr = $1; + my $ext = $2; + my $title = $3; + my $file = $4; + $file =~ s{^.*/}{}g; + + my $map = $map{$file}; + if (!$map) { + print STDERR "$album: No match for $file\n"; + next; + } + my $path = $map->{path}; + my ($vv, $dd, $ff) = File::Spec->splitpath($path); + $found_dirs{$dd} = 1; + + print "Image: $nr $path [$title]\n"; + my $src; + if (@src) { + if ($nr =~ m{^\d+$} && $nr <= @src) { + $src = $src[$nr-1]; + } else { + print STDERR "$album: Crooked refs ($nr)\n"; + } + } + + print W join("\t", + "$photos_root/$path", + $map->{hash}, + ($src ? $src->{rotate} : '-'), + ($src ? $src->{xform} : '.'), + ($title ne "" ? $title : '-'), + ), "\n"; + + + my $thumb = "../static/photos/$album/$nr-thumb.jpg"; + if (!-f $thumb) { + print STDERR "$album: Cannot find thumbnail for photo $nr ($thumb)\n"; + print T "1 1\n"; + } else { + my $im = new Image::Magick; + my ($thumb_w, $thumb_h, $thumb_size, $thumb_format) = $im->PingImage($thumb) or die "Error reading $thumb\n"; + print T "$thumb $thumb_w $thumb_h\n"; + } + } elsif (/^($|#|require|SetOptions|\)|Start|Finish)/) { + # Nothing important + } else { + print STDERR "$album/index.cgi: Parse error at line $.: $_\n"; + } +} +close T; +close W; +close I; + +if (scalar keys %found_dirs != 1) { + print STDERR "$album: Photos in multiple directories\n"; +} + +### Create gallery.cf ### + +open CF, ">$album/gallery.cf" or die "Cannot create $album/gallery.cf"; +print CF <<'AMEN' ; +use strict; +use warnings; +use utf8; + +my $gal = require '../../default.cf'; +$gal->set( +AMEN + +for my $cf (reverse sort keys %opt) { + print CF "\t$cf => \"", $opt{$cf}, "\",\n"; +} + +print CF <<'AMEN' ; +); + +return $gal; +AMEN + +close CF; diff --git a/bin/gal-mj-upload b/bin/gal-mj-upload new file mode 100755 index 0000000..6fc7cf1 --- /dev/null +++ b/bin/gal-mj-upload @@ -0,0 +1,42 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +print "## Updating Git\n"; +system "git", "pull"; +die if $?; + +print "## Ensuring that photos are generated\n"; +system "gal", "gen"; +die if $?; + +use Cwd; +my $cwd = getcwd; +my ($root, $album) = $cwd =~ m{(.*)/photos/(.*)} or die "Cannot identify album from current directory, giving up.\n"; +my $static = "$root/static/gallery/photo/$album"; +-d $static or die "Cannot find generated photos in $static, giving up.\n"; + +print "## Uploading album $album\n"; +system "rs", "$static/", "jw:www/static/gallery/photo/$album/"; +die if $?; + +print "## Calling editor on index files\n"; +system 'vim', 'gallery.cf', "$root/photos/Makefile", "$root/photos/index.thtml"; +die if $?; + +print "## Committing to repository\n"; +system 'git', 'add', 'gallery.cf', 'gallery.list'; die if #?; +system 'git', 'add', "$root/photos/Makefile", "$root/photos/index.thtml"; die if $?; +system 'git', 'commit'; die if $?; +system 'git', 'push'; die if $?; + +print "## Pulling at the server\n"; +system "ssh", "jw", "cd web && git pull && make"; +die if $?; + +print "## Regenerating cache at the server\n"; +system "ssh", "jw", "cd web/photos/$album && ~/web/gal/gal cache"; +die if $?; + +print "Done.\n"; diff --git a/bin/gal-scan b/bin/gal-scan new file mode 100755 index 0000000..a976d5d --- /dev/null +++ b/bin/gal-scan @@ -0,0 +1,150 @@ +#!/usr/bin/perl +# UCW Gallery: Scan images and generate image list +# (c) 2004--2015 Martin Mares + +use strict; +use warnings; + +use UCW::Gallery; +use UCW::Gallery::Hashes; +use File::Spec; +use Image::EXIF; +use Getopt::Long; + +if (@ARGV && $ARGV[0] eq '--help') { + die < + or: gal scan + or: gal scan --update + +Options: +--add Keep existing images and add new ones +AMEN +} + +my $add; +my $update; +GetOptions( + 'add!' => \$add, + 'update!' => \$update, +) or die "Try gal scan --help\n"; + +STDOUT->autoflush(1); +my $gal = UCW::Gallery->load_config; +my $orig_prefix = $gal->get('OrigDir'); +$orig_prefix =~ m{/$} or $orig_prefix .= '/'; + +my @source = (); +if ($update) { + print "Loading previous gallery.list\n"; + my $pg = $gal->read_list('gallery.list') or die "Unable to load gallery.list\n"; + @source = @{$pg}; +} elsif (@ARGV) { + for my $in (@ARGV) { + if (-f $in) { + push @source, { file => $in }; + } elsif (-d $in) { + opendir D, $in or die "Cannot scan directory $in: $!\n"; + my @p = (); + while (my $e = readdir D) { + my $f = File::Spec->canonpath(File::Spec->catfile($in, $e)); + if ($f =~ m{\.(jpe?g|png)$}i) { + push @p, $f; + } + } + closedir D; + push @source, map { { file => $_ } } sort @p; + } else { + die "$in is neither file nor directory\n"; + } + } +} else { + binmode STDIN, ':utf8'; + @source = @{$gal->read_list_fh(\*STDIN)}; +} + +my $hashes = UCW::Gallery::Hashes->new($gal); + +print "Scanning photos\n"; +my @images = (); +foreach my $src (@source) { + my $name = $src->{file}; + if ($name =~ m{^/}) { + # Try to relativize to OrigDir + if (substr($name, 0, length $orig_prefix) eq $orig_prefix) { + $src->{file} = $name = substr($name, length $orig_prefix); + } + } + print "\t$name:"; + + my $path = File::Spec->rel2abs($name, $gal->get('OrigDir')); + -f $path or die "Cannot find $path\n"; + + $src->{id} = $hashes->hash_image($path); + print " id=", $src->{id}; + + if (!defined $src->{orientation} || $src->{orientation} eq '-') { + my $e = new Image::EXIF($path); + my $i = $e->get_all_info(); + if ($e->error) { + print "EXIF error: ", $e->error, "\n"; + $src->{orientation} = '.'; + } else { + # print STDERR Dumper($i), "\n"; + my $o = $i->{'image'}->{'Image Orientation'} || "Top, Left-Hand"; + if ($o eq "Top, Left-Hand") { $o = "."; } + elsif ($o eq "Right-Hand, Top") { $o = "r"; } + elsif ($o eq "Left-Hand, Bottom") { $o = "l"; } + elsif ($o eq "Bottom, Right-Hand") { $o = "u"; } + else { + print "Unrecognized orientation: $o\n"; + $o = "."; + } + $src->{orientation} = $o; + } + } + print " ori=", $src->{orientation}; + + defined $src->{xf} or $src->{xf} = $gal->get('ScanDefaultTransform'); + print " xfrm=", $src->{xf}; + + $src->{title} //= ''; + push @images, $src; + print "\n"; +} + +if (!$update) { + my $old = $gal->read_list('gallery.list'); + if ($old) { + print "Updating gallery.list\n"; + my %new_by_id = map { $_->{id} => $_ } @images; + my @result = (); + for my $o (@$old) { + my $id = $o->{id}; + my $i = $new_by_id{$id}; + if (!$i) { + if ($add) { + print "\t$id: kept\n"; + push @result, $o; + } else { + print "\t$id: removed\n"; + } + } else { + print "\t$id: kept\n"; + push @result, $o; + delete $new_by_id{$id}; + } + } + for my $i (@images) { + my $id = $i->{id}; + $new_by_id{$id} or next; + print "\t$id: new\n"; + push @result, $i; + } + @images = @result; + } +} + +$gal->write_list('gallery.list', \@images); +print "Written gallery.list (", (scalar @images), " items)\n"; +$hashes->write; diff --git a/gal b/gal new file mode 100755 index 0000000..f16a5f7 --- /dev/null +++ b/gal @@ -0,0 +1,114 @@ +#!/usr/bin/perl +# UCW Gallery -- Master Program +# (c) 2012 Martin Mares + +use strict; +use warnings; +use Getopt::Long; + +use FindBin; +my $gallery_root = $FindBin::Bin; + +my $all; +my $parallel; + +sub show_help() { + print < + +General options: + --all Run on all galleries in subdirectories +-p, --parallel= Run in parallel and use processes + +Common commands: +scan Scan directory and obtain originals +gen Generate web photos from originals +cache Rebuild thumbnails and other cached info + +Other commands: +dump-config Show configuration settings +dump-meta Show contents of metadata files +from-gqview Parse gqview/geeqie collections +AMEN + exit 0; +} + +Getopt::Long::Configure('require_order', 'bundling'); +GetOptions( + "help" => \&show_help, + "version" => sub { + print "UCW Gallery 2.0 (c) 2004-2012 Martin Mares \n"; + }, + "all!" => \$all, + "p|parallel=i" => \$parallel, +) or die "Try `gal help' for more information.\n"; +Getopt::Long::Configure('default'); + +@ARGV or die "Missing subcommand.\n"; +my $sub = shift @ARGV; +$sub =~ /^[0-9a-zA-Z-]+$/ or die "Invalid subcommand $sub\n"; + +if ($sub eq 'help') { show_help(); } + +my $sub_path = "$gallery_root/bin/gal-$sub"; +-x $sub_path or die "Unknown subcommand $sub\n"; + +$ENV{"GALLERY_ROOT"} = $gallery_root; +$ENV{"PERL5LIB"} = join(":", $gallery_root, $ENV{"PERL5LIB"} // ()); + +if (!$all) { + exec $sub_path, @ARGV; + die "Cannot execute $sub_path: $!\n"; +} + +### Parallel execution ### + +my @dirs = sort map { chomp; s{^\./}{}; s{\/gallery.cf}{}; $_; } `find . -mindepth 2 -name gallery.cf`; +my $done = 0; +my $need = @dirs; +my $logging = $parallel ? 1 : 0; +my $threads = $parallel // 1; + +my $running = 0; +my $errors = 0; +my %pid_to_dir = (); + +while ($running || @dirs) { + if ($running == $threads || !@dirs) { + # Wait for children + my $pid = wait; die if $pid < 0; + my $dir = $pid_to_dir{$pid} or die; + if ($?) { + print "!! $dir FAILED"; + print " [see $dir/gallery.log]" if $logging; + $errors++; + } else { + print "++ $dir"; + unlink "$dir/gallery.log" if $logging; + } + delete $pid_to_dir{$pid}; + $running--; + $done++; + print " (done $done/$need)\n"; + } else { + my $dir = shift @dirs; + print "<< $dir\n"; + my $pid = fork; + if (!$pid) { + if ($logging) { + close STDOUT; + open STDOUT, '>', "$dir/gallery.log" or die; + close STDERR; + open STDERR, '>&STDOUT'; + } + chdir $dir or die "Cannot chdir to $dir: $!\n"; + exec $sub_path, @ARGV; + die "Cannot execute $sub_path: $!\n"; + } + $pid_to_dir{$pid} = $dir; + $running++; + } +} + +print "$done jobs, $errors errors.\n"; +exit ($errors > 0); diff --git a/gal/FORMAT b/gal/FORMAT deleted file mode 100644 index 059f2af..0000000 --- a/gal/FORMAT +++ /dev/null @@ -1,58 +0,0 @@ -List of photos (gallery.list) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -One photo per line, tab-separated columns: - - File name (relative to OrigDir in config) - Identifier (16 hex digits) - Orientation: one of "l", "r", "d", "." - Transformation -- sequence of: - n normalize contrast - s sharpen - h equalize histogram - Title (UTF-8 string) - -Lines starting with "#" are ignored. - -Lines starting with a tab add further attributes to the previous photo. -The following attributes are recognized: - - lat geographic latitude in degrees north of equator - lon geographic longitude in degrees east of Greenwich - alt geographic altitude in meters - t time when the photo was taken (2014-01-25 09:40:12) - - -Importing meta-data from other sources -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Occasionally, you want to add photos from a source which already provides -some meta-data. In this case, you can construct a gallery.list with a subset -of fields, or feed the stdin of "gal scan" with such a list. - -For convenience, orientation, transformation and title can be also given -as named attributes. They are called "orientation", "xf", and "title". - - -Photo meta-data (PhotoDir/gallery.meta or CacheDir/cache.meta) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Perl Storable containing a single hash. - -$meta->{photo}->{$identifier} is a hash of: - o orientation - xf transformation applied - w width after scaling - h height after scaling - w0, h0 width and height of original image - title photo title - fmt photo format (png/jpg; defaults to jpg) - lat geographic latitude in degrees north of equator - lon geographic longitude in degrees east of Greenwich - alt geographic altitude in meters - t time when the photo was taken (2014-01-25 09:40:12) - -The rest is present in cache.meta only: - -$meta->{sequence} is an array of photo IDs as they appear in the gallery. - -$meta->{thumb}->{$format}->{$identifier} is a hash of: - w thumbnail width - h thumbnail height diff --git a/gal/Makefile b/gal/Makefile deleted file mode 100644 index f7d01c2..0000000 --- a/gal/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -$(eval $(dir-setup)) - -$(call lib-copy, UCW/Gallery.pm) -$(call lib-copy, $(addprefix UCW/Gallery/, Web.pm Hashes.pm Archive.pm)) -$(call lib-copy, $(addprefix UCW/Gallery/Web/, Plain.pm NrtBlue.pm HighSlide.pm)) - -$(call copy, $(addprefix nrt-blue/,back.png bot.png left.png next.png prev.png right.png top.png style.css)) -$(call copy, $(addprefix plain/,back.png next.png prev.png style.css)) -$(call copy, $(addprefix highslide/nav/,back.png next.png prev.png)) - -$(call copy, $(addprefix highslide/, highslide.css highslide-ie6.css highslide-with-gallery.js custom.css custom.js)) -$(call copy, $(addprefix highslide/graphics/, \ - close.png closeX.png controlbar-black-border.gif controlbar-text-buttons.png controlbar-white-small.gif \ - controlbar-white.gif controlbar2.gif controlbar3.gif controlbar4-hover.gif controlbar4.gif fullexpand.gif \ - geckodimmer.png icon.gif loader.big.black.gif loader.big.white.gif loader.black.gif loader.white.gif \ - resize.gif scrollarrows.png zoom.png zoomin.cur zoomout.cur)) -$(call copy, $(addprefix highslide/graphics/outlines/, \ - beveled.png custom.png drop-shadow.png glossy-dark.png outer-glow.png rounded-black.png rounded-white.png)) diff --git a/gal/README b/gal/README deleted file mode 100644 index 0a5d7c7..0000000 --- a/gal/README +++ /dev/null @@ -1,88 +0,0 @@ -================================================================================ - - UCW::Gallery v2.0 - - (c) 2004--2015 Martin Mares - -================================================================================ - -This is a simple photo gallery for my web pages, or rather a set of bricks -from which you can create one. It can be freely used and distributed according -to the terms of the GNU GPL v2 or newer. - -Requirements -~~~~~~~~~~~~ -Perl with the following non-core modules: - - o UCW::CGI (see http://www.ucw.cz/libucw/) - o Image::Magick - o Image::Exif - o Archive::Zip - o Digest::SHA - -Files and directories -~~~~~~~~~~~~~~~~~~~~~ - - o gal -- a front-end script for maintenance of galleries. All other programs - are run as sub-commands of this script. Try "gal --help". - - o gallery.cf -- all programs expect that the current directory contains - a configuration file. In fact, the config file is a perl script, whose - sole purpose is to set up paths, construct a gallery object and set its - options. - - A simple example looks as follows: - - use UCW::Gallery; - - my $gal = UCW::Gallery->new; - $gal->set(Title => 'A Gallery', SubTitle => '(an example)'); - return $gal; - - o gallery.list -- a list of original photos together with per-photo parameters. - Usually created by "gal scan", then tweaked manually. See `FORMATS' for - description of file syntax. - - o gallery.cache -- used internally by "gal scan" to store cached image hashes. - - o Originals directory -- original images, from which the gallery is generated. - - o Photo directory -- keeps processed (usually down-scaled) versions of the original - images. These are the images shown to the user. - - o Cache directory -- keeps various cached data, like thumbnails of all photos. - - o gallery.cgi -- interfaces the gallery to your web server. E.g.: - - #!/usr/bin/perl - - use lib "../path/to/gallery/modules"; - use UCW::Gallery; - use UCW::Gallery::Web::Plain; - - my $gal = UCW::Gallery->load_config(); - UCW::Gallery::Web::Plain->run($gal); - -Workflow -~~~~~~~~ - - o vi gallery.cf - - o gal scan /path/to/originals -- this creates gallery.list, populates it with - descriptions of all photos and reads rotation from EXIF tags. - - o vi gallery.list -- edit image descriptions and other options. - - o gal gen -- generate photos from the originals (after this, the originals are no - longer needed). - - o gal cache -- generate cached data. - - o Later, the gallery can be updated: - - - When you edit image descriptions, re-run "gal cache". - - When you edit image options (rotation etc.), re-run "gal gen". - - When you want to add new images, re-run "gal scan". Give it the new list - of images and it will try to re-use as much information from the previous - gallery.list as possible. - - When you modify existing images, run "gal scan --update". diff --git a/gal/UCW/Gallery.pm b/gal/UCW/Gallery.pm deleted file mode 100644 index 748fe3c..0000000 --- a/gal/UCW/Gallery.pm +++ /dev/null @@ -1,216 +0,0 @@ -# Simple Photo Gallery -# (c) 2003--2015 Martin Mares - -package UCW::Gallery; - -use strict; -use warnings; - -use File::Spec; -use Storable; - -### Class methods ### - -sub new($) { - my ($class) = @_; - my $self = { }; - $self->{cfg} = { - # Directories - OrigDir => '.', # Original images - PhotoDir => 'photo', # Scaled-down photos for web - CacheDir => 'cache', # Cache with meta-data and thumbnails - - # URL prefixes - PhotoUrlPrefix => 'photo/', - ThumbUrlPrefix => 'thumb/', - ThemeUrlPrefix => 'gal/', - - # Processing machinery settings - ScanDefaultTransform => 's', - PhotoMaxWidth => 1024, - PhotoMaxHeight => 1024, - ThumbFormats => [ "114x94" ], # Built-in themes use the first size, - # but more can be generated - CacheExif => 0, # Cache selected EXIF meta-data - CacheHashes => 1, # Let gal-scan cache file hashes - - # Titles and navigation - Title => 'An Unnamed Gallery', - SubTitle => "", - ParentURL => '../', - BackURL => "", - FwdURL => "", - - # Hacks - GeoHack => 0, - }; - return bless $self, $class; -} - -sub load_config($) { - my $cfg = "./gallery.cf"; - my $self = do $cfg; - unless (defined $self) { - if ($@) { - die "Error parsing $cfg: $@"; - } elsif ($!) { - die "Cannot load $cfg: $!\n"; - } else { - die "Cannot load $cfg, check that it returns true\n"; - } - } - return $self; -} - -### Object methods ### - -sub get($$) { - my ($self, $key) = @_; - if (exists $self->{cfg}->{$key}) { - my $val = $self->{cfg}->{$key}; - defined $val or warn "Gallery: Config item $key is not set\n"; - return $val; - } else { - warn "Gallery: Config item $key does not exist\n"; - return; - } -} - -sub try_get($$) { - my ($self, $key) = @_; - return $self->{cfg}->{$key}; -} - -sub def($@) { - my $self = shift; - while (my $key = shift @_) { - my $val = shift @_; - $self->{cfg}->{$key} //= $val; - } -} - -sub set($@) { - my $self = shift; - while (my $key = shift @_) { - $self->{cfg}->{$key} = shift @_; - } -} - -sub get_config_keys($) { - my ($self) = @_; - return keys %{$self->{cfg}}; -} - -our %list_attrs = ( - 'file' => 0, # 0 = not permitted as extended attribute - 'id' => 0, - 'orientation' => 1, # 1 = aliases for normal attributes - 'title' => 1, - 'xf' => 2, # 2 = ... and propagated to gal-gen - 'lat' => 3, # 3 = normal extended attributes, propagated to gal-gen - 'lon' => 3, - 'alt' => 3, - 't' => 3, -); - -sub write_list($$$) { - my ($self, $file, $images) = @_; - open my $fh, '>:utf8', "$file.new" or die "Cannot create $file.new: $!\n"; - for my $i (@$images) { - print $fh join("\t", - $i->{file}, - $i->{id}, - $i->{orientation}, - $i->{xf}, - ($i->{title} eq '' ? '-' : $i->{title}), - ), "\n"; - for my $k (keys %$i) { - print $fh "\t$k=", $i->{$k}, "\n" if $list_attrs{$k} >= 3; - } - } - close $fh; - rename "$file.new", $file or die "Cannot rename $file.new to $file: $!\n"; -} - -sub read_list_fh($$) { - my ($self, $fh) = @_; - my @images = (); - while (<$fh>) { - chomp; - /^$/ and next; - /^#/ and next; - if (/^\t/) { - @images or die "Misplaced continuation line before first image\n"; - if (my ($k, $v) = /^\t+(.*?)=(.*)/) { - # Continutation of previous line - my $i = $images[-1]; - if ($list_attrs{$k}) { - $i->{$k} = $v; - } else { - print STDERR "Ignoring unknown attribute $k for ", $i->{file}, "\n"; - } - } else { - die "Invalid continuation line. Expecting 'key=value'.\n"; - } - } else { - my $i = {}; - ($i->{file}, $i->{id}, $i->{orientation}, $i->{xf}, $i->{title}) = split /\t/; - if (!defined $i->{title} || $i->{title} eq '-') { $i->{title} = ""; } - push @images, $i; - } - } - return \@images; -} - -sub read_list($$) { - my ($self, $file) = @_; - open my $fh, '<:utf8', $file or return; - my $list = $self->read_list_fh($fh); - close $fh; - return $list; -} - -sub write_meta($$) { - my ($self, $file, $meta) = @_; - open my $fh, '>', "$file.new" or die "Cannot create $file.new: $!\n"; - Storable::nstore_fd($meta, $fh); - close $fh; - rename "$file.new", $file or die "Cannot rename $file.new to $file: $!\n"; -} - -sub read_meta($) { - my ($self, $file) = @_; - open my $fh, '<', $file or die "Cannot read $file: $!\n"; - my $meta = Storable::fd_retrieve($fh) or die "Cannot parse $file\n"; - close $fh; - return $meta; -} - -sub photo_meta_name($) { - my ($self) = @_; - return File::Spec->catfile($self->get('PhotoDir'), 'gallery.meta'); -} - -sub cache_meta_name($) { - my ($self) = @_; - return File::Spec->catfile($self->get('CacheDir'), 'cache.meta'); -} - -sub thumb_fmt_to_size($$) { - my ($self, $fmt) = @_; - my ($tw, $th) = ($fmt =~ m{^(\d+)x(\d+)$}) or die "Cannot parse thumbnail format $fmt\n"; - return ($tw, $th); -} - -sub photo_file_name($$$) { - my ($self, $photo_meta, $id) = @_; - return $id . '.' . ($photo_meta->{fmt} // 'jpg'); -} - -sub photo_name($$$) { - my ($self, $photo_meta, $id) = @_; - return File::Spec->catfile($self->get('PhotoDir'), $self->photo_file_name($photo_meta, $id)); -} - - -1; diff --git a/gal/UCW/Gallery/Archive.pm b/gal/UCW/Gallery/Archive.pm deleted file mode 100644 index fb7c40b..0000000 --- a/gal/UCW/Gallery/Archive.pm +++ /dev/null @@ -1,34 +0,0 @@ -# Simple Photo Gallery: Archiving -# (c) 2003--2012 Martin Mares - -package UCW::Gallery::Archive; - -use strict; -use warnings; - -use Archive::Zip; -use File::Spec; -use UCW::CGI; - -sub send_archive($$) { - my ($gal, $meta) = @_; - - if (!$gal->get('WebAllowArchives')) { - UCW::CGI::http_error('403 Archiving forbidden by server configuration'); - return; - } - - my $zip = Archive::Zip->new; - my $cnt = 0; - for my $id (@{$meta->{sequence}}) { - $zip->addFile(File::Spec->catfile($gal->get('PhotoDir'), "$id.jpg"), sprintf("%03d.jpg", $cnt)) or die; - $cnt++; - } - - print "Content-type: application/zip\n"; - print "Content-Disposition: attachment; filename=gallery.zip\n"; - print "\n"; - $zip->writeToFileHandle(\*STDOUT, 0); -} - -42; diff --git a/gal/UCW/Gallery/Hashes.pm b/gal/UCW/Gallery/Hashes.pm deleted file mode 100644 index d0414e9..0000000 --- a/gal/UCW/Gallery/Hashes.pm +++ /dev/null @@ -1,51 +0,0 @@ -# Simple Photo Gallery: Image Hashes -# (c) 2015 Martin Mares - -package UCW::Gallery::Hashes; - -use strict; -use warnings; - -use File::stat (); -use Digest::SHA; - -sub new { - my ($class, $gal) = @_; - my $self = { gal => $gal }; - - if ($gal->get('CacheHashes') && -f 'gallery.cache') { - print "Loading gallery.cache\n"; - $self->{cache} = $gal->read_meta('gallery.cache'); - } else { - $self->{cache} = {}; - } - - return bless $self, $class; -} - -sub write { - my ($self) = @_; - my $gal = $self->{gal}; - if ($gal->get('CacheHashes')) { - print "Writing gallery.cache\n"; - $gal->write_meta('gallery.cache', $self->{cache}); - } -} - -sub hash_image { - my ($self, $path) = @_; - my $cache = $self->{cache}; - - my $st = File::stat::stat($path) or die "Cannot access $path: $!\n"; - my $key_text = join(":", $path, $st->dev, $st->ino, $st->mtime); - my $key = Digest::SHA->sha1_base64($key_text); - - if (!exists $cache->{$key}) { - my $sha = Digest::SHA->new(1); - $sha->addfile($path) or die "Cannot hash $path\n"; - $cache->{$key} = substr($sha->hexdigest, 0, 16); - } - return $cache->{$key}; -} - -42; diff --git a/gal/UCW/Gallery/Web.pm b/gal/UCW/Gallery/Web.pm deleted file mode 100644 index ccca477..0000000 --- a/gal/UCW/Gallery/Web.pm +++ /dev/null @@ -1,181 +0,0 @@ -# Simple Photo Gallery: Web Interface -# (c) 2003--2012 Martin Mares - -package UCW::Gallery::Web; - -use strict; -use warnings; - -use UCW::Gallery; -use UCW::CGI; -use File::Spec; - -my $show_img; -my $want_archive; - -my %args = ( - 'i' => { 'var' => \$show_img, 'check' => '\d+' }, - 'a' => { 'var' => \$want_archive }, -); - -sub error($) { - print "

Bad luck, the script is broken. Sorry.\n

$_[0]\n"; - print "\n"; -} - -sub get($$) { - my ($self, $key) = @_; - return $self->{gal}->get($key); -} - -sub extras($$) { - my ($self, $key) = @_; - my $val = $self->get($key); - if (ref $val eq 'CODE') { - return &$val($self); - } else { - return $val; - } -} - -# For use by extras hooks -sub gallery($) { - my ($self) = @_; - return $self->{gal}; -} - -# For use by extras hooks: return true if we are showing an image page, false for index page -sub showing_image($) { - my ($self) = @_; - return $show_img ne ""; -} - -sub html_top($) { - my ($self) = @_; - my $title = UCW::CGI::html_escape($self->get('Title')); - my $hextras = $self->extras('WebHeadExtras'); - my $theme_hextras = $self->theme_head_extras; - print < - -$hextras$theme_hextras$title - -EOF - - $UCW::CGI::ErrorHandler::error_hook = \&error; - - # WebTopExtras are evaluated separately, since they can override the error hook - print $self->extras('WebTopExtras'); -} - -sub html_bot($) { - my ($self) = @_; - print $self->extras('WebBotExtras'), "\n"; -} - -sub show_img($) { - my ($self) = @_; - - if ($show_img < 1 || $show_img > $self->{num_photos}) { - UCW::CGI::http_error('404 No such photo'); - return; - } - - my $meta = $self->{meta}; - my $id = $meta->{sequence}->[$show_img-1]; - my $m = $meta->{photo}->{$id} or die; - $self->html_top; - - $self->show_links(($show_img > 1 ? ("?i=".($show_img-1)) : ""), - ".", - ($show_img < $self->{num_photos} ? ("?i=".($show_img+1)) : "")); - - my $t = UCW::CGI::html_escape($m->{title}); - my $w = $m->{w}; - my $h = $m->{h}; - print "

$t

\n" if $t ne ""; - my $img = $self->get('PhotoUrlPrefix') . $self->{gal}->photo_file_name($m, $id); - print "

$t\n"; - - $self->html_bot; -} - -sub show_pre_thumbs($) { - my ($self) = @_; -} - -sub show_post_thumbs($) { - my ($self) = @_; -} - -sub show_list($) { - my ($self) = @_; - $self->html_top; - - $self->show_links($self->get('BackURL'), $self->get('ParentURL'), $self->get('FwdURL')); - print "

", $self->get('Title'), "

\n"; - my $subtitle = $self->get('SubTitle'); - print "

$subtitle

\n" if $subtitle ne ""; - $self->show_pre_thumbs; - - my $meta = $self->{meta}; - for my $idx (1..$self->{num_photos}) { - my $id = $meta->{sequence}->[$idx-1]; - my $m = $meta->{photo}->{$id}; - my $click_url; - if ($self->get('WebImageSubpages')) { - $click_url = "?i=$idx"; - } else { - $click_url = $self->get('PhotoUrlPrefix') . $self->{gal}->photo_file_name($m, $id); - } - $self->show_thumb($meta, $id, $click_url); - } - - $self->show_post_thumbs; - $self->html_bot(); -} - -sub dispatch($) { - my ($self) = @_; - binmode STDOUT, ':utf8'; - UCW::CGI::parse_args(\%args); - $self->{meta} = $self->{gal}->read_meta(File::Spec->catfile($self->get('CacheDir'), 'cache.meta')); - $self->{num_photos} = scalar @{$self->{meta}->{sequence}}; - - if ($want_archive) { - require UCW::Gallery::Archive; - UCW::Gallery::Archive::send_archive($self->{gal}, $self->{meta}); - } elsif ($show_img ne "") { - $self->show_img; - } else { - $self->show_list; - } -} - -sub attach($$) { - my ($class, $gal) = @_; - my $self = { gal => $gal }; - $gal->def( - WebFE => $self, - - # Extras are either strings or functions called with the current gallery object as parameter - WebHeadExtras => "", - WebTopExtras => "", - WebBotExtras => "", - - # Used by the theming logic - WebThemeCSS => undef, - - # 1 if thumbnail link to sub-pages with images, 0 if they link directly to image files - WebImageSubpages => 1, - - # If enabled, calling the CGI with a=zip produces a ZIP archive with all photos. - WebAllowArchives => 1, - ); - bless $self, $class; - return $self; -} - -42; diff --git a/gal/UCW/Gallery/Web/HighSlide.pm b/gal/UCW/Gallery/Web/HighSlide.pm deleted file mode 100644 index b1e503b..0000000 --- a/gal/UCW/Gallery/Web/HighSlide.pm +++ /dev/null @@ -1,86 +0,0 @@ -# Highslide JS Theme for MJ's Photo Gallery -# (c) 2012 Martin Mares ; GPL'ed - -package UCW::Gallery::Web::HighSlide; - -use strict; -use warnings; -use utf8; - -use UCW::Gallery; -use UCW::Gallery::Web; - -our @ISA = qw(UCW::Gallery::Web); - -sub theme_head_extras($) { - my ($self) = @_; - my $hsdir = $self->get('ThemeUrlPrefix') . "highslide"; - return $self->showing_image ? < -AMEN_MINI - - - - - - -AMEN_FULL -} - -sub show_links($$$$) { - my ($self, $prev, $up, $next) = @_; - my $nav = $self->get('ThemeUrlPrefix') . "highslide/nav"; - print "

"; - print "" if $prev ne ""; - printf "" if $next ne ""; - printf "" if $up ne ""; -} - -sub show_pre_thumbs($) { - my ($self) = @_; - print "\n

\n\n"; -} - -sub show_thumb($) { - my ($self, $meta, $photo_id, $click_url) = @_; - my $m = $meta->{photo}->{$photo_id}; - my $annot = UCW::CGI::html_escape($m->{title}); - my $tf = $self->get('ThumbFormats')->[0]; - my $tm = $meta->{thumb}->{$tf}->{$photo_id} or die "No thumbnails for format $tf found!\n"; - my $tw = $tm->{w}; - my $th = $tm->{h}; - my $thumb = $self->get('ThumbUrlPrefix') . "$tf/$photo_id.jpg"; - # Highslide requires title either for all images, or for none - my $tit = " title=\"$annot\""; - my $aid = 'i'.(++$self->{hs_thumb_counter}); - my $photo_url = $self->get('PhotoUrlPrefix') . $self->{gal}->photo_file_name($m, $photo_id); - print "
  • "; - print "Photo\n"; - if ($self->get('GeoHack')) { - my ($lat, $lon) = ($m->{lat}, $m->{lon}); - if (defined $lat && defined $lon) { - my $local = "trasa"; - my $osm = "OSM"; - print "
    Ukázat na mapě: $local, $osm
    \n"; - } - } -} - -sub run($$) { - my ($class, $gal) = @_; - my $self = $class->SUPER::attach($gal); - $self->dispatch(); - return $self; -} - -1; diff --git a/gal/UCW/Gallery/Web/NrtBlue.pm b/gal/UCW/Gallery/Web/NrtBlue.pm deleted file mode 100644 index 7fc5637..0000000 --- a/gal/UCW/Gallery/Web/NrtBlue.pm +++ /dev/null @@ -1,82 +0,0 @@ -# NRT Theme for MJ's Photo Gallery -# (c) 2003--2012 Martin Mares ; GPL'ed -# Theme images taken from the cthumb package (c) Carlos Puchol - -package UCW::Gallery::Web::NrtBlue; - -use strict; -use warnings; - -use UCW::Gallery; -use UCW::Gallery::Web; - -our @ISA = qw(UCW::Gallery::Web); - -my $theme_name = "nrt-blue"; -my $navw = 48; -my $navh = 48; -my $interior_margin = 4; -my $left_w = 14; -my $right_w = 18; -my $top_h = 14; -my $bot_h = 18; - -sub theme_dir($) { - my ($self) = @_; - return $self->get('ThemeUrlPrefix') . $theme_name; -} - -sub theme_head_extras($) { - my ($self) = @_; - my $stylesheet = $self->theme_dir . "/style.css"; - return "\n"; -} - -sub show_links($$$$) { - my ($self, $prev, $up, $next) = @_; - my $theme = $self->theme_dir; - print "

    "; - print ""; - print "Back" if $prev ne ""; - print "\n"; - printf ""; - printf "Forward" if $next ne ""; - print "\n"; - printf "Up" if $up ne ""; -} - -sub show_thumb($) { - my ($self, $meta, $photo_id, $click_url) = @_; - my $theme = $self->theme_dir; - my $m = $meta->{photo}->{$photo_id}; - my $annot = UCW::CGI::html_escape($m->{title}); - my $tf = $self->get('ThumbFormats')->[0]; - my ($thumb_w, $thumb_h) = $self->{gal}->thumb_fmt_to_size($tf); - my $tm = $meta->{thumb}->{$tf}->{$photo_id} or die "No thumbnails for format $tf found!\n"; - my $tw = $tm->{w}; - my $th = $tm->{h}; - my $thumb = $self->get('ThumbUrlPrefix') . "$tf/$photo_id.jpg"; - my $side_w = $thumb_w + 2*$interior_margin; - my $side_h = $thumb_h + 2*$interior_margin; - my $box_w = $left_w + $side_w + $right_w; - my $box_h = $top_h + $side_h + $bot_h; - print "

    \n"; - print "\n"; - print "\n"; - my $ol = $left_w + $interior_margin + int(($thumb_w - $tw)/2); - my $ot = $top_h + $interior_margin + int(($thumb_h - $th)/2); - my $tit = ($annot ne "") ? " title=\"$annot\"" : ""; - print "Photo\n"; - print "\n"; - print "\n"; - print "
    \n\n"; -} - -sub run($$) { - my ($class, $gal) = @_; - my $self = $class->SUPER::attach($gal); - $self->dispatch(); - return $self; -} - -1; diff --git a/gal/UCW/Gallery/Web/Plain.pm b/gal/UCW/Gallery/Web/Plain.pm deleted file mode 100644 index f7d213c..0000000 --- a/gal/UCW/Gallery/Web/Plain.pm +++ /dev/null @@ -1,70 +0,0 @@ -# Plain Theme for MJ's Photo Gallery -# (c) 2012 Martin Mares ; GPL'ed - -package UCW::Gallery::Web::Plain; - -use strict; -use warnings; - -use UCW::Gallery; -use UCW::Gallery::Web; - -our @ISA = qw(UCW::Gallery::Web); - -my $theme_name = "plain"; -my $navw = 48; -my $navh = 48; -my $box_w = 130; -my $box_h = 110; - -sub theme_dir($) { - my ($self) = @_; - return $self->get('ThemeUrlPrefix') . $theme_name; -} - -sub theme_head_extras($) { - my ($self) = @_; - my $stylesheet = $self->theme_dir . "/style.css"; - return "\n"; -} - -sub show_links($$$$) { - my ($self, $prev, $up, $next) = @_; - my $theme = $self->theme_dir; - print "

    "; - print ""; - print "Back" if $prev ne ""; - print "\n"; - printf ""; - printf "Forward" if $next ne ""; - print "\n"; - printf "Up" if $up ne ""; -} - -sub show_thumb($) { - my ($self, $meta, $photo_id, $click_url) = @_; - my $theme = $self->theme_dir; - my $m = $meta->{photo}->{$photo_id}; - my $annot = UCW::CGI::html_escape($m->{title}); - my $tf = $self->get('ThumbFormats')->[0]; - my ($thumb_w, $thumb_h) = $self->{gal}->thumb_fmt_to_size($tf); - my $tm = $meta->{thumb}->{$tf}->{$photo_id} or die "No thumbnails for format $tf found!\n"; - my $tw = $tm->{w}; - my $th = $tm->{h}; - my $thumb = $self->get('ThumbUrlPrefix') . "$tf/$photo_id.jpg"; - print "

    \n"; - my $ol = int(($box_w - $tw)/2); - my $ot = int(($box_h - $th)/2); - my $tit = ($annot ne "") ? " title=\"$annot\"" : ""; - print "Photo\n"; - print "
    \n\n"; -} - -sub run($$) { - my ($class, $gal) = @_; - my $self = $class->SUPER::attach($gal); - $self->dispatch(); - return $self; -} - -1; diff --git a/gal/bin/gal-cache b/gal/bin/gal-cache deleted file mode 100755 index 73c97db..0000000 --- a/gal/bin/gal-cache +++ /dev/null @@ -1,92 +0,0 @@ -#!/usr/bin/perl -# UCW Gallery: Prepare cache -# (c) 2004--2012 Martin Mares - -use strict; -use warnings; - -use UCW::Gallery; -use Image::Magick; -use IO::Handle; -use File::Spec; -use File::Path; - -STDOUT->autoflush(1); - -my $gal = UCW::Gallery->load_config; - -print "Reading gallery.list\n"; -my $orig_list = $gal->read_list('gallery.list') or die "Cannot read gallery.list: $!\n"; - -my $photo_meta = $gal->photo_meta_name; -print "Reading meta-data from $photo_meta\n"; --f $photo_meta or die "Cannot load $photo_meta\n"; -my $meta = $gal->read_meta($photo_meta); - -my $cache_dir = $gal->get('CacheDir'); -if (-d $cache_dir) { - print "Deleting old cache directory: $cache_dir\n"; - File::Path::remove_tree($cache_dir); -} -print "Creating cache directory: $cache_dir\n"; -File::Path::mkpath($cache_dir) or die "Unable to create $cache_dir: $!\n"; - -# Construct sequence and store photo titles -$meta->{sequence} = []; -for my $f (@$orig_list) { - push @{$meta->{sequence}}, $f->{id}; - my $m = $meta->{photo}->{$f->{id}} or die; - $m->{title} = $f->{title}; -} - -for my $thumb_fmt (@{$gal->get('ThumbFormats')}) { - print "Generating $thumb_fmt thumbnails\n"; - my ($tw, $th) = $gal->thumb_fmt_to_size($thumb_fmt); - my $thumb_meta = {}; - $meta->{thumb}->{$thumb_fmt} = $thumb_meta; - my $thumb_dir = File::Spec->catfile($cache_dir, $thumb_fmt); - -d $thumb_dir or File::Path::mkpath($thumb_dir) or die "Unable to create $thumb_dir: $!\n"; - - for my $id (@{$meta->{sequence}}) { - my $m = $meta->{photo}->{$id} or die; - print "\t$id: "; - - my $p = new Image::Magick; - my $photo = $gal->photo_name($m, $id); - my $e; - $e = $p->Read($photo) and die "Error reading $photo: $e"; - - my $w = $m->{w}; - my $h = $m->{h}; - if ($w > $tw) { - my $s = $tw / $w; - $w = $w * $s; - $h = $h * $s; - } - if ($h > $th) { - my $s = $th / $h; - $w = $w * $s; - $h = $h * $s; - } - $w = int($w + .5); - $h = int($h + .5); - print "${w}x${h} "; - $p->Resize(width=>$w, height=>$h); - - my $out = File::Spec->catfile($thumb_dir, "$id.jpg"); - my $tmp = "$photo.new"; - $e = $p->Write($tmp) and die "Unable to write $tmp: $e"; - rename $tmp, $out or die "Unable to rename $tmp to $out: $!\n"; - - $thumb_meta->{$id} = { - 'w' => $w, - 'h' => $h, - }; - - print "... OK\n"; - } -} - -my $cache_meta = $gal->cache_meta_name; -print "Writing meta-data to $cache_meta\n"; -$gal->write_meta($cache_meta, $meta); diff --git a/gal/bin/gal-date b/gal/bin/gal-date deleted file mode 100755 index 451201c..0000000 --- a/gal/bin/gal-date +++ /dev/null @@ -1,98 +0,0 @@ -#!/usr/bin/perl -# UCW Gallery: Symlink photos according to their EXIF timestamps -# (c) 2013 Martin Mares - -use strict; -use warnings; - -use Image::EXIF; -use Getopt::Long; - -if (@ARGV && $ARGV[0] eq '--help') { - die <] - or: gal date [] - -Options: --d, --destdir= Create symlinks in specified directory (default: current dir) --n, --dry-run Perform a trial run with no changes made --o, --offset= Adjust timestamps by a given offset ([:[:]]) - --suffix= Add suffix to all names --s, --symbolic Create symbolic links (default: hardlinks) -AMEN -} - -my $destdir = '.'; -my $dry_run = 0; -my $offset = 0; -my $suffix = ""; -my $symbolic = 0; -Getopt::Long::Configure('bundling'); -GetOptions( - 'destdir|d=s' => \$destdir, - 'dry-run|n!' => \$dry_run, - 'offset|o=s' => \$offset, - 'suffix=s' => \$suffix, - 'symbolic|s!' => \$symbolic, -) or die "Try gal date --help\n"; - -my @src = @ARGV; -if (!@src) { - while () { - chomp; - push @src, $_; - } -} - -if ($offset =~ m{^([+-])?(\d)+(:(\d+)(:(\d+))?)?$}) { - $offset = $2 * 3600 + ($4 // 0) * 60 + ($6 // 0); - if ($1 eq '-') { $offset = -$offset; } -} else { - die "Invalid offset: $offset\n"; -} - -foreach my $f (@src) { - my $e = new Image::EXIF($f); - my $i = $e->get_all_info(); - if ($e->error) { - print STDERR "EXIF error on $f: ", $e->error, "\n"; - next; - } - # print STDERR Dumper($i), "\n"; - - my $d = $i->{'image'}->{'Image Created'}; - if (!defined $d) { - print STDERR "No date for $f\n"; - next; - } - my ($ty, $tm, $td, $tH, $tM, $tS) = ($d =~ m{^(\d{4}):(\d{2}):(\d{2}) (\d{2}):(\d{2}):(\d{2})$}) or die "EXIF date parse error: $d\n"; - my $t = 3600*$tH + 60*$tM + $tS; - $t = int($t + $offset); - $tH = int($t / 3600); - $tM = int(($t % 3600) / 60); - $tS = $t % 60; - - my $retry = 0; - my $dest = ""; - do { - $dest = sprintf("%s/%04d-%02d-%02d-%02d:%02d:%02d%s%s.jpg", - $destdir, - $ty, $tm, $td, - $tH, $tM, $tS, - $suffix, - $retry ? sprintf("-%d", $retry) : ""); - $retry++; - } while (-f $dest); - - print "$f $dest\n"; - - unless ($dry_run) { - if ($symbolic) { - symlink $f, $dest or die "Cannot symlink $f to $dest: $!\n"; - } else { - link $f, $dest or die "Cannot hardlink $f to $dest: $!\n"; - } - } -} - -STDOUT->autoflush(1); diff --git a/gal/bin/gal-dump-config b/gal/bin/gal-dump-config deleted file mode 100755 index 600a9d5..0000000 --- a/gal/bin/gal-dump-config +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/perl -# UCW Gallery: Show configuration variables -# (c) 2004--2012 Martin Mares - -use strict; -use warnings; - -use UCW::Gallery; -use Data::Dumper; - -my $gal = UCW::Gallery->load_config(); - -for my $k (sort $gal->get_config_keys) { - my $d = Data::Dumper->new([ $gal->get($k) ]); - $d->Terse(1); - print "$k=", $d->Dump; -} diff --git a/gal/bin/gal-dump-meta b/gal/bin/gal-dump-meta deleted file mode 100755 index 1734183..0000000 --- a/gal/bin/gal-dump-meta +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/perl -# UCW Gallery: Dump meta-data -# (c) 2004--2012 Martin Mares - -use strict; -use warnings; - -use UCW::Gallery; -use Data::Dumper; - -@ARGV == 1 or die "Usage: $0 \n"; - -my $gal = UCW::Gallery->load_config; -my $meta = $gal->read_meta($ARGV[0]); -print Dumper($meta); diff --git a/gal/bin/gal-from-gqview b/gal/bin/gal-from-gqview deleted file mode 100755 index 138af5a..0000000 --- a/gal/bin/gal-from-gqview +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/perl -# UCW Gallery: Extract image list from GQview collection -# (c) 2004--2012 Martin Mares - -use strict; -use warnings; - -while (<>) { - chomp; - /^#/ && next; - /^$/ && next; - if (/^"(.*)"$/) { - print "$1\n"; - } else { - die "Error parsing collection: $_"; - } -} diff --git a/gal/bin/gal-gen b/gal/bin/gal-gen deleted file mode 100755 index 1ee10b0..0000000 --- a/gal/bin/gal-gen +++ /dev/null @@ -1,212 +0,0 @@ -#!/usr/bin/perl -# UCW Gallery: Generate published photos -# (c) 2004--2015 Martin Mares - -use strict; -use warnings; - -use UCW::Gallery; -use Image::EXIF; -use Image::Magick; -use IO::Handle; -use File::Spec; -use File::Path; - -STDOUT->autoflush(1); - -my $gal = UCW::Gallery->load_config; - -my $orig_list = $gal->read_list('gallery.list') or die "Cannot read gallery.list: $!\n"; - -my $photo_dir = $gal->get('PhotoDir'); -if (-d $photo_dir) { - print "Using existing output directory: $photo_dir\n"; -} else { - print "Creating output directory: $photo_dir\n"; - File::Path::mkpath($photo_dir) or die "Unable to create $photo_dir: $!\n"; -} - -my $photo_meta = $gal->photo_meta_name; -my $old_meta = {}; -if (-f $photo_meta) { - print "Reading old meta-data\n"; - $old_meta = $gal->read_meta($photo_meta); - # use Data::Dumper; print "Read old meta: ", Dumper($old_meta), "\n"; -} -my $meta = { 'photo' => {} }; - -sub get_meta_basic($$$) { - my ($f, $m, $p) = @_; - my $rotate = $f->{orientation}; - - my ($orig_w, $orig_h, $orig_size, $orig_format) = $p->PingImage($f->{orig}) or die "Error reading " . $f->{orig} . "\n"; - print "${orig_w}x${orig_h} "; - - my ($w0, $h0) = ($rotate eq "l" || $rotate eq "r") ? ($orig_h, $orig_w) : ($orig_w, $orig_h); - my ($w, $h) = ($w0, $h0); - if ($w > $gal->get('PhotoMaxWidth')) { - my $s = $gal->get('PhotoMaxWidth') / $w; - $w = $w * $s; - $h = $h * $s; - } - if ($h > $gal->get('PhotoMaxHeight')) { - my $s = $gal->get('PhotoMaxHeight') / $h; - $w = $w * $s; - $h = $h * $s; - } - $w = int($w + .5); - $h = int($h + .5); - - $m->{o} = $rotate; - for my $k (keys %UCW::Gallery::list_attrs) { - next if $UCW::Gallery::list_attrs{$k} < 2; - $m->{$k} = $f->{$k} if defined $f->{$k}; - } - $m->{w0} = $w0; - $m->{h0} = $h0; - $m->{w} = $w; - $m->{h} = $h; -} - -sub parse_geo($) { - my ($g) = @_; - defined $g or return; - if ($g =~ m{^([NEWS]) (\d+)\xb0 ([0-9.]+)'$}) { - $g = $2 + $3/60; - $g = -$g if $1 eq 'W' || $1 eq 'S'; - } elsif ($g =~ m{^([NEWS]) (\d+)\xb0 (\d+)' ([0-9.]+)$}) { - $g = $2 + $3/60 + $4/3600; - $g = -$g if $1 eq 'W' || $1 eq 'S'; - } else { - print "[EXIF: unable to parse coordinate $g] "; - return; - } - return sprintf "%.6f", $g; -} - -sub get_meta_exif($$) { - my ($f, $m) = @_; - $gal->get('CacheExif') or return; - - my $e = new Image::EXIF($f->{orig}); - my $i = $e->get_all_info(); - if ($e->error) { - print "[EXIF error: ", $e->error, "] "; - return; - } - # use Data::Dumper; print Dumper($i); - - my $lat = parse_geo($i->{image}->{'Latitude'}); - my $lon = parse_geo($i->{image}->{'Longitude'}); - - my $alt = $i->{image}->{'Altitude'}; - if ($alt) { - if ($alt =~ m{^([0-9.]+) m$}) { - $alt = $1; - } else { - print "[EXIF: unable to parse altitude $alt] "; - $alt = undef; - } - } - - # printf "[GEO: lat=%s lon=%s alt=%s] ", $lat // '?', $lon // '?', $alt // '?'; - if ($lat && $lon) { - $m->{lat} //= $lat; - $m->{lon} //= $lon; - } - $m->{alt} //= $alt if $alt; - - my $time = $i->{image}->{'Image Created'}; - if ($time) { - if ($time =~ m{^(\d{4}):(\d{2}):(\d{2}) (\d{2}:\d{2}:\d{2})$}) { - $m->{t} //= "$1-$2-$3 $4"; - # print "[TIME: ", $m->{t}, "] "; - } else { - print "[EXIF: unable to parse time $time] "; - } - } -} - -sub generate_photo($$$) { - my ($f, $m, $p) = @_; - - my $e; - $e = $p->Read($f->{orig}) and die "Error reading " . $f->{orig} . ": $e"; - $p->Strip; - $p->SetAttribute(quality=>90); - - my $xfrm = $m->{xf}; - if ($xfrm =~ /s/) { - print "-> sharpen "; - $p->Sharpen(1); - } - if ($xfrm =~ /h/) { - print "-> equalize "; - $p->Equalize(); - } - if ($xfrm =~ /n/) { - print "-> normalize "; - $p->Normalize(); - } - - my $rotate = $m->{o}; - my $rot = 0; - if ($rotate eq "l") { $rot = 270; } - elsif ($rotate eq "r") { $rot = 90; } - elsif ($rotate eq "u") { $rot = 180; } - if ($rot) { - print "-> rotate $rot "; - $p->Rotate(degrees=>$rot); - } - - my ($w, $h) = ($m->{w}, $m->{h}); - if ($w != $m->{w0} || $h != $m->{h0}) { - print "-> ${w}x${h} "; - $p->Resize(width=>$w, height=>$h); - } - - my $photo = $gal->photo_name($m, $f->{id}); - my $tmp = "$photo.new"; - $e = $p->Write($tmp) and die "Unable to write $tmp: $e"; - rename $tmp, $photo or die "Cannot rename $tmp to $photo: $!\n"; -} - -for my $f (@$orig_list) { - my $id = $f->{id}; - print "$id: "; - - my $m = { }; - $meta->{photo}->{$id} = $m; - $f->{orig} = File::Spec->rel2abs($f->{file}, $gal->get('OrigDir')); - - my $p = new Image::Magick; - get_meta_basic($f, $m, $p); - get_meta_exif($f, $m); - - my $om = $old_meta->{photo}->{$id}; - if ($om && - $om->{o} eq $m->{o} && - $om->{xf} eq $m->{xf} && - $om->{w} eq $m->{w} && - $om->{h} eq $m->{h}) { - print "... uptodate\n"; - next; - } - - generate_photo($f, $m, $p); - print "... OK\n"; -} - -print "Cleaning up stale files\n"; -for my $f (<$photo_dir/*.jpg>) { - my ($vv, $dd, $id) = File::Spec->splitpath($f); - $id =~ s{\..*$}{}; - unless (defined $meta->{photo}->{$id}) { - print "$id: removing\n"; - unlink $f; - } -} - -print "Writing meta-data\n"; -$gal->write_meta($photo_meta, $meta); -exit 0; diff --git a/gal/bin/gal-mj-digikam b/gal/bin/gal-mj-digikam deleted file mode 100755 index 8576f89..0000000 --- a/gal/bin/gal-mj-digikam +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/perl - -use strict; -use warnings; - -use Cwd; -use DBI; -use Getopt::Long; - -if (@ARGV && $ARGV[0] eq '--help') { - die <] -AMEN -} - -my $photos_root = $ENV{HOME} . '/photos'; - -my $album = $ARGV[0]; -if (!defined $album) { - my $cwd = getcwd; - $cwd =~ m{/photos/(.*)} or die "Cannot identify album from current directory, need to specify maunally.\n"; - $album = $1; -} - -if (! -f "gallery.cf") { - system 'gal', 'mj-init'; die if $?; -} - -my $dbfile = "$photos_root/digikam4.db"; -my $dbh = DBI->connect("dbi:SQLite:dbname=$dbfile", "", "") or die "Cannot access $dbfile\n"; - -my %alba = (); -for my $r (@{$dbh->selectall_arrayref( - 'SELECT a.id AS id, r.label AS label, a.relativePath AS rpath FROM Albums a JOIN AlbumRoots r ON (r.id = a.albumRoot)', - { Slice => {} })}) { - my $name = $r->{label} . $r->{rpath}; - # print "$name\n"; - $alba{$name} = $r->{id}; -} - -my $album_id = $alba{$album} // die "Unknown album $album\n"; -print "## Album $album: id=$album_id\n"; - -my ($tag_id) = $dbh->selectrow_array('SELECT id FROM Tags WHERE pid=0 AND name="web"'); -$tag_id // die "Cannot find web tag\n"; -print "## Tag ID: $tag_id\n"; - -my $res = $dbh->selectall_arrayref( < {} }, - SELECT - i.name AS name, - p.latitudeNumber AS lat, - p.longitudeNumber AS lon, - p.altitude AS alt - FROM Images i - JOIN ImageTags t ON (i.id = t.imageid) - LEFT JOIN ImagePositions p ON (i.id = p.imageid) - WHERE i.album=? AND t.tagid=? - ORDER BY i.modificationDate -AMEN - $album_id, - $tag_id, - ); - -open OUT, '|-', $ENV{GALLERY_ROOT} . '/bin/gal-scan' or die "Cannot feed gal scan\n"; -for my $r (@$res) { - print OUT "$photos_root/$album/" . $r->{name}, "\n"; - for my $k (qw(lat lon alt)) { - print OUT "\t$k=", $r->{$k}, "\n" if defined $r->{$k}; - } -} -close OUT; diff --git a/gal/bin/gal-mj-init b/gal/bin/gal-mj-init deleted file mode 100755 index 1105254..0000000 --- a/gal/bin/gal-mj-init +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/sh -set -e - -if [ -f gallery.cf ] ; then - echo >&2 'gallery.cf already present, giving up!' - exit 1 -fi - -cat >gallery.cf <<'AMEN' -use strict; -use warnings; -use utf8; - -my $gal = require '../../default.cf'; -$gal->set( - Title => 'Unnamed', -); - -return $gal; -AMEN diff --git a/gal/bin/gal-mj-map b/gal/bin/gal-mj-map deleted file mode 100755 index b9f48d9..0000000 --- a/gal/bin/gal-mj-map +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/perl - -use strict; -use warnings; - -use Digest::SHA; -use File::Find; - -STDOUT->autoflush(1); - -find({ - wanted => sub { - my $name = $File::Find::name; - $name =~ s{^\./}{}; - $name =~ m{^\d} or return; - $name =~ m{\.jpe?g$}i or return; - -f $name or return; - print "$name\t"; - - my $sha = Digest::SHA->new(1); - $sha->addfile($name) or die "Cannot hash $name\n"; - my $id = substr($sha->hexdigest, 0, 16); - print "$id\n"; - }, - no_chdir => 1, -}, "."); diff --git a/gal/bin/gal-mj-migrate-check b/gal/bin/gal-mj-migrate-check deleted file mode 100755 index 5936f32..0000000 --- a/gal/bin/gal-mj-migrate-check +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/perl -# Check that thumbnail aspect ratios match pre-migration data - -use strict; -use warnings; - -use UCW::Gallery; - -my $gal = UCW::Gallery->load_config; -my $meta = $gal->read_meta($gal->cache_meta_name); - -open T, "gallery.thumb" or die "Cannot read gallery.thumb\n"; -my @thumbs = (); -while () { - chomp; - my ($name, $tw, $th) = split /\s+/; - push @thumbs, [ $tw, $th ]; -} -close T; - -for my $id (@{$meta->{sequence}}) { - my ($tw, $th) = @{shift @thumbs} or die; - my $ta = $tw / $th; - my $m = $meta->{photo}->{$id} or die; - my $pw = $m->{w}; - my $ph = $m->{h}; - my $pa = $pw / $ph; - if (abs($ta - $pa) > 0.05) { - if (abs($ta - 1/$pa) > 0.05) { - print STDERR "$id: Mismatched aspect ratio: orig $ta (${tw}x${th}) new $pa (${pw}x${ph})\n"; - } else { - print STDERR "$id: Mismatched rotation\n"; - } - } -} diff --git a/gal/bin/gal-mj-upgrade b/gal/bin/gal-mj-upgrade deleted file mode 100755 index 66c260f..0000000 --- a/gal/bin/gal-mj-upgrade +++ /dev/null @@ -1,154 +0,0 @@ -#!/usr/bin/perl - -use strict; -use warnings; - -use UCW::Gallery; -use File::Spec; -use Image::Magick; - -my $photos_root = $ENV{HOME} . '/photos'; - -my $album = $ARGV[0] // die "Usage: gal mj-upgrade \n"; -print "### $album ###\n"; - -### Scan photos.map ### - -my $year = $album; -$year =~ s{/.*}{}; -if ($album eq '2009/Fireworks') { $year = 2008; } -print "Loading map for $year\n"; -open M, "$photos_root/photos.map" or die "No map file found\n"; -my %map = (); -while () { - chomp; - m{^$year} or next; - my ($path, $hash) = split /\t/; - my $name = $path; - $name =~ s{.*/}{}; - if (defined $map{$name}) { - my $prev = $map{$name}; - if ($prev->{hash} ne $hash) { - print STDERR "Collision for $name: ", $prev->{path}, " vs. ", $path, "\n"; - } else { - # print STDERR "Harmless collision for $name: ", $prev->{path}, " vs. ", $path, "\n"; - } - } else { - $map{$name} = { path => $path, hash => $hash }; - } -} -close M; - -### Scan static list (if any) ### - -my @src = (); -if (open S, "../static/photos/$album/x") { - print "Found static list\n"; - while () { - chomp; - my @fields = split /\t/; - if (@fields == 4) { - push @src, { name => $fields[0], rotate => $fields[2], xform => $fields[3] }; - } elsif (@fields == 2) { - push @src, { name => $fields[0], rotate => $fields[1], xform => '.' }; - } else { - die "Error parsing gallery list: $_\n"; - } - } - close S; -} - -### Parse index.cgi and produce gallery.new ### - -open I, "$album/index.cgi" or die "Cannot find $album/index.cgi\n"; -open W, ">$album/gallery.new" or die "Cannot create $album/gallery.new\n"; -open T, ">$album/gallery.thumb" or die "Cannot create $album/gallery.thumb\n"; -my %opt = (); -my %found_dirs = (); -my @items = (); -while () { - chomp; - if (/^\s+"(\w+)" => "(.*)",?$/) { - $opt{$1} = $2; - print "Option: $1 = $2\n"; - } elsif (/^img\("([^"]+)(\.jpe?g)", "([^"]*)"\);\s*# (\S+)/) { - my $nr = $1; - my $ext = $2; - my $title = $3; - my $file = $4; - $file =~ s{^.*/}{}g; - - my $map = $map{$file}; - if (!$map) { - print STDERR "$album: No match for $file\n"; - next; - } - my $path = $map->{path}; - my ($vv, $dd, $ff) = File::Spec->splitpath($path); - $found_dirs{$dd} = 1; - - print "Image: $nr $path [$title]\n"; - my $src; - if (@src) { - if ($nr =~ m{^\d+$} && $nr <= @src) { - $src = $src[$nr-1]; - } else { - print STDERR "$album: Crooked refs ($nr)\n"; - } - } - - print W join("\t", - "$photos_root/$path", - $map->{hash}, - ($src ? $src->{rotate} : '-'), - ($src ? $src->{xform} : '.'), - ($title ne "" ? $title : '-'), - ), "\n"; - - - my $thumb = "../static/photos/$album/$nr-thumb.jpg"; - if (!-f $thumb) { - print STDERR "$album: Cannot find thumbnail for photo $nr ($thumb)\n"; - print T "1 1\n"; - } else { - my $im = new Image::Magick; - my ($thumb_w, $thumb_h, $thumb_size, $thumb_format) = $im->PingImage($thumb) or die "Error reading $thumb\n"; - print T "$thumb $thumb_w $thumb_h\n"; - } - } elsif (/^($|#|require|SetOptions|\)|Start|Finish)/) { - # Nothing important - } else { - print STDERR "$album/index.cgi: Parse error at line $.: $_\n"; - } -} -close T; -close W; -close I; - -if (scalar keys %found_dirs != 1) { - print STDERR "$album: Photos in multiple directories\n"; -} - -### Create gallery.cf ### - -open CF, ">$album/gallery.cf" or die "Cannot create $album/gallery.cf"; -print CF <<'AMEN' ; -use strict; -use warnings; -use utf8; - -my $gal = require '../../default.cf'; -$gal->set( -AMEN - -for my $cf (reverse sort keys %opt) { - print CF "\t$cf => \"", $opt{$cf}, "\",\n"; -} - -print CF <<'AMEN' ; -); - -return $gal; -AMEN - -close CF; diff --git a/gal/bin/gal-mj-upload b/gal/bin/gal-mj-upload deleted file mode 100755 index 6fc7cf1..0000000 --- a/gal/bin/gal-mj-upload +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/perl - -use strict; -use warnings; - -print "## Updating Git\n"; -system "git", "pull"; -die if $?; - -print "## Ensuring that photos are generated\n"; -system "gal", "gen"; -die if $?; - -use Cwd; -my $cwd = getcwd; -my ($root, $album) = $cwd =~ m{(.*)/photos/(.*)} or die "Cannot identify album from current directory, giving up.\n"; -my $static = "$root/static/gallery/photo/$album"; --d $static or die "Cannot find generated photos in $static, giving up.\n"; - -print "## Uploading album $album\n"; -system "rs", "$static/", "jw:www/static/gallery/photo/$album/"; -die if $?; - -print "## Calling editor on index files\n"; -system 'vim', 'gallery.cf', "$root/photos/Makefile", "$root/photos/index.thtml"; -die if $?; - -print "## Committing to repository\n"; -system 'git', 'add', 'gallery.cf', 'gallery.list'; die if #?; -system 'git', 'add', "$root/photos/Makefile", "$root/photos/index.thtml"; die if $?; -system 'git', 'commit'; die if $?; -system 'git', 'push'; die if $?; - -print "## Pulling at the server\n"; -system "ssh", "jw", "cd web && git pull && make"; -die if $?; - -print "## Regenerating cache at the server\n"; -system "ssh", "jw", "cd web/photos/$album && ~/web/gal/gal cache"; -die if $?; - -print "Done.\n"; diff --git a/gal/bin/gal-scan b/gal/bin/gal-scan deleted file mode 100755 index a976d5d..0000000 --- a/gal/bin/gal-scan +++ /dev/null @@ -1,150 +0,0 @@ -#!/usr/bin/perl -# UCW Gallery: Scan images and generate image list -# (c) 2004--2015 Martin Mares - -use strict; -use warnings; - -use UCW::Gallery; -use UCW::Gallery::Hashes; -use File::Spec; -use Image::EXIF; -use Getopt::Long; - -if (@ARGV && $ARGV[0] eq '--help') { - die < - or: gal scan - or: gal scan --update - -Options: ---add Keep existing images and add new ones -AMEN -} - -my $add; -my $update; -GetOptions( - 'add!' => \$add, - 'update!' => \$update, -) or die "Try gal scan --help\n"; - -STDOUT->autoflush(1); -my $gal = UCW::Gallery->load_config; -my $orig_prefix = $gal->get('OrigDir'); -$orig_prefix =~ m{/$} or $orig_prefix .= '/'; - -my @source = (); -if ($update) { - print "Loading previous gallery.list\n"; - my $pg = $gal->read_list('gallery.list') or die "Unable to load gallery.list\n"; - @source = @{$pg}; -} elsif (@ARGV) { - for my $in (@ARGV) { - if (-f $in) { - push @source, { file => $in }; - } elsif (-d $in) { - opendir D, $in or die "Cannot scan directory $in: $!\n"; - my @p = (); - while (my $e = readdir D) { - my $f = File::Spec->canonpath(File::Spec->catfile($in, $e)); - if ($f =~ m{\.(jpe?g|png)$}i) { - push @p, $f; - } - } - closedir D; - push @source, map { { file => $_ } } sort @p; - } else { - die "$in is neither file nor directory\n"; - } - } -} else { - binmode STDIN, ':utf8'; - @source = @{$gal->read_list_fh(\*STDIN)}; -} - -my $hashes = UCW::Gallery::Hashes->new($gal); - -print "Scanning photos\n"; -my @images = (); -foreach my $src (@source) { - my $name = $src->{file}; - if ($name =~ m{^/}) { - # Try to relativize to OrigDir - if (substr($name, 0, length $orig_prefix) eq $orig_prefix) { - $src->{file} = $name = substr($name, length $orig_prefix); - } - } - print "\t$name:"; - - my $path = File::Spec->rel2abs($name, $gal->get('OrigDir')); - -f $path or die "Cannot find $path\n"; - - $src->{id} = $hashes->hash_image($path); - print " id=", $src->{id}; - - if (!defined $src->{orientation} || $src->{orientation} eq '-') { - my $e = new Image::EXIF($path); - my $i = $e->get_all_info(); - if ($e->error) { - print "EXIF error: ", $e->error, "\n"; - $src->{orientation} = '.'; - } else { - # print STDERR Dumper($i), "\n"; - my $o = $i->{'image'}->{'Image Orientation'} || "Top, Left-Hand"; - if ($o eq "Top, Left-Hand") { $o = "."; } - elsif ($o eq "Right-Hand, Top") { $o = "r"; } - elsif ($o eq "Left-Hand, Bottom") { $o = "l"; } - elsif ($o eq "Bottom, Right-Hand") { $o = "u"; } - else { - print "Unrecognized orientation: $o\n"; - $o = "."; - } - $src->{orientation} = $o; - } - } - print " ori=", $src->{orientation}; - - defined $src->{xf} or $src->{xf} = $gal->get('ScanDefaultTransform'); - print " xfrm=", $src->{xf}; - - $src->{title} //= ''; - push @images, $src; - print "\n"; -} - -if (!$update) { - my $old = $gal->read_list('gallery.list'); - if ($old) { - print "Updating gallery.list\n"; - my %new_by_id = map { $_->{id} => $_ } @images; - my @result = (); - for my $o (@$old) { - my $id = $o->{id}; - my $i = $new_by_id{$id}; - if (!$i) { - if ($add) { - print "\t$id: kept\n"; - push @result, $o; - } else { - print "\t$id: removed\n"; - } - } else { - print "\t$id: kept\n"; - push @result, $o; - delete $new_by_id{$id}; - } - } - for my $i (@images) { - my $id = $i->{id}; - $new_by_id{$id} or next; - print "\t$id: new\n"; - push @result, $i; - } - @images = @result; - } -} - -$gal->write_list('gallery.list', \@images); -print "Written gallery.list (", (scalar @images), " items)\n"; -$hashes->write; diff --git a/gal/gal b/gal/gal deleted file mode 100755 index f16a5f7..0000000 --- a/gal/gal +++ /dev/null @@ -1,114 +0,0 @@ -#!/usr/bin/perl -# UCW Gallery -- Master Program -# (c) 2012 Martin Mares - -use strict; -use warnings; -use Getopt::Long; - -use FindBin; -my $gallery_root = $FindBin::Bin; - -my $all; -my $parallel; - -sub show_help() { - print < - -General options: - --all Run on all galleries in subdirectories --p, --parallel= Run in parallel and use processes - -Common commands: -scan Scan directory and obtain originals -gen Generate web photos from originals -cache Rebuild thumbnails and other cached info - -Other commands: -dump-config Show configuration settings -dump-meta Show contents of metadata files -from-gqview Parse gqview/geeqie collections -AMEN - exit 0; -} - -Getopt::Long::Configure('require_order', 'bundling'); -GetOptions( - "help" => \&show_help, - "version" => sub { - print "UCW Gallery 2.0 (c) 2004-2012 Martin Mares \n"; - }, - "all!" => \$all, - "p|parallel=i" => \$parallel, -) or die "Try `gal help' for more information.\n"; -Getopt::Long::Configure('default'); - -@ARGV or die "Missing subcommand.\n"; -my $sub = shift @ARGV; -$sub =~ /^[0-9a-zA-Z-]+$/ or die "Invalid subcommand $sub\n"; - -if ($sub eq 'help') { show_help(); } - -my $sub_path = "$gallery_root/bin/gal-$sub"; --x $sub_path or die "Unknown subcommand $sub\n"; - -$ENV{"GALLERY_ROOT"} = $gallery_root; -$ENV{"PERL5LIB"} = join(":", $gallery_root, $ENV{"PERL5LIB"} // ()); - -if (!$all) { - exec $sub_path, @ARGV; - die "Cannot execute $sub_path: $!\n"; -} - -### Parallel execution ### - -my @dirs = sort map { chomp; s{^\./}{}; s{\/gallery.cf}{}; $_; } `find . -mindepth 2 -name gallery.cf`; -my $done = 0; -my $need = @dirs; -my $logging = $parallel ? 1 : 0; -my $threads = $parallel // 1; - -my $running = 0; -my $errors = 0; -my %pid_to_dir = (); - -while ($running || @dirs) { - if ($running == $threads || !@dirs) { - # Wait for children - my $pid = wait; die if $pid < 0; - my $dir = $pid_to_dir{$pid} or die; - if ($?) { - print "!! $dir FAILED"; - print " [see $dir/gallery.log]" if $logging; - $errors++; - } else { - print "++ $dir"; - unlink "$dir/gallery.log" if $logging; - } - delete $pid_to_dir{$pid}; - $running--; - $done++; - print " (done $done/$need)\n"; - } else { - my $dir = shift @dirs; - print "<< $dir\n"; - my $pid = fork; - if (!$pid) { - if ($logging) { - close STDOUT; - open STDOUT, '>', "$dir/gallery.log" or die; - close STDERR; - open STDERR, '>&STDOUT'; - } - chdir $dir or die "Cannot chdir to $dir: $!\n"; - exec $sub_path, @ARGV; - die "Cannot execute $sub_path: $!\n"; - } - $pid_to_dir{$pid} = $dir; - $running++; - } -} - -print "$done jobs, $errors errors.\n"; -exit ($errors > 0); diff --git a/gal/highslide/custom.css b/gal/highslide/custom.css deleted file mode 100644 index 6ef8df9..0000000 --- a/gal/highslide/custom.css +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Site-specific stylesheet for Highslide JS gallery theme - */ - -H1 { text-align: center; } -H2 { text-align: center; margin-bottom: 3ex; } -.parent { text-align: center; } -.large { text-align: center; } - -.fwd { - float: right; - width: 48px; - height: 48px; -} -.back { - float: left; - width: 48px; - height: 48px; -} -.up { - width: 48px; - height: 48px; -} - -A[href]:hover { background-color: inherit; } - -.highslide-wrapper, .highslide-outline { - background: #111111; -} -.highslide img { - border: 1px solid #D0D0D0; -} -.highslide:hover img { - border-color: #A0A0A0; -} -.highslide-active-anchor img { - visibility: visible; - border-color: #808080 !important; -} -.highslide-image { - border: 2px solid #111111; -} -.highslide-caption { - color: #CCCCCC; - padding: 2px; -} -.highslide-loading { - color: black; - border: 1px solid black; - background-color: white; - background-image: url(graphics/loader.white.gif); -} -.highslide-controls { - position: static !important; - margin: 0; - width: 120px !important; -} -.highslide-gallery ul li { - width: 130px; - height: 110px; - border: 1px solid #505; - margin: 2px; -} -.highslide-dimming { - background: black; -} diff --git a/gal/highslide/custom.js b/gal/highslide/custom.js deleted file mode 100644 index ae76993..0000000 --- a/gal/highslide/custom.js +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Site-specific configuration settings for Highslide JS - */ - -hs.showCredits = true; -hs.creditsPosition = 'top right'; -hs.outlineType = 'rounded-black'; -hs.fadeInOut = false; -hs.align = 'center'; -hs.useBox = true; -hs.width = 1080; -hs.height = 840; -// hs.allowMultipleInstances = false; -hs.captionEval = 'this.thumb.title'; -hs.captionOverlay = { position: "top" }; -hs.expandDuration = 100; -hs.restoreDuration = 100; -hs.dimmingDuration = 100; -hs.dimmingOpacity = 0.8; -hs.transitionDuration = 100; -hs.thumbnailId = 'thumb1'; -hs.numberPosition = 'caption'; -hs.transitions = ['expand', 'crossfade']; - -hs.addSlideshow({ - interval: 5000, - repeat: false, - useControls: true, - fixedControls: 'fit', - overlayOptions: { - className: 'controls-in-heading', - opacity: 0.6, - position: 'top center', - hideOnMouseOut: true - }, - thumbstrip: { - mode: 'horizontal', - position: 'below', - relativeTo: 'expander' - } -}); - - -//////////////////////////////////////////////// -// Dynamic change of the hash part of the URL // -//////////////////////////////////////////////// -var hashTag = ''; -// Use hashDelimiter to "hide" name of the anchor (and to not scroll the page) -var hashDelimiter = '_'; - -hs.extend (hs.Expander.prototype, { - onAfterExpand: function(sender) { - hashTag = this.a.id; - window.location.hash = hashDelimiter + hashTag; - }, - - onBeforeClose: function(sender) { - window.location.hash = ''; - } -}); - -function showDefaultImage() { - var hashParts = window.location.hash.split(hashDelimiter); - var myThumb = document.getElementById(hashParts[1]) - if (hashParts[1] && myThumb) myThumb.click(); - else window.location.hash = ''; -} -// If the new hash is not equal to the hash we store internally, -// then it must be the user hitting the "back/forward" button -function checkHashChange() { - var hashParts = window.location.hash.split(hashDelimiter); - if (hashParts[1] != hashTag) { - hs.close(); - window.location.hash = ''; - } -} - -// Add onLoad and onHashChange functions -if(window.onload) { - var current = window.onload; - var newAction = function() { - current(); - showDefaultImage(); - }; - window.onload = newAction; -} else window.onload = showDefaultImage; - -if(window.onhashchange) { - var current = window.onHashChange; - var newAction = function() { - current(); - checkHashChange(); - }; - window.onhashchange = newAction; -} else window.onhashchange = checkHashChange; diff --git a/gal/highslide/graphics/close.png b/gal/highslide/graphics/close.png deleted file mode 100644 index 4de4396..0000000 Binary files a/gal/highslide/graphics/close.png and /dev/null differ diff --git a/gal/highslide/graphics/closeX.png b/gal/highslide/graphics/closeX.png deleted file mode 100644 index cf5d018..0000000 Binary files a/gal/highslide/graphics/closeX.png and /dev/null differ diff --git a/gal/highslide/graphics/controlbar-black-border.gif b/gal/highslide/graphics/controlbar-black-border.gif deleted file mode 100644 index e2403fe..0000000 Binary files a/gal/highslide/graphics/controlbar-black-border.gif and /dev/null differ diff --git a/gal/highslide/graphics/controlbar-text-buttons.png b/gal/highslide/graphics/controlbar-text-buttons.png deleted file mode 100644 index d2f72e0..0000000 Binary files a/gal/highslide/graphics/controlbar-text-buttons.png and /dev/null differ diff --git a/gal/highslide/graphics/controlbar-white-small.gif b/gal/highslide/graphics/controlbar-white-small.gif deleted file mode 100644 index 462fce7..0000000 Binary files a/gal/highslide/graphics/controlbar-white-small.gif and /dev/null differ diff --git a/gal/highslide/graphics/controlbar-white.gif b/gal/highslide/graphics/controlbar-white.gif deleted file mode 100644 index 1f143f5..0000000 Binary files a/gal/highslide/graphics/controlbar-white.gif and /dev/null differ diff --git a/gal/highslide/graphics/controlbar2.gif b/gal/highslide/graphics/controlbar2.gif deleted file mode 100644 index 39ad652..0000000 Binary files a/gal/highslide/graphics/controlbar2.gif and /dev/null differ diff --git a/gal/highslide/graphics/controlbar3.gif b/gal/highslide/graphics/controlbar3.gif deleted file mode 100644 index 3eebb81..0000000 Binary files a/gal/highslide/graphics/controlbar3.gif and /dev/null differ diff --git a/gal/highslide/graphics/controlbar4-hover.gif b/gal/highslide/graphics/controlbar4-hover.gif deleted file mode 100644 index ca08b59..0000000 Binary files a/gal/highslide/graphics/controlbar4-hover.gif and /dev/null differ diff --git a/gal/highslide/graphics/controlbar4.gif b/gal/highslide/graphics/controlbar4.gif deleted file mode 100644 index 7a3ad34..0000000 Binary files a/gal/highslide/graphics/controlbar4.gif and /dev/null differ diff --git a/gal/highslide/graphics/fullexpand.gif b/gal/highslide/graphics/fullexpand.gif deleted file mode 100644 index 26d9ed0..0000000 Binary files a/gal/highslide/graphics/fullexpand.gif and /dev/null differ diff --git a/gal/highslide/graphics/geckodimmer.png b/gal/highslide/graphics/geckodimmer.png deleted file mode 100644 index 309bb27..0000000 Binary files a/gal/highslide/graphics/geckodimmer.png and /dev/null differ diff --git a/gal/highslide/graphics/icon.gif b/gal/highslide/graphics/icon.gif deleted file mode 100644 index b74a073..0000000 Binary files a/gal/highslide/graphics/icon.gif and /dev/null differ diff --git a/gal/highslide/graphics/loader.big.black.gif b/gal/highslide/graphics/loader.big.black.gif deleted file mode 100644 index c95d05a..0000000 Binary files a/gal/highslide/graphics/loader.big.black.gif and /dev/null differ diff --git a/gal/highslide/graphics/loader.big.white.gif b/gal/highslide/graphics/loader.big.white.gif deleted file mode 100644 index 3288d10..0000000 Binary files a/gal/highslide/graphics/loader.big.white.gif and /dev/null differ diff --git a/gal/highslide/graphics/loader.black.gif b/gal/highslide/graphics/loader.black.gif deleted file mode 100644 index 0b31f6f..0000000 Binary files a/gal/highslide/graphics/loader.black.gif and /dev/null differ diff --git a/gal/highslide/graphics/loader.white.gif b/gal/highslide/graphics/loader.white.gif deleted file mode 100644 index f2a1bc0..0000000 Binary files a/gal/highslide/graphics/loader.white.gif and /dev/null differ diff --git a/gal/highslide/graphics/outlines/beveled.png b/gal/highslide/graphics/outlines/beveled.png deleted file mode 100644 index fc428f4..0000000 Binary files a/gal/highslide/graphics/outlines/beveled.png and /dev/null differ diff --git a/gal/highslide/graphics/outlines/custom.png b/gal/highslide/graphics/outlines/custom.png deleted file mode 100644 index 2f20f70..0000000 Binary files a/gal/highslide/graphics/outlines/custom.png and /dev/null differ diff --git a/gal/highslide/graphics/outlines/drop-shadow.png b/gal/highslide/graphics/outlines/drop-shadow.png deleted file mode 100644 index 0186c2e..0000000 Binary files a/gal/highslide/graphics/outlines/drop-shadow.png and /dev/null differ diff --git a/gal/highslide/graphics/outlines/glossy-dark.png b/gal/highslide/graphics/outlines/glossy-dark.png deleted file mode 100644 index 3c64c0d..0000000 Binary files a/gal/highslide/graphics/outlines/glossy-dark.png and /dev/null differ diff --git a/gal/highslide/graphics/outlines/outer-glow.png b/gal/highslide/graphics/outlines/outer-glow.png deleted file mode 100644 index 288d43f..0000000 Binary files a/gal/highslide/graphics/outlines/outer-glow.png and /dev/null differ diff --git a/gal/highslide/graphics/outlines/rounded-black.png b/gal/highslide/graphics/outlines/rounded-black.png deleted file mode 100644 index a77e65d..0000000 Binary files a/gal/highslide/graphics/outlines/rounded-black.png and /dev/null differ diff --git a/gal/highslide/graphics/outlines/rounded-white.png b/gal/highslide/graphics/outlines/rounded-white.png deleted file mode 100644 index 0d4b817..0000000 Binary files a/gal/highslide/graphics/outlines/rounded-white.png and /dev/null differ diff --git a/gal/highslide/graphics/resize.gif b/gal/highslide/graphics/resize.gif deleted file mode 100644 index 9100de7..0000000 Binary files a/gal/highslide/graphics/resize.gif and /dev/null differ diff --git a/gal/highslide/graphics/scrollarrows.png b/gal/highslide/graphics/scrollarrows.png deleted file mode 100644 index b3d5575..0000000 Binary files a/gal/highslide/graphics/scrollarrows.png and /dev/null differ diff --git a/gal/highslide/graphics/zoom.png b/gal/highslide/graphics/zoom.png deleted file mode 100644 index 908612e..0000000 Binary files a/gal/highslide/graphics/zoom.png and /dev/null differ diff --git a/gal/highslide/graphics/zoomin.cur b/gal/highslide/graphics/zoomin.cur deleted file mode 100644 index cb79124..0000000 Binary files a/gal/highslide/graphics/zoomin.cur and /dev/null differ diff --git a/gal/highslide/graphics/zoomout.cur b/gal/highslide/graphics/zoomout.cur deleted file mode 100644 index acf6199..0000000 Binary files a/gal/highslide/graphics/zoomout.cur and /dev/null differ diff --git a/gal/highslide/highslide-ie6.css b/gal/highslide/highslide-ie6.css deleted file mode 100644 index b4d5484..0000000 --- a/gal/highslide/highslide-ie6.css +++ /dev/null @@ -1,76 +0,0 @@ -.closebutton { - /* NOTE! This URL is relative to the HTML page, not the CSS */ - filter:progid:DXImageTransform.Microsoft.AlphaImageLoader( - src='../highslide/graphics/close.png', sizingMethod='scale'); - - background: none; - cursor: hand; -} - -/* Viewport fixed hack */ -.highslide-viewport { - position: absolute; - left: expression( ( ( ignoreMe1 = document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft ) ) + 'px' ); - top: expression( ( ignoreMe2 = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop ) + 'px' ); - width: expression( ( ( ignoreMe3 = document.documentElement.clientWidth ? document.documentElement.clientWidth : document.body.clientWidth ) ) + 'px' ); - height: expression( ( ( ignoreMe4 = document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight ) ) + 'px' ); -} - -/* Thumbstrip PNG fix */ -.highslide-scroll-down, .highslide-scroll-up { - position: relative; - overflow: hidden; -} -.highslide-scroll-down div, .highslide-scroll-up div { - /* NOTE! This URL is relative to the HTML page, not the CSS */ - filter:progid:DXImageTransform.Microsoft.AlphaImageLoader( - src='../highslide/graphics/scrollarrows.png', sizingMethod='scale'); - background: none !important; - position: absolute; - cursor: hand; - width: 75px; - height: 75px !important; -} -.highslide-thumbstrip-horizontal .highslide-scroll-down div { - left: -50px; - top: -15px; -} -.highslide-thumbstrip-horizontal .highslide-scroll-up div { - top: -15px; -} -.highslide-thumbstrip-vertical .highslide-scroll-down div { - top: -50px; -} - -/* Thumbstrip marker arrow trasparent background fix */ -.highslide-thumbstrip .highslide-marker { - border-color: white; /* match the background */ -} -.dark .highslide-thumbstrip-horizontal .highslide-marker { - border-color: #111; -} -.highslide-viewport .highslide-marker { - border-color: #333; -} -.highslide-thumbstrip { - float: left; -} - -/* Positioning fixes for the control bar */ -.text-controls .highslide-controls { - width: 480px; -} -.text-controls a span { - width: 4em; -} -.text-controls .highslide-full-expand a span { - width: 0; -} -.text-controls .highslide-close a span { - width: 0; -} - -/* Special */ -.in-page .highslide-thumbstrip-horizontal .highslide-marker { - border-bottom: gray; -} diff --git a/gal/highslide/highslide-with-gallery.js b/gal/highslide/highslide-with-gallery.js deleted file mode 100644 index 5576e5a..0000000 --- a/gal/highslide/highslide-with-gallery.js +++ /dev/null @@ -1,2687 +0,0 @@ -/** - * Name: Highslide JS - * Version: 4.1.13 (2011-10-06) - * Config: default +events +slideshow +positioning +transitions +viewport +thumbstrip - * Author: Torstein Hønsi - * Support: www.highslide.com/support - * License: www.highslide.com/#license - */ -if (!hs) { var hs = { -// Language strings -lang : { - cssDirection: 'ltr', - loadingText : 'Loading...', - loadingTitle : 'Click to cancel', - focusTitle : 'Click to bring to front', - fullExpandTitle : 'Expand to actual size (f)', - creditsText : 'Powered by Highslide JS', - creditsTitle : 'Go to the Highslide JS homepage', - previousText : 'Previous', - nextText : 'Next', - moveText : 'Move', - closeText : 'Close', - closeTitle : 'Close (esc)', - resizeTitle : 'Resize', - playText : 'Play', - playTitle : 'Play slideshow (spacebar)', - pauseText : 'Pause', - pauseTitle : 'Pause slideshow (spacebar)', - previousTitle : 'Previous (arrow left)', - nextTitle : 'Next (arrow right)', - moveTitle : 'Move', - fullExpandText : '1:1', - number: 'Image %1 of %2', - restoreTitle : 'Click to close image, click and drag to move. Use arrow keys for next and previous.' -}, -// See http://highslide.com/ref for examples of settings -graphicsDir : 'highslide/graphics/', -expandCursor : 'zoomin.cur', // null disables -restoreCursor : 'zoomout.cur', // null disables -expandDuration : 250, // milliseconds -restoreDuration : 250, -marginLeft : 15, -marginRight : 15, -marginTop : 15, -marginBottom : 15, -zIndexCounter : 1001, // adjust to other absolutely positioned elements -loadingOpacity : 0.75, -allowMultipleInstances: true, -numberOfImagesToPreload : 5, -outlineWhileAnimating : 2, // 0 = never, 1 = always, 2 = HTML only -outlineStartOffset : 3, // ends at 10 -padToMinWidth : false, // pad the popup width to make room for wide caption -fullExpandPosition : 'bottom right', -fullExpandOpacity : 1, -showCredits : true, // you can set this to false if you want -creditsHref : 'http://highslide.com/', -creditsTarget : '_self', -enableKeyListener : true, -openerTagNames : ['a'], // Add more to allow slideshow indexing -transitions : [], -transitionDuration: 250, -dimmingOpacity: 0, // Lightbox style dimming background -dimmingDuration: 50, // 0 for instant dimming - -anchor : 'auto', // where the image expands from -align : 'auto', // position in the client (overrides anchor) -targetX: null, // the id of a target element -targetY: null, -dragByHeading: true, -minWidth: 200, -minHeight: 200, -allowSizeReduction: true, // allow the image to reduce to fit client size. If false, this overrides minWidth and minHeight -outlineType : 'drop-shadow', // set null to disable outlines -skin : { - controls: - '
    ' -}, -// END OF YOUR SETTINGS - - -// declare internal properties -preloadTheseImages : [], -continuePreloading: true, -expanders : [], -overrides : [ - 'allowSizeReduction', - 'useBox', - 'anchor', - 'align', - 'targetX', - 'targetY', - 'outlineType', - 'outlineWhileAnimating', - 'captionId', - 'captionText', - 'captionEval', - 'captionOverlay', - 'headingId', - 'headingText', - 'headingEval', - 'headingOverlay', - 'creditsPosition', - 'dragByHeading', - 'autoplay', - 'numberPosition', - 'transitions', - 'dimmingOpacity', - - 'width', - 'height', - - 'wrapperClassName', - 'minWidth', - 'minHeight', - 'maxWidth', - 'maxHeight', - 'pageOrigin', - 'slideshowGroup', - 'easing', - 'easingClose', - 'fadeInOut', - 'src' -], -overlays : [], -idCounter : 0, -oPos : { - x: ['leftpanel', 'left', 'center', 'right', 'rightpanel'], - y: ['above', 'top', 'middle', 'bottom', 'below'] -}, -mouse: {}, -headingOverlay: {}, -captionOverlay: {}, -timers : [], - -slideshows : [], - -pendingOutlines : {}, -clones : {}, -onReady: [], -uaVersion: /Trident\/4\.0/.test(navigator.userAgent) ? 8 : - parseFloat((navigator.userAgent.toLowerCase().match( /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [0,'0'])[1]), -ie : (document.all && !window.opera), -//ie : navigator && /MSIE [678]/.test(navigator.userAgent), // ie9 compliant? -safari : /Safari/.test(navigator.userAgent), -geckoMac : /Macintosh.+rv:1\.[0-8].+Gecko/.test(navigator.userAgent), - -$ : function (id) { - if (id) return document.getElementById(id); -}, - -push : function (arr, val) { - arr[arr.length] = val; -}, - -createElement : function (tag, attribs, styles, parent, nopad) { - var el = document.createElement(tag); - if (attribs) hs.extend(el, attribs); - if (nopad) hs.setStyles(el, {padding: 0, border: 'none', margin: 0}); - if (styles) hs.setStyles(el, styles); - if (parent) parent.appendChild(el); - return el; -}, - -extend : function (el, attribs) { - for (var x in attribs) el[x] = attribs[x]; - return el; -}, - -setStyles : function (el, styles) { - for (var x in styles) { - if (hs.ieLt9 && x == 'opacity') { - if (styles[x] > 0.99) el.style.removeAttribute('filter'); - else el.style.filter = 'alpha(opacity='+ (styles[x] * 100) +')'; - } - else el.style[x] = styles[x]; - } -}, -animate: function(el, prop, opt) { - var start, - end, - unit; - if (typeof opt != 'object' || opt === null) { - var args = arguments; - opt = { - duration: args[2], - easing: args[3], - complete: args[4] - }; - } - if (typeof opt.duration != 'number') opt.duration = 250; - opt.easing = Math[opt.easing] || Math.easeInQuad; - opt.curAnim = hs.extend({}, prop); - for (var name in prop) { - var e = new hs.fx(el, opt , name ); - - start = parseFloat(hs.css(el, name)) || 0; - end = parseFloat(prop[name]); - unit = name != 'opacity' ? 'px' : ''; - - e.custom( start, end, unit ); - } -}, -css: function(el, prop) { - if (el.style[prop]) { - return el.style[prop]; - } else if (document.defaultView) { - return document.defaultView.getComputedStyle(el, null).getPropertyValue(prop); - - } else { - if (prop == 'opacity') prop = 'filter'; - var val = el.currentStyle[prop.replace(/\-(\w)/g, function (a, b){ return b.toUpperCase(); })]; - if (prop == 'filter') - val = val.replace(/alpha\(opacity=([0-9]+)\)/, - function (a, b) { return b / 100 }); - return val === '' ? 1 : val; - } -}, - -getPageSize : function () { - var d = document, w = window, iebody = d.compatMode && d.compatMode != 'BackCompat' - ? d.documentElement : d.body, - ieLt9 = hs.ie && (hs.uaVersion < 9 || typeof pageXOffset == 'undefined'); - - var width = ieLt9 ? iebody.clientWidth : - (d.documentElement.clientWidth || self.innerWidth), - height = ieLt9 ? iebody.clientHeight : self.innerHeight; - hs.page = { - width: width, - height: height, - scrollLeft: ieLt9 ? iebody.scrollLeft : pageXOffset, - scrollTop: ieLt9 ? iebody.scrollTop : pageYOffset - }; - return hs.page; -}, - -getPosition : function(el) { - var p = { x: el.offsetLeft, y: el.offsetTop }; - while (el.offsetParent) { - el = el.offsetParent; - p.x += el.offsetLeft; - p.y += el.offsetTop; - if (el != document.body && el != document.documentElement) { - p.x -= el.scrollLeft; - p.y -= el.scrollTop; - } - } - return p; -}, - -expand : function(a, params, custom, type) { - if (!a) a = hs.createElement('a', null, { display: 'none' }, hs.container); - if (typeof a.getParams == 'function') return params; - try { - new hs.Expander(a, params, custom); - return false; - } catch (e) { return true; } -}, -getElementByClass : function (el, tagName, className) { - var els = el.getElementsByTagName(tagName); - for (var i = 0; i < els.length; i++) { - if ((new RegExp(className)).test(els[i].className)) { - return els[i]; - } - } - return null; -}, -replaceLang : function(s) { - s = s.replace(/\s/g, ' '); - var re = /{hs\.lang\.([^}]+)\}/g, - matches = s.match(re), - lang; - if (matches) for (var i = 0; i < matches.length; i++) { - lang = matches[i].replace(re, "$1"); - if (typeof hs.lang[lang] != 'undefined') s = s.replace(matches[i], hs.lang[lang]); - } - return s; -}, - - -focusTopmost : function() { - var topZ = 0, - topmostKey = -1, - expanders = hs.expanders, - exp, - zIndex; - for (var i = 0; i < expanders.length; i++) { - exp = expanders[i]; - if (exp) { - zIndex = exp.wrapper.style.zIndex; - if (zIndex && zIndex > topZ) { - topZ = zIndex; - topmostKey = i; - } - } - } - if (topmostKey == -1) hs.focusKey = -1; - else expanders[topmostKey].focus(); -}, - -getParam : function (a, param) { - a.getParams = a.onclick; - var p = a.getParams ? a.getParams() : null; - a.getParams = null; - - return (p && typeof p[param] != 'undefined') ? p[param] : - (typeof hs[param] != 'undefined' ? hs[param] : null); -}, - -getSrc : function (a) { - var src = hs.getParam(a, 'src'); - if (src) return src; - return a.href; -}, - -getNode : function (id) { - var node = hs.$(id), clone = hs.clones[id], a = {}; - if (!node && !clone) return null; - if (!clone) { - clone = node.cloneNode(true); - clone.id = ''; - hs.clones[id] = clone; - return node; - } else { - return clone.cloneNode(true); - } -}, - -discardElement : function(d) { - if (d) hs.garbageBin.appendChild(d); - hs.garbageBin.innerHTML = ''; -}, -dim : function(exp) { - if (!hs.dimmer) { - isNew = true; - hs.dimmer = hs.createElement ('div', { - className: 'highslide-dimming highslide-viewport-size', - owner: '', - onclick: function() { - if (hs.fireEvent(hs, 'onDimmerClick')) - - hs.close(); - } - }, { - visibility: 'visible', - opacity: 0 - }, hs.container, true); - - if (/(Android|iPad|iPhone|iPod)/.test(navigator.userAgent)) { - var body = document.body; - function pixDimmerSize() { - hs.setStyles(hs.dimmer, { - width: body.scrollWidth +'px', - height: body.scrollHeight +'px' - }); - } - pixDimmerSize(); - hs.addEventListener(window, 'resize', pixDimmerSize); - } - } - hs.dimmer.style.display = ''; - - var isNew = hs.dimmer.owner == ''; - hs.dimmer.owner += '|'+ exp.key; - - if (isNew) { - if (hs.geckoMac && hs.dimmingGeckoFix) - hs.setStyles(hs.dimmer, { - background: 'url('+ hs.graphicsDir + 'geckodimmer.png)', - opacity: 1 - }); - else - hs.animate(hs.dimmer, { opacity: exp.dimmingOpacity }, hs.dimmingDuration); - } -}, -undim : function(key) { - if (!hs.dimmer) return; - if (typeof key != 'undefined') hs.dimmer.owner = hs.dimmer.owner.replace('|'+ key, ''); - - if ( - (typeof key != 'undefined' && hs.dimmer.owner != '') - || (hs.upcoming && hs.getParam(hs.upcoming, 'dimmingOpacity')) - ) return; - - if (hs.geckoMac && hs.dimmingGeckoFix) hs.dimmer.style.display = 'none'; - else hs.animate(hs.dimmer, { opacity: 0 }, hs.dimmingDuration, null, function() { - hs.dimmer.style.display = 'none'; - }); -}, -transit : function (adj, exp) { - var last = exp || hs.getExpander(); - exp = last; - if (hs.upcoming) return false; - else hs.last = last; - hs.removeEventListener(document, window.opera ? 'keypress' : 'keydown', hs.keyHandler); - try { - hs.upcoming = adj; - adj.onclick(); - } catch (e){ - hs.last = hs.upcoming = null; - } - try { - if (!adj || exp.transitions[1] != 'crossfade') - exp.close(); - } catch (e) {} - return false; -}, - -previousOrNext : function (el, op) { - var exp = hs.getExpander(el); - if (exp) return hs.transit(exp.getAdjacentAnchor(op), exp); - else return false; -}, - -previous : function (el) { - return hs.previousOrNext(el, -1); -}, - -next : function (el) { - return hs.previousOrNext(el, 1); -}, - -keyHandler : function(e) { - if (!e) e = window.event; - if (!e.target) e.target = e.srcElement; // ie - if (typeof e.target.form != 'undefined') return true; // form element has focus - if (!hs.fireEvent(hs, 'onKeyDown', e)) return true; - var exp = hs.getExpander(); - - var op = null; - switch (e.keyCode) { - case 70: // f - if (exp) exp.doFullExpand(); - return true; - case 32: // Space - op = 2; - break; - case 34: // Page Down - case 39: // Arrow right - case 40: // Arrow down - op = 1; - break; - case 8: // Backspace - case 33: // Page Up - case 37: // Arrow left - case 38: // Arrow up - op = -1; - break; - case 27: // Escape - case 13: // Enter - op = 0; - } - if (op !== null) {if (op != 2)hs.removeEventListener(document, window.opera ? 'keypress' : 'keydown', hs.keyHandler); - if (!hs.enableKeyListener) return true; - - if (e.preventDefault) e.preventDefault(); - else e.returnValue = false; - if (exp) { - if (op == 0) { - exp.close(); - } else if (op == 2) { - if (exp.slideshow) exp.slideshow.hitSpace(); - } else { - if (exp.slideshow) exp.slideshow.pause(); - hs.previousOrNext(exp.key, op); - } - return false; - } - } - return true; -}, - - -registerOverlay : function (overlay) { - hs.push(hs.overlays, hs.extend(overlay, { hsId: 'hsId'+ hs.idCounter++ } )); -}, - - -addSlideshow : function (options) { - var sg = options.slideshowGroup; - if (typeof sg == 'object') { - for (var i = 0; i < sg.length; i++) { - var o = {}; - for (var x in options) o[x] = options[x]; - o.slideshowGroup = sg[i]; - hs.push(hs.slideshows, o); - } - } else { - hs.push(hs.slideshows, options); - } -}, - -getWrapperKey : function (element, expOnly) { - var el, re = /^highslide-wrapper-([0-9]+)$/; - // 1. look in open expanders - el = element; - while (el.parentNode) { - if (el.hsKey !== undefined) return el.hsKey; - if (el.id && re.test(el.id)) return el.id.replace(re, "$1"); - el = el.parentNode; - } - // 2. look in thumbnail - if (!expOnly) { - el = element; - while (el.parentNode) { - if (el.tagName && hs.isHsAnchor(el)) { - for (var key = 0; key < hs.expanders.length; key++) { - var exp = hs.expanders[key]; - if (exp && exp.a == el) return key; - } - } - el = el.parentNode; - } - } - return null; -}, - -getExpander : function (el, expOnly) { - if (typeof el == 'undefined') return hs.expanders[hs.focusKey] || null; - if (typeof el == 'number') return hs.expanders[el] || null; - if (typeof el == 'string') el = hs.$(el); - return hs.expanders[hs.getWrapperKey(el, expOnly)] || null; -}, - -isHsAnchor : function (a) { - return (a.onclick && a.onclick.toString().replace(/\s/g, ' ').match(/hs.(htmlE|e)xpand/)); -}, - -reOrder : function () { - for (var i = 0; i < hs.expanders.length; i++) - if (hs.expanders[i] && hs.expanders[i].isExpanded) hs.focusTopmost(); -}, -fireEvent : function (obj, evt, args) { - return obj && obj[evt] ? (obj[evt](obj, args) !== false) : true; -}, - -mouseClickHandler : function(e) -{ - if (!e) e = window.event; - if (e.button > 1) return true; - if (!e.target) e.target = e.srcElement; - - var el = e.target; - while (el.parentNode - && !(/highslide-(image|move|html|resize)/.test(el.className))) - { - el = el.parentNode; - } - var exp = hs.getExpander(el); - if (exp && (exp.isClosing || !exp.isExpanded)) return true; - - if (exp && e.type == 'mousedown') { - if (e.target.form) return true; - var match = el.className.match(/highslide-(image|move|resize)/); - if (match) { - hs.dragArgs = { - exp: exp , - type: match[1], - left: exp.x.pos, - width: exp.x.size, - top: exp.y.pos, - height: exp.y.size, - clickX: e.clientX, - clickY: e.clientY - }; - - - hs.addEventListener(document, 'mousemove', hs.dragHandler); - if (e.preventDefault) e.preventDefault(); // FF - - if (/highslide-(image|html)-blur/.test(exp.content.className)) { - exp.focus(); - hs.hasFocused = true; - } - return false; - } - } else if (e.type == 'mouseup') { - - hs.removeEventListener(document, 'mousemove', hs.dragHandler); - - if (hs.dragArgs) { - if (hs.styleRestoreCursor && hs.dragArgs.type == 'image') - hs.dragArgs.exp.content.style.cursor = hs.styleRestoreCursor; - var hasDragged = hs.dragArgs.hasDragged; - - if (!hasDragged &&!hs.hasFocused && !/(move|resize)/.test(hs.dragArgs.type)) { - if (hs.fireEvent(exp, 'onImageClick')) - exp.close(); - } - else if (hasDragged || (!hasDragged && hs.hasHtmlExpanders)) { - hs.dragArgs.exp.doShowHide('hidden'); - } - - if (hasDragged) hs.fireEvent(hs.dragArgs.exp, 'onDrop', hs.dragArgs); - hs.hasFocused = false; - hs.dragArgs = null; - - } else if (/highslide-image-blur/.test(el.className)) { - el.style.cursor = hs.styleRestoreCursor; - } - } - return false; -}, - -dragHandler : function(e) -{ - if (!hs.dragArgs) return true; - if (!e) e = window.event; - var a = hs.dragArgs, exp = a.exp; - - a.dX = e.clientX - a.clickX; - a.dY = e.clientY - a.clickY; - - var distance = Math.sqrt(Math.pow(a.dX, 2) + Math.pow(a.dY, 2)); - if (!a.hasDragged) a.hasDragged = (a.type != 'image' && distance > 0) - || (distance > (hs.dragSensitivity || 5)); - - if (a.hasDragged && e.clientX > 5 && e.clientY > 5) { - if (!hs.fireEvent(exp, 'onDrag', a)) return false; - - if (a.type == 'resize') exp.resize(a); - else { - exp.moveTo(a.left + a.dX, a.top + a.dY); - if (a.type == 'image') exp.content.style.cursor = 'move'; - } - } - return false; -}, - -wrapperMouseHandler : function (e) { - try { - if (!e) e = window.event; - var over = /mouseover/i.test(e.type); - if (!e.target) e.target = e.srcElement; // ie - if (!e.relatedTarget) e.relatedTarget = - over ? e.fromElement : e.toElement; // ie - var exp = hs.getExpander(e.target); - if (!exp.isExpanded) return; - if (!exp || !e.relatedTarget || hs.getExpander(e.relatedTarget, true) == exp - || hs.dragArgs) return; - hs.fireEvent(exp, over ? 'onMouseOver' : 'onMouseOut', e); - for (var i = 0; i < exp.overlays.length; i++) (function() { - var o = hs.$('hsId'+ exp.overlays[i]); - if (o && o.hideOnMouseOut) { - if (over) hs.setStyles(o, { visibility: 'visible', display: '' }); - hs.animate(o, { opacity: over ? o.opacity : 0 }, o.dur); - } - })(); - } catch (e) {} -}, -addEventListener : function (el, event, func) { - if (el == document && event == 'ready') { - hs.push(hs.onReady, func); - } - try { - el.addEventListener(event, func, false); - } catch (e) { - try { - el.detachEvent('on'+ event, func); - el.attachEvent('on'+ event, func); - } catch (e) { - el['on'+ event] = func; - } - } -}, - -removeEventListener : function (el, event, func) { - try { - el.removeEventListener(event, func, false); - } catch (e) { - try { - el.detachEvent('on'+ event, func); - } catch (e) { - el['on'+ event] = null; - } - } -}, - -preloadFullImage : function (i) { - if (hs.continuePreloading && hs.preloadTheseImages[i] && hs.preloadTheseImages[i] != 'undefined') { - var img = document.createElement('img'); - img.onload = function() { - img = null; - hs.preloadFullImage(i + 1); - }; - img.src = hs.preloadTheseImages[i]; - } -}, -preloadImages : function (number) { - if (number && typeof number != 'object') hs.numberOfImagesToPreload = number; - - var arr = hs.getAnchors(); - for (var i = 0; i < arr.images.length && i < hs.numberOfImagesToPreload; i++) { - hs.push(hs.preloadTheseImages, hs.getSrc(arr.images[i])); - } - - // preload outlines - if (hs.outlineType) new hs.Outline(hs.outlineType, function () { hs.preloadFullImage(0)} ); - else - - hs.preloadFullImage(0); - - // preload cursor - if (hs.restoreCursor) var cur = hs.createElement('img', { src: hs.graphicsDir + hs.restoreCursor }); -}, - - -init : function () { - if (!hs.container) { - - hs.ieLt7 = hs.ie && hs.uaVersion < 7; - hs.ieLt9 = hs.ie && hs.uaVersion < 9; - - hs.getPageSize(); - for (var x in hs.langDefaults) { - if (typeof hs[x] != 'undefined') hs.lang[x] = hs[x]; - else if (typeof hs.lang[x] == 'undefined' && typeof hs.langDefaults[x] != 'undefined') - hs.lang[x] = hs.langDefaults[x]; - } - - hs.container = hs.createElement('div', { - className: 'highslide-container' - }, { - position: 'absolute', - left: 0, - top: 0, - width: '100%', - zIndex: hs.zIndexCounter, - direction: 'ltr' - }, - document.body, - true - ); - hs.loading = hs.createElement('a', { - className: 'highslide-loading', - title: hs.lang.loadingTitle, - innerHTML: hs.lang.loadingText, - href: 'javascript:;' - }, { - position: 'absolute', - top: '-9999px', - opacity: hs.loadingOpacity, - zIndex: 1 - }, hs.container - ); - hs.garbageBin = hs.createElement('div', null, { display: 'none' }, hs.container); - hs.viewport = hs.createElement('div', { - className: 'highslide-viewport highslide-viewport-size' - }, { - visibility: (hs.safari && hs.uaVersion < 525) ? 'visible' : 'hidden' - }, hs.container, 1 - ); - - // http://www.robertpenner.com/easing/ - Math.linearTween = function (t, b, c, d) { - return c*t/d + b; - }; - Math.easeInQuad = function (t, b, c, d) { - return c*(t/=d)*t + b; - }; - Math.easeOutQuad = function (t, b, c, d) { - return -c *(t/=d)*(t-2) + b; - }; - - hs.hideSelects = hs.ieLt7; - hs.hideIframes = ((window.opera && hs.uaVersion < 9) || navigator.vendor == 'KDE' - || (hs.ieLt7 && hs.uaVersion < 5.5)); - hs.fireEvent(this, 'onActivate'); - } -}, -ready : function() { - if (hs.isReady) return; - hs.isReady = true; - for (var i = 0; i < hs.onReady.length; i++) hs.onReady[i](); -}, - -updateAnchors : function() { - var el, els, all = [], images = [],groups = {}, re; - - for (var i = 0; i < hs.openerTagNames.length; i++) { - els = document.getElementsByTagName(hs.openerTagNames[i]); - for (var j = 0; j < els.length; j++) { - el = els[j]; - re = hs.isHsAnchor(el); - if (re) { - hs.push(all, el); - if (re[0] == 'hs.expand') hs.push(images, el); - var g = hs.getParam(el, 'slideshowGroup') || 'none'; - if (!groups[g]) groups[g] = []; - hs.push(groups[g], el); - } - } - } - hs.anchors = { all: all, groups: groups, images: images }; - return hs.anchors; - -}, - -getAnchors : function() { - return hs.anchors || hs.updateAnchors(); -}, - - -close : function(el) { - var exp = hs.getExpander(el); - if (exp) exp.close(); - return false; -} -}; // end hs object -hs.fx = function( elem, options, prop ){ - this.options = options; - this.elem = elem; - this.prop = prop; - - if (!options.orig) options.orig = {}; -}; -hs.fx.prototype = { - update: function(){ - (hs.fx.step[this.prop] || hs.fx.step._default)(this); - - if (this.options.step) - this.options.step.call(this.elem, this.now, this); - - }, - custom: function(from, to, unit){ - this.startTime = (new Date()).getTime(); - this.start = from; - this.end = to; - this.unit = unit;// || this.unit || "px"; - this.now = this.start; - this.pos = this.state = 0; - - var self = this; - function t(gotoEnd){ - return self.step(gotoEnd); - } - - t.elem = this.elem; - - if ( t() && hs.timers.push(t) == 1 ) { - hs.timerId = setInterval(function(){ - var timers = hs.timers; - - for ( var i = 0; i < timers.length; i++ ) - if ( !timers[i]() ) - timers.splice(i--, 1); - - if ( !timers.length ) { - clearInterval(hs.timerId); - } - }, 13); - } - }, - step: function(gotoEnd){ - var t = (new Date()).getTime(); - if ( gotoEnd || t >= this.options.duration + this.startTime ) { - this.now = this.end; - this.pos = this.state = 1; - this.update(); - - this.options.curAnim[ this.prop ] = true; - - var done = true; - for ( var i in this.options.curAnim ) - if ( this.options.curAnim[i] !== true ) - done = false; - - if ( done ) { - if (this.options.complete) this.options.complete.call(this.elem); - } - return false; - } else { - var n = t - this.startTime; - this.state = n / this.options.duration; - this.pos = this.options.easing(n, 0, 1, this.options.duration); - this.now = this.start + ((this.end - this.start) * this.pos); - this.update(); - } - return true; - } - -}; - -hs.extend( hs.fx, { - step: { - - opacity: function(fx){ - hs.setStyles(fx.elem, { opacity: fx.now }); - }, - - _default: function(fx){ - try { - if ( fx.elem.style && fx.elem.style[ fx.prop ] != null ) - fx.elem.style[ fx.prop ] = fx.now + fx.unit; - else - fx.elem[ fx.prop ] = fx.now; - } catch (e) {} - } - } -}); - -hs.Outline = function (outlineType, onLoad) { - this.onLoad = onLoad; - this.outlineType = outlineType; - var v = hs.uaVersion, tr; - - this.hasAlphaImageLoader = hs.ie && hs.uaVersion < 7; - if (!outlineType) { - if (onLoad) onLoad(); - return; - } - - hs.init(); - this.table = hs.createElement( - 'table', { - cellSpacing: 0 - }, { - visibility: 'hidden', - position: 'absolute', - borderCollapse: 'collapse', - width: 0 - }, - hs.container, - true - ); - var tbody = hs.createElement('tbody', null, null, this.table, 1); - - this.td = []; - for (var i = 0; i <= 8; i++) { - if (i % 3 == 0) tr = hs.createElement('tr', null, { height: 'auto' }, tbody, true); - this.td[i] = hs.createElement('td', null, null, tr, true); - var style = i != 4 ? { lineHeight: 0, fontSize: 0} : { position : 'relative' }; - hs.setStyles(this.td[i], style); - } - this.td[4].className = outlineType +' highslide-outline'; - - this.preloadGraphic(); -}; - -hs.Outline.prototype = { -preloadGraphic : function () { - var src = hs.graphicsDir + (hs.outlinesDir || "outlines/")+ this.outlineType +".png"; - - var appendTo = hs.safari && hs.uaVersion < 525 ? hs.container : null; - this.graphic = hs.createElement('img', null, { position: 'absolute', - top: '-9999px' }, appendTo, true); // for onload trigger - - var pThis = this; - this.graphic.onload = function() { pThis.onGraphicLoad(); }; - - this.graphic.src = src; -}, - -onGraphicLoad : function () { - var o = this.offset = this.graphic.width / 4, - pos = [[0,0],[0,-4],[-2,0],[0,-8],0,[-2,-8],[0,-2],[0,-6],[-2,-2]], - dim = { height: (2*o) +'px', width: (2*o) +'px' }; - for (var i = 0; i <= 8; i++) { - if (pos[i]) { - if (this.hasAlphaImageLoader) { - var w = (i == 1 || i == 7) ? '100%' : this.graphic.width +'px'; - var div = hs.createElement('div', null, { width: '100%', height: '100%', position: 'relative', overflow: 'hidden'}, this.td[i], true); - hs.createElement ('div', null, { - filter: "progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=scale, src='"+ this.graphic.src + "')", - position: 'absolute', - width: w, - height: this.graphic.height +'px', - left: (pos[i][0]*o)+'px', - top: (pos[i][1]*o)+'px' - }, - div, - true); - } else { - hs.setStyles(this.td[i], { background: 'url('+ this.graphic.src +') '+ (pos[i][0]*o)+'px '+(pos[i][1]*o)+'px'}); - } - - if (window.opera && (i == 3 || i ==5)) - hs.createElement('div', null, dim, this.td[i], true); - - hs.setStyles (this.td[i], dim); - } - } - this.graphic = null; - if (hs.pendingOutlines[this.outlineType]) hs.pendingOutlines[this.outlineType].destroy(); - hs.pendingOutlines[this.outlineType] = this; - if (this.onLoad) this.onLoad(); -}, - -setPosition : function (pos, offset, vis, dur, easing) { - var exp = this.exp, - stl = exp.wrapper.style, - offset = offset || 0, - pos = pos || { - x: exp.x.pos + offset, - y: exp.y.pos + offset, - w: exp.x.get('wsize') - 2 * offset, - h: exp.y.get('wsize') - 2 * offset - }; - if (vis) this.table.style.visibility = (pos.h >= 4 * this.offset) - ? 'visible' : 'hidden'; - hs.setStyles(this.table, { - left: (pos.x - this.offset) +'px', - top: (pos.y - this.offset) +'px', - width: (pos.w + 2 * this.offset) +'px' - }); - - pos.w -= 2 * this.offset; - pos.h -= 2 * this.offset; - hs.setStyles (this.td[4], { - width: pos.w >= 0 ? pos.w +'px' : 0, - height: pos.h >= 0 ? pos.h +'px' : 0 - }); - if (this.hasAlphaImageLoader) this.td[3].style.height - = this.td[5].style.height = this.td[4].style.height; - -}, - -destroy : function(hide) { - if (hide) this.table.style.visibility = 'hidden'; - else hs.discardElement(this.table); -} -}; - -hs.Dimension = function(exp, dim) { - this.exp = exp; - this.dim = dim; - this.ucwh = dim == 'x' ? 'Width' : 'Height'; - this.wh = this.ucwh.toLowerCase(); - this.uclt = dim == 'x' ? 'Left' : 'Top'; - this.lt = this.uclt.toLowerCase(); - this.ucrb = dim == 'x' ? 'Right' : 'Bottom'; - this.rb = this.ucrb.toLowerCase(); - this.p1 = this.p2 = 0; -}; -hs.Dimension.prototype = { -get : function(key) { - switch (key) { - case 'loadingPos': - return this.tpos + this.tb + (this.t - hs.loading['offset'+ this.ucwh]) / 2; - case 'loadingPosXfade': - return this.pos + this.cb+ this.p1 + (this.size - hs.loading['offset'+ this.ucwh]) / 2; - case 'wsize': - return this.size + 2 * this.cb + this.p1 + this.p2; - case 'fitsize': - return this.clientSize - this.marginMin - this.marginMax; - case 'maxsize': - return this.get('fitsize') - 2 * this.cb - this.p1 - this.p2 ; - case 'opos': - return this.pos - (this.exp.outline ? this.exp.outline.offset : 0); - case 'osize': - return this.get('wsize') + (this.exp.outline ? 2*this.exp.outline.offset : 0); - case 'imgPad': - return this.imgSize ? Math.round((this.size - this.imgSize) / 2) : 0; - - } -}, -calcBorders: function() { - // correct for borders - this.cb = (this.exp.content['offset'+ this.ucwh] - this.t) / 2; - - this.marginMax = hs['margin'+ this.ucrb]; -}, -calcThumb: function() { - this.t = this.exp.el[this.wh] ? parseInt(this.exp.el[this.wh]) : - this.exp.el['offset'+ this.ucwh]; - this.tpos = this.exp.tpos[this.dim]; - this.tb = (this.exp.el['offset'+ this.ucwh] - this.t) / 2; - if (this.tpos == 0 || this.tpos == -1) { - this.tpos = (hs.page[this.wh] / 2) + hs.page['scroll'+ this.uclt]; - }; -}, -calcExpanded: function() { - var exp = this.exp; - this.justify = 'auto'; - - // get alignment - if (exp.align == 'center') this.justify = 'center'; - else if (new RegExp(this.lt).test(exp.anchor)) this.justify = null; - else if (new RegExp(this.rb).test(exp.anchor)) this.justify = 'max'; - - - // size and position - this.pos = this.tpos - this.cb + this.tb; - - if (this.maxHeight && this.dim == 'x') - exp.maxWidth = Math.min(exp.maxWidth || this.full, exp.maxHeight * this.full / exp.y.full); - - this.size = Math.min(this.full, exp['max'+ this.ucwh] || this.full); - this.minSize = exp.allowSizeReduction ? - Math.min(exp['min'+ this.ucwh], this.full) :this.full; - if (exp.isImage && exp.useBox) { - this.size = exp[this.wh]; - this.imgSize = this.full; - } - if (this.dim == 'x' && hs.padToMinWidth) this.minSize = exp.minWidth; - this.target = exp['target'+ this.dim.toUpperCase()]; - this.marginMin = hs['margin'+ this.uclt]; - this.scroll = hs.page['scroll'+ this.uclt]; - this.clientSize = hs.page[this.wh]; -}, -setSize: function(i) { - var exp = this.exp; - if (exp.isImage && (exp.useBox || hs.padToMinWidth)) { - this.imgSize = i; - this.size = Math.max(this.size, this.imgSize); - exp.content.style[this.lt] = this.get('imgPad')+'px'; - } else - this.size = i; - - exp.content.style[this.wh] = i +'px'; - exp.wrapper.style[this.wh] = this.get('wsize') +'px'; - if (exp.outline) exp.outline.setPosition(); - if (this.dim == 'x' && exp.overlayBox) exp.sizeOverlayBox(true); - if (this.dim == 'x' && exp.slideshow && exp.isImage) { - if (i == this.full) exp.slideshow.disable('full-expand'); - else exp.slideshow.enable('full-expand'); - } -}, -setPos: function(i) { - this.pos = i; - this.exp.wrapper.style[this.lt] = i +'px'; - - if (this.exp.outline) this.exp.outline.setPosition(); - -} -}; - -hs.Expander = function(a, params, custom, contentType) { - if (document.readyState && hs.ie && !hs.isReady) { - hs.addEventListener(document, 'ready', function() { - new hs.Expander(a, params, custom, contentType); - }); - return; - } - this.a = a; - this.custom = custom; - this.contentType = contentType || 'image'; - this.isImage = !this.isHtml; - - hs.continuePreloading = false; - this.overlays = []; - this.last = hs.last; - hs.last = null; - hs.init(); - var key = this.key = hs.expanders.length; - // override inline parameters - for (var i = 0; i < hs.overrides.length; i++) { - var name = hs.overrides[i]; - this[name] = params && typeof params[name] != 'undefined' ? - params[name] : hs[name]; - } - if (!this.src) this.src = a.href; - - // get thumb - var el = (params && params.thumbnailId) ? hs.$(params.thumbnailId) : a; - el = this.thumb = el.getElementsByTagName('img')[0] || el; - this.thumbsUserSetId = el.id || a.id; - if (!hs.fireEvent(this, 'onInit')) return true; - - // check if already open - for (var i = 0; i < hs.expanders.length; i++) { - if (hs.expanders[i] && hs.expanders[i].a == a - && !(this.last && this.transitions[1] == 'crossfade')) { - hs.expanders[i].focus(); - return false; - } - } - - // cancel other - if (!hs.allowSimultaneousLoading) for (var i = 0; i < hs.expanders.length; i++) { - if (hs.expanders[i] && hs.expanders[i].thumb != el && !hs.expanders[i].onLoadStarted) { - hs.expanders[i].cancelLoading(); - } - } - hs.expanders[key] = this; - if (!hs.allowMultipleInstances && !hs.upcoming) { - if (hs.expanders[key-1]) hs.expanders[key-1].close(); - if (typeof hs.focusKey != 'undefined' && hs.expanders[hs.focusKey]) - hs.expanders[hs.focusKey].close(); - } - - // initiate metrics - this.el = el; - this.tpos = this.pageOrigin || hs.getPosition(el); - hs.getPageSize(); - var x = this.x = new hs.Dimension(this, 'x'); - x.calcThumb(); - var y = this.y = new hs.Dimension(this, 'y'); - y.calcThumb(); - this.wrapper = hs.createElement( - 'div', { - id: 'highslide-wrapper-'+ this.key, - className: 'highslide-wrapper '+ this.wrapperClassName - }, { - visibility: 'hidden', - position: 'absolute', - zIndex: hs.zIndexCounter += 2 - }, null, true ); - - this.wrapper.onmouseover = this.wrapper.onmouseout = hs.wrapperMouseHandler; - if (this.contentType == 'image' && this.outlineWhileAnimating == 2) - this.outlineWhileAnimating = 0; - - // get the outline - if (!this.outlineType - || (this.last && this.isImage && this.transitions[1] == 'crossfade')) { - this[this.contentType +'Create'](); - - } else if (hs.pendingOutlines[this.outlineType]) { - this.connectOutline(); - this[this.contentType +'Create'](); - - } else { - this.showLoading(); - var exp = this; - new hs.Outline(this.outlineType, - function () { - exp.connectOutline(); - exp[exp.contentType +'Create'](); - } - ); - } - return true; -}; - -hs.Expander.prototype = { -error : function(e) { - if (hs.debug) alert ('Line '+ e.lineNumber +': '+ e.message); - else window.location.href = this.src; -}, - -connectOutline : function() { - var outline = this.outline = hs.pendingOutlines[this.outlineType]; - outline.exp = this; - outline.table.style.zIndex = this.wrapper.style.zIndex - 1; - hs.pendingOutlines[this.outlineType] = null; -}, - -showLoading : function() { - if (this.onLoadStarted || this.loading) return; - - this.loading = hs.loading; - var exp = this; - this.loading.onclick = function() { - exp.cancelLoading(); - }; - - - if (!hs.fireEvent(this, 'onShowLoading')) return; - var exp = this, - l = this.x.get('loadingPos') +'px', - t = this.y.get('loadingPos') +'px'; - if (!tgt && this.last && this.transitions[1] == 'crossfade') - var tgt = this.last; - if (tgt) { - l = tgt.x.get('loadingPosXfade') +'px'; - t = tgt.y.get('loadingPosXfade') +'px'; - this.loading.style.zIndex = hs.zIndexCounter++; - } - setTimeout(function () { - if (exp.loading) hs.setStyles(exp.loading, { left: l, top: t, zIndex: hs.zIndexCounter++ })} - , 100); -}, - -imageCreate : function() { - var exp = this; - - var img = document.createElement('img'); - this.content = img; - img.onload = function () { - if (hs.expanders[exp.key]) exp.contentLoaded(); - }; - if (hs.blockRightClick) img.oncontextmenu = function() { return false; }; - img.className = 'highslide-image'; - hs.setStyles(img, { - visibility: 'hidden', - display: 'block', - position: 'absolute', - maxWidth: '9999px', - zIndex: 3 - }); - img.title = hs.lang.restoreTitle; - if (hs.safari && hs.uaVersion < 525) hs.container.appendChild(img); - if (hs.ie && hs.flushImgSize) img.src = null; - img.src = this.src; - - this.showLoading(); -}, - -contentLoaded : function() { - try { - if (!this.content) return; - this.content.onload = null; - if (this.onLoadStarted) return; - else this.onLoadStarted = true; - - var x = this.x, y = this.y; - - if (this.loading) { - hs.setStyles(this.loading, { top: '-9999px' }); - this.loading = null; - hs.fireEvent(this, 'onHideLoading'); - } - x.full = this.content.width; - y.full = this.content.height; - - hs.setStyles(this.content, { - width: x.t +'px', - height: y.t +'px' - }); - this.wrapper.appendChild(this.content); - hs.container.appendChild(this.wrapper); - - x.calcBorders(); - y.calcBorders(); - - hs.setStyles (this.wrapper, { - left: (x.tpos + x.tb - x.cb) +'px', - top: (y.tpos + x.tb - y.cb) +'px' - }); - - - this.initSlideshow(); - this.getOverlays(); - - var ratio = x.full / y.full; - x.calcExpanded(); - this.justify(x); - - y.calcExpanded(); - this.justify(y); - if (this.overlayBox) this.sizeOverlayBox(0, 1); - - - if (this.allowSizeReduction) { - this.correctRatio(ratio); - var ss = this.slideshow; - if (ss && this.last && ss.controls && ss.fixedControls) { - var pos = ss.overlayOptions.position || '', p; - for (var dim in hs.oPos) for (var i = 0; i < 5; i++) { - p = this[dim]; - if (pos.match(hs.oPos[dim][i])) { - p.pos = this.last[dim].pos - + (this.last[dim].p1 - p.p1) - + (this.last[dim].size - p.size) * [0, 0, .5, 1, 1][i]; - if (ss.fixedControls == 'fit') { - if (p.pos + p.size + p.p1 + p.p2 > p.scroll + p.clientSize - p.marginMax) - p.pos = p.scroll + p.clientSize - p.size - p.marginMin - p.marginMax - p.p1 - p.p2; - if (p.pos < p.scroll + p.marginMin) p.pos = p.scroll + p.marginMin; - } - } - } - } - if (this.isImage && this.x.full > (this.x.imgSize || this.x.size)) { - this.createFullExpand(); - if (this.overlays.length == 1) this.sizeOverlayBox(); - } - } - this.show(); - - } catch (e) { - this.error(e); - } -}, - -justify : function (p, moveOnly) { - var tgtArr, tgt = p.target, dim = p == this.x ? 'x' : 'y'; - - if (tgt && tgt.match(/ /)) { - tgtArr = tgt.split(' '); - tgt = tgtArr[0]; - } - if (tgt && hs.$(tgt)) { - p.pos = hs.getPosition(hs.$(tgt))[dim]; - if (tgtArr && tgtArr[1] && tgtArr[1].match(/^[-]?[0-9]+px$/)) - p.pos += parseInt(tgtArr[1]); - if (p.size < p.minSize) p.size = p.minSize; - - } else if (p.justify == 'auto' || p.justify == 'center') { - - var hasMovedMin = false; - - var allowReduce = p.exp.allowSizeReduction; - if (p.justify == 'center') - p.pos = Math.round(p.scroll + (p.clientSize + p.marginMin - p.marginMax - p.get('wsize')) / 2); - else - p.pos = Math.round(p.pos - ((p.get('wsize') - p.t) / 2)); - if (p.pos < p.scroll + p.marginMin) { - p.pos = p.scroll + p.marginMin; - hasMovedMin = true; - } - if (!moveOnly && p.size < p.minSize) { - p.size = p.minSize; - allowReduce = false; - } - if (p.pos + p.get('wsize') > p.scroll + p.clientSize - p.marginMax) { - if (!moveOnly && hasMovedMin && allowReduce) { - p.size = Math.min(p.size, p.get(dim == 'y' ? 'fitsize' : 'maxsize')); - } else if (p.get('wsize') < p.get('fitsize')) { - p.pos = p.scroll + p.clientSize - p.marginMax - p.get('wsize'); - } else { // image larger than viewport - p.pos = p.scroll + p.marginMin; - if (!moveOnly && allowReduce) p.size = p.get(dim == 'y' ? 'fitsize' : 'maxsize'); - } - } - - if (!moveOnly && p.size < p.minSize) { - p.size = p.minSize; - allowReduce = false; - } - - - } else if (p.justify == 'max') { - p.pos = Math.floor(p.pos - p.size + p.t); - } - - - if (p.pos < p.marginMin) { - var tmpMin = p.pos; - p.pos = p.marginMin; - - if (allowReduce && !moveOnly) p.size = p.size - (p.pos - tmpMin); - - } -}, - -correctRatio : function(ratio) { - var x = this.x, - y = this.y, - changed = false, - xSize = Math.min(x.full, x.size), - ySize = Math.min(y.full, y.size), - useBox = (this.useBox || hs.padToMinWidth); - - if (xSize / ySize > ratio) { // width greater - xSize = ySize * ratio; - if (xSize < x.minSize) { // below minWidth - xSize = x.minSize; - ySize = xSize / ratio; - } - changed = true; - - } else if (xSize / ySize < ratio) { // height greater - ySize = xSize / ratio; - changed = true; - } - - if (hs.padToMinWidth && x.full < x.minSize) { - x.imgSize = x.full; - y.size = y.imgSize = y.full; - } else if (this.useBox) { - x.imgSize = xSize; - y.imgSize = ySize; - } else { - x.size = xSize; - y.size = ySize; - } - changed = this.fitOverlayBox(this.useBox ? null : ratio, changed); - if (useBox && y.size < y.imgSize) { - y.imgSize = y.size; - x.imgSize = y.size * ratio; - } - if (changed || useBox) { - x.pos = x.tpos - x.cb + x.tb; - x.minSize = x.size; - this.justify(x, true); - - y.pos = y.tpos - y.cb + y.tb; - y.minSize = y.size; - this.justify(y, true); - if (this.overlayBox) this.sizeOverlayBox(); - } - - -}, -fitOverlayBox : function(ratio, changed) { - var x = this.x, y = this.y; - if (this.overlayBox) { - while (y.size > this.minHeight && x.size > this.minWidth - && y.get('wsize') > y.get('fitsize')) { - y.size -= 10; - if (ratio) x.size = y.size * ratio; - this.sizeOverlayBox(0, 1); - changed = true; - } - } - return changed; -}, - -show : function () { - var x = this.x, y = this.y; - this.doShowHide('hidden'); - hs.fireEvent(this, 'onBeforeExpand'); - if (this.slideshow && this.slideshow.thumbstrip) this.slideshow.thumbstrip.selectThumb(); - - // Apply size change - this.changeSize( - 1, { - wrapper: { - width : x.get('wsize'), - height : y.get('wsize'), - left: x.pos, - top: y.pos - }, - content: { - left: x.p1 + x.get('imgPad'), - top: y.p1 + y.get('imgPad'), - width:x.imgSize ||x.size, - height:y.imgSize ||y.size - } - }, - hs.expandDuration - ); -}, - -changeSize : function(up, to, dur) { - // transition - var trans = this.transitions, - other = up ? (this.last ? this.last.a : null) : hs.upcoming, - t = (trans[1] && other - && hs.getParam(other, 'transitions')[1] == trans[1]) ? - trans[1] : trans[0]; - - if (this[t] && t != 'expand') { - this[t](up, to); - return; - } - - if (this.outline && !this.outlineWhileAnimating) { - if (up) this.outline.setPosition(); - else this.outline.destroy(); - } - - - if (!up) this.destroyOverlays(); - - var exp = this, - x = exp.x, - y = exp.y, - easing = this.easing; - if (!up) easing = this.easingClose || easing; - var after = up ? - function() { - - if (exp.outline) exp.outline.table.style.visibility = "visible"; - setTimeout(function() { - exp.afterExpand(); - }, 50); - } : - function() { - exp.afterClose(); - }; - if (up) hs.setStyles( this.wrapper, { - width: x.t +'px', - height: y.t +'px' - }); - if (this.fadeInOut) { - hs.setStyles(this.wrapper, { opacity: up ? 0 : 1 }); - hs.extend(to.wrapper, { opacity: up }); - } - hs.animate( this.wrapper, to.wrapper, { - duration: dur, - easing: easing, - step: function(val, args) { - if (exp.outline && exp.outlineWhileAnimating && args.prop == 'top') { - var fac = up ? args.pos : 1 - args.pos; - var pos = { - w: x.t + (x.get('wsize') - x.t) * fac, - h: y.t + (y.get('wsize') - y.t) * fac, - x: x.tpos + (x.pos - x.tpos) * fac, - y: y.tpos + (y.pos - y.tpos) * fac - }; - exp.outline.setPosition(pos, 0, 1); - } - } - }); - hs.animate( this.content, to.content, dur, easing, after); - if (up) { - this.wrapper.style.visibility = 'visible'; - this.content.style.visibility = 'visible'; - this.a.className += ' highslide-active-anchor'; - } -}, - - - -fade : function(up, to) { - this.outlineWhileAnimating = false; - var exp = this, t = up ? hs.expandDuration : 0; - - if (up) { - hs.animate(this.wrapper, to.wrapper, 0); - hs.setStyles(this.wrapper, { opacity: 0, visibility: 'visible' }); - hs.animate(this.content, to.content, 0); - this.content.style.visibility = 'visible'; - - hs.animate(this.wrapper, { opacity: 1 }, t, null, - function() { exp.afterExpand(); }); - } - - if (this.outline) { - this.outline.table.style.zIndex = this.wrapper.style.zIndex; - var dir = up || -1, - offset = this.outline.offset, - startOff = up ? 3 : offset, - endOff = up? offset : 3; - for (var i = startOff; dir * i <= dir * endOff; i += dir, t += 25) { - (function() { - var o = up ? endOff - i : startOff - i; - setTimeout(function() { - exp.outline.setPosition(0, o, 1); - }, t); - })(); - } - } - - - if (up) {}//setTimeout(function() { exp.afterExpand(); }, t+50); - else { - setTimeout( function() { - if (exp.outline) exp.outline.destroy(exp.preserveContent); - - exp.destroyOverlays(); - - hs.animate( exp.wrapper, { opacity: 0 }, hs.restoreDuration, null, function(){ - exp.afterClose(); - }); - }, t); - } -}, -crossfade : function (up, to, from) { - if (!up) return; - var exp = this, - last = this.last, - x = this.x, - y = this.y, - lastX = last.x, - lastY = last.y, - wrapper = this.wrapper, - content = this.content, - overlayBox = this.overlayBox; - hs.removeEventListener(document, 'mousemove', hs.dragHandler); - - hs.setStyles(content, { - width: (x.imgSize || x.size) +'px', - height: (y.imgSize || y.size) +'px' - }); - if (overlayBox) overlayBox.style.overflow = 'visible'; - this.outline = last.outline; - if (this.outline) this.outline.exp = exp; - last.outline = null; - var fadeBox = hs.createElement('div', { - className: 'highslide-'+ this.contentType - }, { - position: 'absolute', - zIndex: 4, - overflow: 'hidden', - display: 'none' - } - ); - var names = { oldImg: last, newImg: this }; - for (var n in names) { - this[n] = names[n].content.cloneNode(1); - hs.setStyles(this[n], { - position: 'absolute', - border: 0, - visibility: 'visible' - }); - fadeBox.appendChild(this[n]); - } - wrapper.appendChild(fadeBox); - if (overlayBox) { - overlayBox.className = ''; - wrapper.appendChild(overlayBox); - } - fadeBox.style.display = ''; - last.content.style.display = 'none'; - - - if (hs.safari && hs.uaVersion < 525) { - this.wrapper.style.visibility = 'visible'; - } - hs.animate(wrapper, { - width: x.size - }, { - duration: hs.transitionDuration, - step: function(val, args) { - var pos = args.pos, - invPos = 1 - pos; - var prop, - size = {}, - props = ['pos', 'size', 'p1', 'p2']; - for (var n in props) { - prop = props[n]; - size['x'+ prop] = Math.round(invPos * lastX[prop] + pos * x[prop]); - size['y'+ prop] = Math.round(invPos * lastY[prop] + pos * y[prop]); - size.ximgSize = Math.round( - invPos * (lastX.imgSize || lastX.size) + pos * (x.imgSize || x.size)); - size.ximgPad = Math.round(invPos * lastX.get('imgPad') + pos * x.get('imgPad')); - size.yimgSize = Math.round( - invPos * (lastY.imgSize || lastY.size) + pos * (y.imgSize || y.size)); - size.yimgPad = Math.round(invPos * lastY.get('imgPad') + pos * y.get('imgPad')); - } - if (exp.outline) exp.outline.setPosition({ - x: size.xpos, - y: size.ypos, - w: size.xsize + size.xp1 + size.xp2 + 2 * x.cb, - h: size.ysize + size.yp1 + size.yp2 + 2 * y.cb - }); - last.wrapper.style.clip = 'rect(' - + (size.ypos - lastY.pos)+'px, ' - + (size.xsize + size.xp1 + size.xp2 + size.xpos + 2 * lastX.cb - lastX.pos) +'px, ' - + (size.ysize + size.yp1 + size.yp2 + size.ypos + 2 * lastY.cb - lastY.pos) +'px, ' - + (size.xpos - lastX.pos)+'px)'; - - hs.setStyles(content, { - top: (size.yp1 + y.get('imgPad')) +'px', - left: (size.xp1 + x.get('imgPad')) +'px', - marginTop: (y.pos - size.ypos) +'px', - marginLeft: (x.pos - size.xpos) +'px' - }); - hs.setStyles(wrapper, { - top: size.ypos +'px', - left: size.xpos +'px', - width: (size.xp1 + size.xp2 + size.xsize + 2 * x.cb)+ 'px', - height: (size.yp1 + size.yp2 + size.ysize + 2 * y.cb) + 'px' - }); - hs.setStyles(fadeBox, { - width: (size.ximgSize || size.xsize) + 'px', - height: (size.yimgSize || size.ysize) +'px', - left: (size.xp1 + size.ximgPad) +'px', - top: (size.yp1 + size.yimgPad) +'px', - visibility: 'visible' - }); - - hs.setStyles(exp.oldImg, { - top: (lastY.pos - size.ypos + lastY.p1 - size.yp1 + lastY.get('imgPad') - size.yimgPad)+'px', - left: (lastX.pos - size.xpos + lastX.p1 - size.xp1 + lastX.get('imgPad') - size.ximgPad)+'px' - }); - - hs.setStyles(exp.newImg, { - opacity: pos, - top: (y.pos - size.ypos + y.p1 - size.yp1 + y.get('imgPad') - size.yimgPad) +'px', - left: (x.pos - size.xpos + x.p1 - size.xp1 + x.get('imgPad') - size.ximgPad) +'px' - }); - if (overlayBox) hs.setStyles(overlayBox, { - width: size.xsize + 'px', - height: size.ysize +'px', - left: (size.xp1 + x.cb) +'px', - top: (size.yp1 + y.cb) +'px' - }); - }, - complete: function () { - wrapper.style.visibility = content.style.visibility = 'visible'; - content.style.display = 'block'; - hs.discardElement(fadeBox); - exp.afterExpand(); - last.afterClose(); - exp.last = null; - } - - }); -}, -reuseOverlay : function(o, el) { - if (!this.last) return false; - for (var i = 0; i < this.last.overlays.length; i++) { - var oDiv = hs.$('hsId'+ this.last.overlays[i]); - if (oDiv && oDiv.hsId == o.hsId) { - this.genOverlayBox(); - oDiv.reuse = this.key; - hs.push(this.overlays, this.last.overlays[i]); - return true; - } - } - return false; -}, - - -afterExpand : function() { - this.isExpanded = true; - this.focus(); - if (this.dimmingOpacity) hs.dim(this); - if (hs.upcoming && hs.upcoming == this.a) hs.upcoming = null; - this.prepareNextOutline(); - var p = hs.page, mX = hs.mouse.x + p.scrollLeft, mY = hs.mouse.y + p.scrollTop; - this.mouseIsOver = this.x.pos < mX && mX < this.x.pos + this.x.get('wsize') - && this.y.pos < mY && mY < this.y.pos + this.y.get('wsize'); - if (this.overlayBox) this.showOverlays(); - hs.fireEvent(this, 'onAfterExpand'); - -}, - - -prepareNextOutline : function() { - var key = this.key; - var outlineType = this.outlineType; - new hs.Outline(outlineType, - function () { try { hs.expanders[key].preloadNext(); } catch (e) {} }); -}, - - -preloadNext : function() { - var next = this.getAdjacentAnchor(1); - if (next && next.onclick.toString().match(/hs\.expand/)) - var img = hs.createElement('img', { src: hs.getSrc(next) }); -}, - - -getAdjacentAnchor : function(op) { - var current = this.getAnchorIndex(), as = hs.anchors.groups[this.slideshowGroup || 'none']; - if (as && !as[current + op] && this.slideshow && this.slideshow.repeat) { - if (op == 1) return as[0]; - else if (op == -1) return as[as.length-1]; - } - return (as && as[current + op]) || null; -}, - -getAnchorIndex : function() { - var arr = hs.getAnchors().groups[this.slideshowGroup || 'none']; - if (arr) for (var i = 0; i < arr.length; i++) { - if (arr[i] == this.a) return i; - } - return null; -}, - - -getNumber : function() { - if (this[this.numberPosition]) { - var arr = hs.anchors.groups[this.slideshowGroup || 'none']; - if (arr) { - var s = hs.lang.number.replace('%1', this.getAnchorIndex() + 1).replace('%2', arr.length); - this[this.numberPosition].innerHTML = - '
    '+ s +'
    '+ this[this.numberPosition].innerHTML; - } - } -}, -initSlideshow : function() { - if (!this.last) { - for (var i = 0; i < hs.slideshows.length; i++) { - var ss = hs.slideshows[i], sg = ss.slideshowGroup; - if (typeof sg == 'undefined' || sg === null || sg === this.slideshowGroup) - this.slideshow = new hs.Slideshow(this.key, ss); - } - } else { - this.slideshow = this.last.slideshow; - } - var ss = this.slideshow; - if (!ss) return; - var key = ss.expKey = this.key; - - ss.checkFirstAndLast(); - ss.disable('full-expand'); - if (ss.controls) { - this.createOverlay(hs.extend(ss.overlayOptions || {}, { - overlayId: ss.controls, - hsId: 'controls', - zIndex: 5 - })); - } - if (ss.thumbstrip) ss.thumbstrip.add(this); - if (!this.last && this.autoplay) ss.play(true); - if (ss.autoplay) { - ss.autoplay = setTimeout(function() { - hs.next(key); - }, (ss.interval || 500)); - } -}, - -cancelLoading : function() { - hs.discardElement (this.wrapper); - hs.expanders[this.key] = null; - if (hs.upcoming == this.a) hs.upcoming = null; - hs.undim(this.key); - if (this.loading) hs.loading.style.left = '-9999px'; - hs.fireEvent(this, 'onHideLoading'); -}, - -writeCredits : function () { - if (this.credits) return; - this.credits = hs.createElement('a', { - href: hs.creditsHref, - target: hs.creditsTarget, - className: 'highslide-credits', - innerHTML: hs.lang.creditsText, - title: hs.lang.creditsTitle - }); - this.createOverlay({ - overlayId: this.credits, - position: this.creditsPosition || 'top left', - hsId: 'credits' - }); -}, - -getInline : function(types, addOverlay) { - for (var i = 0; i < types.length; i++) { - var type = types[i], s = null; - if (type == 'caption' && !hs.fireEvent(this, 'onBeforeGetCaption')) return; - else if (type == 'heading' && !hs.fireEvent(this, 'onBeforeGetHeading')) return; - if (!this[type +'Id'] && this.thumbsUserSetId) - this[type +'Id'] = type +'-for-'+ this.thumbsUserSetId; - if (this[type +'Id']) this[type] = hs.getNode(this[type +'Id']); - if (!this[type] && !this[type +'Text'] && this[type +'Eval']) try { - s = eval(this[type +'Eval']); - } catch (e) {} - if (!this[type] && this[type +'Text']) { - s = this[type +'Text']; - } - if (!this[type] && !s) { - this[type] = hs.getNode(this.a['_'+ type + 'Id']); - if (!this[type]) { - var next = this.a.nextSibling; - while (next && !hs.isHsAnchor(next)) { - if ((new RegExp('highslide-'+ type)).test(next.className || null)) { - if (!next.id) this.a['_'+ type + 'Id'] = next.id = 'hsId'+ hs.idCounter++; - this[type] = hs.getNode(next.id); - break; - } - next = next.nextSibling; - } - } - } - if (!this[type] && !s && this.numberPosition == type) s = '\n'; - - if (!this[type] && s) this[type] = hs.createElement('div', - { className: 'highslide-'+ type, innerHTML: s } ); - - if (addOverlay && this[type]) { - var o = { position: (type == 'heading') ? 'above' : 'below' }; - for (var x in this[type+'Overlay']) o[x] = this[type+'Overlay'][x]; - o.overlayId = this[type]; - this.createOverlay(o); - } - } -}, - - -// on end move and resize -doShowHide : function(visibility) { - if (hs.hideSelects) this.showHideElements('SELECT', visibility); - if (hs.hideIframes) this.showHideElements('IFRAME', visibility); - if (hs.geckoMac) this.showHideElements('*', visibility); -}, -showHideElements : function (tagName, visibility) { - var els = document.getElementsByTagName(tagName); - var prop = tagName == '*' ? 'overflow' : 'visibility'; - for (var i = 0; i < els.length; i++) { - if (prop == 'visibility' || (document.defaultView.getComputedStyle( - els[i], "").getPropertyValue('overflow') == 'auto' - || els[i].getAttribute('hidden-by') != null)) { - var hiddenBy = els[i].getAttribute('hidden-by'); - if (visibility == 'visible' && hiddenBy) { - hiddenBy = hiddenBy.replace('['+ this.key +']', ''); - els[i].setAttribute('hidden-by', hiddenBy); - if (!hiddenBy) els[i].style[prop] = els[i].origProp; - } else if (visibility == 'hidden') { // hide if behind - var elPos = hs.getPosition(els[i]); - elPos.w = els[i].offsetWidth; - elPos.h = els[i].offsetHeight; - if (!this.dimmingOpacity) { // hide all if dimming - - var clearsX = (elPos.x + elPos.w < this.x.get('opos') - || elPos.x > this.x.get('opos') + this.x.get('osize')); - var clearsY = (elPos.y + elPos.h < this.y.get('opos') - || elPos.y > this.y.get('opos') + this.y.get('osize')); - } - var wrapperKey = hs.getWrapperKey(els[i]); - if (!clearsX && !clearsY && wrapperKey != this.key) { // element falls behind image - if (!hiddenBy) { - els[i].setAttribute('hidden-by', '['+ this.key +']'); - els[i].origProp = els[i].style[prop]; - els[i].style[prop] = 'hidden'; - - } else if (hiddenBy.indexOf('['+ this.key +']') == -1) { - els[i].setAttribute('hidden-by', hiddenBy + '['+ this.key +']'); - } - } else if ((hiddenBy == '['+ this.key +']' || hs.focusKey == wrapperKey) - && wrapperKey != this.key) { // on move - els[i].setAttribute('hidden-by', ''); - els[i].style[prop] = els[i].origProp || ''; - } else if (hiddenBy && hiddenBy.indexOf('['+ this.key +']') > -1) { - els[i].setAttribute('hidden-by', hiddenBy.replace('['+ this.key +']', '')); - } - - } - } - } -}, - -focus : function() { - this.wrapper.style.zIndex = hs.zIndexCounter += 2; - // blur others - for (var i = 0; i < hs.expanders.length; i++) { - if (hs.expanders[i] && i == hs.focusKey) { - var blurExp = hs.expanders[i]; - blurExp.content.className += ' highslide-'+ blurExp.contentType +'-blur'; - blurExp.content.style.cursor = hs.ieLt7 ? 'hand' : 'pointer'; - blurExp.content.title = hs.lang.focusTitle; - hs.fireEvent(blurExp, 'onBlur'); - } - } - - // focus this - if (this.outline) this.outline.table.style.zIndex - = this.wrapper.style.zIndex - 1; - this.content.className = 'highslide-'+ this.contentType; - this.content.title = hs.lang.restoreTitle; - - if (hs.restoreCursor) { - hs.styleRestoreCursor = window.opera ? 'pointer' : 'url('+ hs.graphicsDir + hs.restoreCursor +'), pointer'; - if (hs.ieLt7 && hs.uaVersion < 6) hs.styleRestoreCursor = 'hand'; - this.content.style.cursor = hs.styleRestoreCursor; - } - - hs.focusKey = this.key; - hs.addEventListener(document, window.opera ? 'keypress' : 'keydown', hs.keyHandler); - hs.fireEvent(this, 'onFocus'); -}, -moveTo: function(x, y) { - this.x.setPos(x); - this.y.setPos(y); -}, -resize : function (e) { - var w, h, r = e.width / e.height; - w = Math.max(e.width + e.dX, Math.min(this.minWidth, this.x.full)); - if (this.isImage && Math.abs(w - this.x.full) < 12) w = this.x.full; - h = w / r; - if (h < Math.min(this.minHeight, this.y.full)) { - h = Math.min(this.minHeight, this.y.full); - if (this.isImage) w = h * r; - } - this.resizeTo(w, h); -}, -resizeTo: function(w, h) { - this.y.setSize(h); - this.x.setSize(w); - this.wrapper.style.height = this.y.get('wsize') +'px'; -}, - -close : function() { - if (this.isClosing || !this.isExpanded) return; - if (this.transitions[1] == 'crossfade' && hs.upcoming) { - hs.getExpander(hs.upcoming).cancelLoading(); - hs.upcoming = null; - } - if (!hs.fireEvent(this, 'onBeforeClose')) return; - this.isClosing = true; - if (this.slideshow && !hs.upcoming) this.slideshow.pause(); - - hs.removeEventListener(document, window.opera ? 'keypress' : 'keydown', hs.keyHandler); - - try { - this.content.style.cursor = 'default'; - this.changeSize( - 0, { - wrapper: { - width : this.x.t, - height : this.y.t, - left: this.x.tpos - this.x.cb + this.x.tb, - top: this.y.tpos - this.y.cb + this.y.tb - }, - content: { - left: 0, - top: 0, - width: this.x.t, - height: this.y.t - } - }, hs.restoreDuration - ); - } catch (e) { this.afterClose(); } -}, - -createOverlay : function (o) { - var el = o.overlayId, - relToVP = (o.relativeTo == 'viewport' && !/panel$/.test(o.position)); - if (typeof el == 'string') el = hs.getNode(el); - if (o.html) el = hs.createElement('div', { innerHTML: o.html }); - if (!el || typeof el == 'string') return; - if (!hs.fireEvent(this, 'onCreateOverlay', { overlay: el })) return; - el.style.display = 'block'; - o.hsId = o.hsId || o.overlayId; - if (this.transitions[1] == 'crossfade' && this.reuseOverlay(o, el)) return; - this.genOverlayBox(); - var width = o.width && /^[0-9]+(px|%)$/.test(o.width) ? o.width : 'auto'; - if (/^(left|right)panel$/.test(o.position) && !/^[0-9]+px$/.test(o.width)) width = '200px'; - var overlay = hs.createElement( - 'div', { - id: 'hsId'+ hs.idCounter++, - hsId: o.hsId - }, { - position: 'absolute', - visibility: 'hidden', - width: width, - direction: hs.lang.cssDirection || '', - opacity: 0 - }, - relToVP ? hs.viewport :this.overlayBox, - true - ); - if (relToVP) overlay.hsKey = this.key; - - overlay.appendChild(el); - hs.extend(overlay, { - opacity: 1, - offsetX: 0, - offsetY: 0, - dur: (o.fade === 0 || o.fade === false || (o.fade == 2 && hs.ie)) ? 0 : 250 - }); - hs.extend(overlay, o); - - - if (this.gotOverlays) { - this.positionOverlay(overlay); - if (!overlay.hideOnMouseOut || this.mouseIsOver) - hs.animate(overlay, { opacity: overlay.opacity }, overlay.dur); - } - hs.push(this.overlays, hs.idCounter - 1); -}, -positionOverlay : function(overlay) { - var p = overlay.position || 'middle center', - relToVP = (overlay.relativeTo == 'viewport'), - offX = overlay.offsetX, - offY = overlay.offsetY; - if (relToVP) { - hs.viewport.style.display = 'block'; - overlay.hsKey = this.key; - if (overlay.offsetWidth > overlay.parentNode.offsetWidth) - overlay.style.width = '100%'; - } else - if (overlay.parentNode != this.overlayBox) this.overlayBox.appendChild(overlay); - if (/left$/.test(p)) overlay.style.left = offX +'px'; - - if (/center$/.test(p)) hs.setStyles (overlay, { - left: '50%', - marginLeft: (offX - Math.round(overlay.offsetWidth / 2)) +'px' - }); - - if (/right$/.test(p)) overlay.style.right = - offX +'px'; - - if (/^leftpanel$/.test(p)) { - hs.setStyles(overlay, { - right: '100%', - marginRight: this.x.cb +'px', - top: - this.y.cb +'px', - bottom: - this.y.cb +'px', - overflow: 'auto' - }); - this.x.p1 = overlay.offsetWidth; - - } else if (/^rightpanel$/.test(p)) { - hs.setStyles(overlay, { - left: '100%', - marginLeft: this.x.cb +'px', - top: - this.y.cb +'px', - bottom: - this.y.cb +'px', - overflow: 'auto' - }); - this.x.p2 = overlay.offsetWidth; - } - var parOff = overlay.parentNode.offsetHeight; - overlay.style.height = 'auto'; - if (relToVP && overlay.offsetHeight > parOff) - overlay.style.height = hs.ieLt7 ? parOff +'px' : '100%'; - - if (/^top/.test(p)) overlay.style.top = offY +'px'; - if (/^middle/.test(p)) hs.setStyles (overlay, { - top: '50%', - marginTop: (offY - Math.round(overlay.offsetHeight / 2)) +'px' - }); - if (/^bottom/.test(p)) overlay.style.bottom = - offY +'px'; - if (/^above$/.test(p)) { - hs.setStyles(overlay, { - left: (- this.x.p1 - this.x.cb) +'px', - right: (- this.x.p2 - this.x.cb) +'px', - bottom: '100%', - marginBottom: this.y.cb +'px', - width: 'auto' - }); - this.y.p1 = overlay.offsetHeight; - - } else if (/^below$/.test(p)) { - hs.setStyles(overlay, { - position: 'relative', - left: (- this.x.p1 - this.x.cb) +'px', - right: (- this.x.p2 - this.x.cb) +'px', - top: '100%', - marginTop: this.y.cb +'px', - width: 'auto' - }); - this.y.p2 = overlay.offsetHeight; - overlay.style.position = 'absolute'; - } -}, - -getOverlays : function() { - this.getInline(['heading', 'caption'], true); - this.getNumber(); - if (this.caption) hs.fireEvent(this, 'onAfterGetCaption'); - if (this.heading) hs.fireEvent(this, 'onAfterGetHeading'); - if (this.heading && this.dragByHeading) this.heading.className += ' highslide-move'; - if (hs.showCredits) this.writeCredits(); - for (var i = 0; i < hs.overlays.length; i++) { - var o = hs.overlays[i], tId = o.thumbnailId, sg = o.slideshowGroup; - if ((!tId && !sg) || (tId && tId == this.thumbsUserSetId) - || (sg && sg === this.slideshowGroup)) { - this.createOverlay(o); - } - } - var os = []; - for (var i = 0; i < this.overlays.length; i++) { - var o = hs.$('hsId'+ this.overlays[i]); - if (/panel$/.test(o.position)) this.positionOverlay(o); - else hs.push(os, o); - } - for (var i = 0; i < os.length; i++) this.positionOverlay(os[i]); - this.gotOverlays = true; -}, -genOverlayBox : function() { - if (!this.overlayBox) this.overlayBox = hs.createElement ( - 'div', { - className: this.wrapperClassName - }, { - position : 'absolute', - width: (this.x.size || (this.useBox ? this.width : null) - || this.x.full) +'px', - height: (this.y.size || this.y.full) +'px', - visibility : 'hidden', - overflow : 'hidden', - zIndex : hs.ie ? 4 : 'auto' - }, - hs.container, - true - ); -}, -sizeOverlayBox : function(doWrapper, doPanels) { - var overlayBox = this.overlayBox, - x = this.x, - y = this.y; - hs.setStyles( overlayBox, { - width: x.size +'px', - height: y.size +'px' - }); - if (doWrapper || doPanels) { - for (var i = 0; i < this.overlays.length; i++) { - var o = hs.$('hsId'+ this.overlays[i]); - var ie6 = (hs.ieLt7 || document.compatMode == 'BackCompat'); - if (o && /^(above|below)$/.test(o.position)) { - if (ie6) { - o.style.width = (overlayBox.offsetWidth + 2 * x.cb - + x.p1 + x.p2) +'px'; - } - y[o.position == 'above' ? 'p1' : 'p2'] = o.offsetHeight; - } - if (o && ie6 && /^(left|right)panel$/.test(o.position)) { - o.style.height = (overlayBox.offsetHeight + 2* y.cb) +'px'; - } - } - } - if (doWrapper) { - hs.setStyles(this.content, { - top: y.p1 +'px' - }); - hs.setStyles(overlayBox, { - top: (y.p1 + y.cb) +'px' - }); - } -}, - -showOverlays : function() { - var b = this.overlayBox; - b.className = ''; - hs.setStyles(b, { - top: (this.y.p1 + this.y.cb) +'px', - left: (this.x.p1 + this.x.cb) +'px', - overflow : 'visible' - }); - if (hs.safari) b.style.visibility = 'visible'; - this.wrapper.appendChild (b); - for (var i = 0; i < this.overlays.length; i++) { - var o = hs.$('hsId'+ this.overlays[i]); - o.style.zIndex = o.zIndex || 4; - if (!o.hideOnMouseOut || this.mouseIsOver) { - o.style.visibility = 'visible'; - hs.setStyles(o, { visibility: 'visible', display: '' }); - hs.animate(o, { opacity: o.opacity }, o.dur); - } - } -}, - -destroyOverlays : function() { - if (!this.overlays.length) return; - if (this.slideshow) { - var c = this.slideshow.controls; - if (c && hs.getExpander(c) == this) c.parentNode.removeChild(c); - } - for (var i = 0; i < this.overlays.length; i++) { - var o = hs.$('hsId'+ this.overlays[i]); - if (o && o.parentNode == hs.viewport && hs.getExpander(o) == this) hs.discardElement(o); - } - hs.discardElement(this.overlayBox); -}, - - - -createFullExpand : function () { - if (this.slideshow && this.slideshow.controls) { - this.slideshow.enable('full-expand'); - return; - } - this.fullExpandLabel = hs.createElement( - 'a', { - href: 'javascript:hs.expanders['+ this.key +'].doFullExpand();', - title: hs.lang.fullExpandTitle, - className: 'highslide-full-expand' - } - ); - if (!hs.fireEvent(this, 'onCreateFullExpand')) return; - - this.createOverlay({ - overlayId: this.fullExpandLabel, - position: hs.fullExpandPosition, - hideOnMouseOut: true, - opacity: hs.fullExpandOpacity - }); -}, - -doFullExpand : function () { - try { - if (!hs.fireEvent(this, 'onDoFullExpand')) return; - if (this.fullExpandLabel) hs.discardElement(this.fullExpandLabel); - - this.focus(); - var xSize = this.x.size, - ySize = this.y.size; - this.resizeTo(this.x.full, this.y.full); - - var xpos = this.x.pos - (this.x.size - xSize) / 2; - if (xpos < hs.marginLeft) xpos = hs.marginLeft; - - var ypos = this.y.pos - (this.y.size - ySize) / 2; - if (ypos < hs.marginTop) ypos = hs.marginTop; - - this.moveTo(xpos, ypos); - this.doShowHide('hidden'); - - } catch (e) { - this.error(e); - } -}, - - -afterClose : function () { - this.a.className = this.a.className.replace('highslide-active-anchor', ''); - - this.doShowHide('visible'); - if (this.outline && this.outlineWhileAnimating) this.outline.destroy(); - - hs.discardElement(this.wrapper); - this.destroyOverlays(); - if (!hs.viewport.childNodes.length) hs.viewport.style.display = 'none'; - - if (this.dimmingOpacity) hs.undim(this.key); - hs.fireEvent(this, 'onAfterClose'); - hs.expanders[this.key] = null; - hs.reOrder(); -} - -}; - - -hs.Slideshow = function (expKey, options) { - if (hs.dynamicallyUpdateAnchors !== false) hs.updateAnchors(); - this.expKey = expKey; - for (var x in options) this[x] = options[x]; - if (this.useControls) this.getControls(); - if (this.thumbstrip) this.thumbstrip = hs.Thumbstrip(this); -}; -hs.Slideshow.prototype = { -getControls: function() { - this.controls = hs.createElement('div', { innerHTML: hs.replaceLang(hs.skin.controls) }, - null, hs.container); - - var buttons = ['play', 'pause', 'previous', 'next', 'move', 'full-expand', 'close']; - this.btn = {}; - var pThis = this; - for (var i = 0; i < buttons.length; i++) { - this.btn[buttons[i]] = hs.getElementByClass(this.controls, 'li', 'highslide-'+ buttons[i]); - this.enable(buttons[i]); - } - this.btn.pause.style.display = 'none'; - //this.disable('full-expand'); -}, -checkFirstAndLast: function() { - if (this.repeat || !this.controls) return; - var exp = hs.expanders[this.expKey], - cur = exp.getAnchorIndex(), - re = /disabled$/; - if (cur == 0) - this.disable('previous'); - else if (re.test(this.btn.previous.getElementsByTagName('a')[0].className)) - this.enable('previous'); - if (cur + 1 == hs.anchors.groups[exp.slideshowGroup || 'none'].length) { - this.disable('next'); - this.disable('play'); - } else if (re.test(this.btn.next.getElementsByTagName('a')[0].className)) { - this.enable('next'); - this.enable('play'); - } -}, -enable: function(btn) { - if (!this.btn) return; - var sls = this, a = this.btn[btn].getElementsByTagName('a')[0], re = /disabled$/; - a.onclick = function() { - sls[btn](); - return false; - }; - if (re.test(a.className)) a.className = a.className.replace(re, ''); -}, -disable: function(btn) { - if (!this.btn) return; - var a = this.btn[btn].getElementsByTagName('a')[0]; - a.onclick = function() { return false; }; - if (!/disabled$/.test(a.className)) a.className += ' disabled'; -}, -hitSpace: function() { - if (this.autoplay) this.pause(); - else this.play(); -}, -play: function(wait) { - if (this.btn) { - this.btn.play.style.display = 'none'; - this.btn.pause.style.display = ''; - } - - this.autoplay = true; - if (!wait) hs.next(this.expKey); -}, -pause: function() { - if (this.btn) { - this.btn.pause.style.display = 'none'; - this.btn.play.style.display = ''; - } - - clearTimeout(this.autoplay); - this.autoplay = null; -}, -previous: function() { - this.pause(); - hs.previous(this.btn.previous); -}, -next: function() { - this.pause(); - hs.next(this.btn.next); -}, -move: function() {}, -'full-expand': function() { - hs.getExpander().doFullExpand(); -}, -close: function() { - hs.close(this.btn.close); -} -}; -hs.Thumbstrip = function(slideshow) { - function add (exp) { - hs.extend(options || {}, { - overlayId: dom, - hsId: 'thumbstrip', - className: 'highslide-thumbstrip-'+ mode +'-overlay ' + (options.className || '') - }); - if (hs.ieLt7) options.fade = 0; - exp.createOverlay(options); - hs.setStyles(dom.parentNode, { overflow: 'hidden' }); - }; - - function scroll (delta) { - selectThumb(undefined, Math.round(delta * dom[isX ? 'offsetWidth' : 'offsetHeight'] * 0.7)); - }; - - function selectThumb (i, scrollBy) { - if (i === undefined) for (var j = 0; j < group.length; j++) { - if (group[j] == hs.expanders[slideshow.expKey].a) { - i = j; - break; - } - } - if (i === undefined) return; - var as = dom.getElementsByTagName('a'), - active = as[i], - cell = active.parentNode, - left = isX ? 'Left' : 'Top', - right = isX ? 'Right' : 'Bottom', - width = isX ? 'Width' : 'Height', - offsetLeft = 'offset' + left, - offsetWidth = 'offset' + width, - overlayWidth = div.parentNode.parentNode[offsetWidth], - minTblPos = overlayWidth - table[offsetWidth], - curTblPos = parseInt(table.style[isX ? 'left' : 'top']) || 0, - tblPos = curTblPos, - mgnRight = 20; - if (scrollBy !== undefined) { - tblPos = curTblPos - scrollBy; - - if (minTblPos > 0) minTblPos = 0; - if (tblPos > 0) tblPos = 0; - if (tblPos < minTblPos) tblPos = minTblPos; - - - } else { - for (var j = 0; j < as.length; j++) as[j].className = ''; - active.className = 'highslide-active-anchor'; - var activeLeft = i > 0 ? as[i - 1].parentNode[offsetLeft] : cell[offsetLeft], - activeRight = cell[offsetLeft] + cell[offsetWidth] + - (as[i + 1] ? as[i + 1].parentNode[offsetWidth] : 0); - if (activeRight > overlayWidth - curTblPos) tblPos = overlayWidth - activeRight; - else if (activeLeft < -curTblPos) tblPos = -activeLeft; - } - var markerPos = cell[offsetLeft] + (cell[offsetWidth] - marker[offsetWidth]) / 2 + tblPos; - hs.animate(table, isX ? { left: tblPos } : { top: tblPos }, null, 'easeOutQuad'); - hs.animate(marker, isX ? { left: markerPos } : { top: markerPos }, null, 'easeOutQuad'); - scrollUp.style.display = tblPos < 0 ? 'block' : 'none'; - scrollDown.style.display = (tblPos > minTblPos) ? 'block' : 'none'; - - }; - - - // initialize - var group = hs.anchors.groups[hs.expanders[slideshow.expKey].slideshowGroup || 'none'], - options = slideshow.thumbstrip, - mode = options.mode || 'horizontal', - floatMode = (mode == 'float'), - tree = floatMode ? ['div', 'ul', 'li', 'span'] : ['table', 'tbody', 'tr', 'td'], - isX = (mode == 'horizontal'), - dom = hs.createElement('div', { - className: 'highslide-thumbstrip highslide-thumbstrip-'+ mode, - innerHTML: - '
    '+ - '<'+ tree[0] +'><'+ tree[1] +'>
    '+ - '
    '+ - '
    '+ - '
    ' - }, { - display: 'none' - }, hs.container), - domCh = dom.childNodes, - div = domCh[0], - scrollUp = domCh[1], - scrollDown = domCh[2], - marker = domCh[3], - table = div.firstChild, - tbody = dom.getElementsByTagName(tree[1])[0], - tr; - for (var i = 0; i < group.length; i++) { - if (i == 0 || !isX) tr = hs.createElement(tree[2], null, null, tbody); - (function(){ - var a = group[i], - cell = hs.createElement(tree[3], null, null, tr), - pI = i; - hs.createElement('a', { - href: a.href, - title: a.title, - onclick: function() { - if (/highslide-active-anchor/.test(this.className)) return false; - hs.getExpander(this).focus(); - return hs.transit(a); - }, - innerHTML: hs.stripItemFormatter ? hs.stripItemFormatter(a) : a.innerHTML - }, null, cell); - })(); - } - if (!floatMode) { - scrollUp.onclick = function () { scroll(-1); }; - scrollDown.onclick = function() { scroll(1); }; - hs.addEventListener(tbody, document.onmousewheel !== undefined ? - 'mousewheel' : 'DOMMouseScroll', function(e) { - var delta = 0; - e = e || window.event; - if (e.wheelDelta) { - delta = e.wheelDelta/120; - if (hs.opera) delta = -delta; - } else if (e.detail) { - delta = -e.detail/3; - } - if (delta) scroll(-delta * 0.2); - if (e.preventDefault) e.preventDefault(); - e.returnValue = false; - }); - } - - return { - add: add, - selectThumb: selectThumb - } -}; -hs.langDefaults = hs.lang; -// history -var HsExpander = hs.Expander; -if (hs.ie && window == window.top) { - (function () { - try { - document.documentElement.doScroll('left'); - } catch (e) { - setTimeout(arguments.callee, 50); - return; - } - hs.ready(); - })(); -} -hs.addEventListener(document, 'DOMContentLoaded', hs.ready); -hs.addEventListener(window, 'load', hs.ready); - -// set handlers -hs.addEventListener(document, 'ready', function() { - if (hs.expandCursor || hs.dimmingOpacity) { - var style = hs.createElement('style', { type: 'text/css' }, null, - document.getElementsByTagName('HEAD')[0]), - backCompat = document.compatMode == 'BackCompat'; - - - function addRule(sel, dec) { - if (hs.ie && (hs.uaVersion < 9 || backCompat)) { - var last = document.styleSheets[document.styleSheets.length - 1]; - if (typeof(last.addRule) == "object") last.addRule(sel, dec); - } else { - style.appendChild(document.createTextNode(sel + " {" + dec + "}")); - } - } - function fix(prop) { - return 'expression( ( ( ignoreMe = document.documentElement.'+ prop + - ' ? document.documentElement.'+ prop +' : document.body.'+ prop +' ) ) + \'px\' );'; - } - if (hs.expandCursor) addRule ('.highslide img', - 'cursor: url('+ hs.graphicsDir + hs.expandCursor +'), pointer !important;'); - addRule ('.highslide-viewport-size', - hs.ie && (hs.uaVersion < 7 || backCompat) ? - 'position: absolute; '+ - 'left:'+ fix('scrollLeft') + - 'top:'+ fix('scrollTop') + - 'width:'+ fix('clientWidth') + - 'height:'+ fix('clientHeight') : - 'position: fixed; width: 100%; height: 100%; left: 0; top: 0'); - } -}); -hs.addEventListener(window, 'resize', function() { - hs.getPageSize(); - if (hs.viewport) for (var i = 0; i < hs.viewport.childNodes.length; i++) { - var node = hs.viewport.childNodes[i], - exp = hs.getExpander(node); - exp.positionOverlay(node); - if (node.hsId == 'thumbstrip') exp.slideshow.thumbstrip.selectThumb(); - } -}); -hs.addEventListener(document, 'mousemove', function(e) { - hs.mouse = { x: e.clientX, y: e.clientY }; -}); -hs.addEventListener(document, 'mousedown', hs.mouseClickHandler); -hs.addEventListener(document, 'mouseup', hs.mouseClickHandler); - -hs.addEventListener(document, 'ready', hs.getAnchors); -hs.addEventListener(window, 'load', hs.preloadImages); -} diff --git a/gal/highslide/highslide.css b/gal/highslide/highslide.css deleted file mode 100644 index 452b862..0000000 --- a/gal/highslide/highslide.css +++ /dev/null @@ -1,888 +0,0 @@ -/** -* @file: highslide.css -* @version: 4.1.13 -*/ -.highslide-container div { - font-family: Verdana, Helvetica; - font-size: 10pt; -} -.highslide-container table { - background: none; - table-layout: auto; -} -.highslide { - outline: none; - text-decoration: none; -} -.highslide img { - border: 2px solid silver; -} -.highslide:hover img { - border-color: gray; -} -.highslide-active-anchor img { - visibility: hidden; -} -.highslide-gallery .highslide-active-anchor img { - border-color: black; - visibility: visible; - cursor: default; -} -.highslide-image { - border-width: 2px; - border-style: solid; - border-color: white; -} -.highslide-wrapper, .highslide-outline { - background: white; -} -.glossy-dark { - background: #111; -} - -.highslide-image-blur { -} -.highslide-number { - font-weight: bold; - color: gray; - font-size: .9em; -} -.highslide-caption { - display: none; - font-size: 1em; - padding: 5px; - /*background: white;*/ -} -.highslide-heading { - display: none; - font-weight: bold; - margin: 0.4em; -} -.highslide-dimming { - /*position: absolute;*/ - background: black; -} -a.highslide-full-expand { - background: url(graphics/fullexpand.gif) no-repeat; - display: block; - margin: 0 10px 10px 0; - width: 34px; - height: 34px; -} -.highslide-loading { - display: block; - color: black; - font-size: 9px; - font-weight: bold; - text-transform: uppercase; - text-decoration: none; - padding: 3px; - border: 1px solid white; - background-color: white; - padding-left: 22px; - background-image: url(graphics/loader.white.gif); - background-repeat: no-repeat; - background-position: 3px 1px; -} -a.highslide-credits, -a.highslide-credits i { - padding: 2px; - color: silver; - text-decoration: none; - font-size: 10px; -} -a.highslide-credits:hover, -a.highslide-credits:hover i { - color: white; - background-color: gray; -} -.highslide-move, .highslide-move * { - cursor: move; -} - -.highslide-viewport { - display: none; - position: fixed; - width: 100%; - height: 100%; - z-index: 1; - background: none; - left: 0; - top: 0; -} -.highslide-overlay { - display: none; -} -.hidden-container { - display: none; -} -/* Example of a semitransparent, offset closebutton */ -.closebutton { - position: relative; - top: -15px; - left: 15px; - width: 30px; - height: 30px; - cursor: pointer; - background: url(graphics/close.png); - /* NOTE! For IE6, you also need to update the highslide-ie6.css file. */ -} - -/*****************************************************************************/ -/* Thumbnail boxes for the galleries. */ -/* Remove these if you are not using a gallery. */ -/*****************************************************************************/ -.highslide-gallery ul { - list-style-type: none; - margin: 0; - padding: 0; -} -.highslide-gallery ul li { - display: block; - position: relative; - float: left; - width: 106px; - height: 106px; - margin: 2px; - padding: 0; - line-height: 0; - overflow: hidden; -} -.highslide-gallery ul a { - position: absolute; - top: 50%; - left: 50%; -} -.highslide-gallery ul img { - position: relative; - top: -50%; - left: -50%; -} -html>/**/body .highslide-gallery ul li { - display: table; - text-align: center; -} -html>/**/body .highslide-gallery ul li { - text-align: center; -} -html>/**/body .highslide-gallery ul a { - position: static; - display: table-cell; - vertical-align: middle; -} -html>/**/body .highslide-gallery ul img { - position: static; -} - -/*****************************************************************************/ -/* Controls for the galleries. */ -/* Remove these if you are not using a gallery */ -/*****************************************************************************/ -.highslide-controls { - width: 195px; - height: 40px; - background: url(graphics/controlbar-white.gif) 0 -90px no-repeat; - margin: 20px 15px 10px 0; -} -.highslide-controls ul { - position: relative; - left: 15px; - height: 40px; - list-style: none; - margin: 0; - padding: 0; - background: url(graphics/controlbar-white.gif) right -90px no-repeat; - -} -.highslide-controls li { - float: left; - padding: 5px 0; - margin:0; - list-style: none; -} -.highslide-controls a { - background-image: url(graphics/controlbar-white.gif); - display: block; - float: left; - height: 30px; - width: 30px; - outline: none; -} -.highslide-controls a.disabled { - cursor: default; -} -.highslide-controls a.disabled span { - cursor: default; -} -.highslide-controls a span { - /* hide the text for these graphic buttons */ - display: none; - cursor: pointer; -} - - -/* The CSS sprites for the controlbar - see http://www.google.com/search?q=css+sprites */ -.highslide-controls .highslide-previous a { - background-position: 0 0; -} -.highslide-controls .highslide-previous a:hover { - background-position: 0 -30px; -} -.highslide-controls .highslide-previous a.disabled { - background-position: 0 -60px !important; -} -.highslide-controls .highslide-play a { - background-position: -30px 0; -} -.highslide-controls .highslide-play a:hover { - background-position: -30px -30px; -} -.highslide-controls .highslide-play a.disabled { - background-position: -30px -60px !important; -} -.highslide-controls .highslide-pause a { - background-position: -60px 0; -} -.highslide-controls .highslide-pause a:hover { - background-position: -60px -30px; -} -.highslide-controls .highslide-next a { - background-position: -90px 0; -} -.highslide-controls .highslide-next a:hover { - background-position: -90px -30px; -} -.highslide-controls .highslide-next a.disabled { - background-position: -90px -60px !important; -} -.highslide-controls .highslide-move a { - background-position: -120px 0; -} -.highslide-controls .highslide-move a:hover { - background-position: -120px -30px; -} -.highslide-controls .highslide-full-expand a { - background-position: -150px 0; -} -.highslide-controls .highslide-full-expand a:hover { - background-position: -150px -30px; -} -.highslide-controls .highslide-full-expand a.disabled { - background-position: -150px -60px !important; -} -.highslide-controls .highslide-close a { - background-position: -180px 0; -} -.highslide-controls .highslide-close a:hover { - background-position: -180px -30px; -} - -/*****************************************************************************/ -/* Styles for the HTML popups */ -/* Remove these if you are not using Highslide HTML */ -/*****************************************************************************/ -.highslide-maincontent { - display: none; -} -.highslide-html { - background-color: white; -} -.mobile .highslide-html { - border: 1px solid silver; -} -.highslide-html-content { - display: none; - width: 400px; - padding: 0 5px 5px 5px; -} -.highslide-header { - padding-bottom: 5px; -} -.highslide-header ul { - margin: 0; - padding: 0; - text-align: right; -} -.highslide-header ul li { - display: inline; - padding-left: 1em; -} -.highslide-header ul li.highslide-previous, .highslide-header ul li.highslide-next { - display: none; -} -.highslide-header a { - font-weight: bold; - color: gray; - text-transform: uppercase; - text-decoration: none; -} -.highslide-header a:hover { - color: black; -} -.highslide-header .highslide-move a { - cursor: move; -} -.highslide-footer { - height: 16px; -} -.highslide-footer .highslide-resize { - display: block; - float: right; - margin-top: 5px; - height: 11px; - width: 11px; - background: url(graphics/resize.gif) no-repeat; -} -.highslide-footer .highslide-resize span { - display: none; -} -.highslide-body { -} -.highslide-resize { - cursor: nw-resize; -} - -/*****************************************************************************/ -/* Styles for the Individual wrapper class names. */ -/* See www.highslide.com/ref/hs.wrapperClassName */ -/* You can safely remove the class name themes you don't use */ -/*****************************************************************************/ - -/* hs.wrapperClassName = 'draggable-header' */ -.draggable-header .highslide-header { - height: 18px; - border-bottom: 1px solid #dddddd; -} -.draggable-header .highslide-heading { - position: absolute; - margin: 2px 0.4em; -} - -.draggable-header .highslide-header .highslide-move { - cursor: move; - display: block; - height: 16px; - position: absolute; - right: 24px; - top: 0; - width: 100%; - z-index: 1; -} -.draggable-header .highslide-header .highslide-move * { - display: none; -} -.draggable-header .highslide-header .highslide-close { - position: absolute; - right: 2px; - top: 2px; - z-index: 5; - padding: 0; -} -.draggable-header .highslide-header .highslide-close a { - display: block; - height: 16px; - width: 16px; - background-image: url(graphics/closeX.png); -} -.draggable-header .highslide-header .highslide-close a:hover { - background-position: 0 16px; -} -.draggable-header .highslide-header .highslide-close span { - display: none; -} -.draggable-header .highslide-maincontent { - padding-top: 1em; -} - -/* hs.wrapperClassName = 'titlebar' */ -.titlebar .highslide-header { - height: 18px; - border-bottom: 1px solid #dddddd; -} -.titlebar .highslide-heading { - position: absolute; - width: 90%; - margin: 1px 0 1px 5px; - color: #666666; -} - -.titlebar .highslide-header .highslide-move { - cursor: move; - display: block; - height: 16px; - position: absolute; - right: 24px; - top: 0; - width: 100%; - z-index: 1; -} -.titlebar .highslide-header .highslide-move * { - display: none; -} -.titlebar .highslide-header li { - position: relative; - top: 3px; - z-index: 2; - padding: 0 0 0 1em; -} -.titlebar .highslide-maincontent { - padding-top: 1em; -} - -/* hs.wrapperClassName = 'no-footer' */ -.no-footer .highslide-footer { - display: none; -} - -/* hs.wrapperClassName = 'wide-border' */ -.wide-border { - background: white; -} -.wide-border .highslide-image { - border-width: 10px; -} -.wide-border .highslide-caption { - padding: 0 10px 10px 10px; -} - -/* hs.wrapperClassName = 'borderless' */ -.borderless .highslide-image { - border: none; -} -.borderless .highslide-caption { - border-bottom: 1px solid white; - border-top: 1px solid white; - background: silver; -} - -/* hs.wrapperClassName = 'outer-glow' */ -.outer-glow { - background: #444; -} -.outer-glow .highslide-image { - border: 5px solid #444444; -} -.outer-glow .highslide-caption { - border: 5px solid #444444; - border-top: none; - padding: 5px; - background-color: gray; -} - -/* hs.wrapperClassName = 'colored-border' */ -.colored-border { - background: white; -} -.colored-border .highslide-image { - border: 2px solid green; -} -.colored-border .highslide-caption { - border: 2px solid green; - border-top: none; -} - -/* hs.wrapperClassName = 'dark' */ -.dark { - background: #111; -} -.dark .highslide-image { - border-color: black black #202020 black; - background: gray; -} -.dark .highslide-caption { - color: white; - background: #111; -} -.dark .highslide-controls, -.dark .highslide-controls ul, -.dark .highslide-controls a { - background-image: url(graphics/controlbar-black-border.gif); -} - -/* hs.wrapperClassName = 'floating-caption' */ -.floating-caption .highslide-caption { - position: absolute; - padding: 1em 0 0 0; - background: none; - color: white; - border: none; - font-weight: bold; -} - -/* hs.wrapperClassName = 'controls-in-heading' */ -.controls-in-heading .highslide-heading { - color: gray; - font-weight: bold; - height: 20px; - overflow: hidden; - cursor: default; - padding: 0 0 0 22px; - margin: 0; - background: url(graphics/icon.gif) no-repeat 0 1px; -} -.controls-in-heading .highslide-controls { - width: 105px; - height: 20px; - position: relative; - margin: 0; - top: -23px; - left: 7px; - background: none; -} -.controls-in-heading .highslide-controls ul { - position: static; - height: 20px; - background: none; -} -.controls-in-heading .highslide-controls li { - padding: 0; -} -.controls-in-heading .highslide-controls a { - background-image: url(graphics/controlbar-white-small.gif); - height: 20px; - width: 20px; -} - -.controls-in-heading .highslide-controls .highslide-move { - display: none; -} - -.controls-in-heading .highslide-controls .highslide-previous a { - background-position: 0 0; -} -.controls-in-heading .highslide-controls .highslide-previous a:hover { - background-position: 0 -20px; -} -.controls-in-heading .highslide-controls .highslide-previous a.disabled { - background-position: 0 -40px !important; -} -.controls-in-heading .highslide-controls .highslide-play a { - background-position: -20px 0; -} -.controls-in-heading .highslide-controls .highslide-play a:hover { - background-position: -20px -20px; -} -.controls-in-heading .highslide-controls .highslide-play a.disabled { - background-position: -20px -40px !important; -} -.controls-in-heading .highslide-controls .highslide-pause a { - background-position: -40px 0; -} -.controls-in-heading .highslide-controls .highslide-pause a:hover { - background-position: -40px -20px; -} -.controls-in-heading .highslide-controls .highslide-next a { - background-position: -60px 0; -} -.controls-in-heading .highslide-controls .highslide-next a:hover { - background-position: -60px -20px; -} -.controls-in-heading .highslide-controls .highslide-next a.disabled { - background-position: -60px -40px !important; -} -.controls-in-heading .highslide-controls .highslide-full-expand a { - background-position: -100px 0; -} -.controls-in-heading .highslide-controls .highslide-full-expand a:hover { - background-position: -100px -20px; -} -.controls-in-heading .highslide-controls .highslide-full-expand a.disabled { - background-position: -100px -40px !important; -} -.controls-in-heading .highslide-controls .highslide-close a { - background-position: -120px 0; -} -.controls-in-heading .highslide-controls .highslide-close a:hover { - background-position: -120px -20px; -} - -/*****************************************************************************/ -/* Styles for text based controls. */ -/* You can safely remove this if you don't use text based controls */ -/*****************************************************************************/ - -.text-controls .highslide-controls { - width: auto; - height: auto; - margin: 0; - text-align: center; - background: none; -} -.text-controls ul { - position: static; - background: none; - height: auto; - left: 0; -} -.text-controls .highslide-move { - display: none; -} -.text-controls li { - background-image: url(graphics/controlbar-text-buttons.png); - background-position: right top !important; - padding: 0; - margin-left: 15px; - display: block; - width: auto; -} -.text-controls a { - background: url(graphics/controlbar-text-buttons.png) no-repeat; - background-position: left top !important; - position: relative; - left: -10px; - display: block; - width: auto; - height: auto; - text-decoration: none !important; -} -.text-controls a span { - background: url(graphics/controlbar-text-buttons.png) no-repeat; - margin: 1px 2px 1px 10px; - display: block; - min-width: 4em; - height: 18px; - line-height: 18px; - padding: 1px 0 1px 18px; - color: #333; - font-family: "Trebuchet MS", Arial, sans-serif; - font-size: 12px; - font-weight: bold; - white-space: nowrap; -} -.text-controls .highslide-next { - margin-right: 1em; -} -.text-controls .highslide-full-expand a span { - min-width: 0; - margin: 1px 0; - padding: 1px 0 1px 10px; -} -.text-controls .highslide-close a span { - min-width: 0; -} -.text-controls a:hover span { - color: black; -} -.text-controls a.disabled span { - color: #999; -} - -.text-controls .highslide-previous span { - background-position: 0 -40px; -} -.text-controls .highslide-previous a.disabled { - background-position: left top !important; -} -.text-controls .highslide-previous a.disabled span { - background-position: 0 -140px; -} -.text-controls .highslide-play span { - background-position: 0 -60px; -} -.text-controls .highslide-play a.disabled { - background-position: left top !important; -} -.text-controls .highslide-play a.disabled span { - background-position: 0 -160px; -} -.text-controls .highslide-pause span { - background-position: 0 -80px; -} -.text-controls .highslide-next span { - background-position: 0 -100px; -} -.text-controls .highslide-next a.disabled { - background-position: left top !important; -} -.text-controls .highslide-next a.disabled span { - background-position: 0 -200px; -} -.text-controls .highslide-full-expand span { - background: none; -} -.text-controls .highslide-full-expand a.disabled { - background-position: left top !important; -} -.text-controls .highslide-close span { - background-position: 0 -120px; -} - - -/*****************************************************************************/ -/* Styles for the thumbstrip. */ -/* See www.highslide.com/ref/hs.addSlideshow */ -/* You can safely remove this if you don't use a thumbstrip */ -/*****************************************************************************/ - -.highslide-thumbstrip { - height: 100%; - direction: ltr; -} -.highslide-thumbstrip div { - overflow: hidden; -} -.highslide-thumbstrip table { - position: relative; - padding: 0; - border-collapse: collapse; -} -.highslide-thumbstrip td { - padding: 1px; - /*text-align: center;*/ -} -.highslide-thumbstrip a { - outline: none; -} -.highslide-thumbstrip img { - display: block; - border: 1px solid gray; - margin: 0 auto; -} -.highslide-thumbstrip .highslide-active-anchor img { - visibility: visible; -} -.highslide-thumbstrip .highslide-marker { - position: absolute; - width: 0; - height: 0; - border-width: 0; - border-style: solid; - border-color: transparent; /* change this to actual background color in highslide-ie6.css */ -} -.highslide-thumbstrip-horizontal div { - width: auto; - /* width: 100% breaks in small strips in IE */ -} -.highslide-thumbstrip-horizontal .highslide-scroll-up { - display: none; - position: absolute; - top: 3px; - left: 3px; - width: 25px; - height: 42px; -} -.highslide-thumbstrip-horizontal .highslide-scroll-up div { - margin-bottom: 10px; - cursor: pointer; - background: url(graphics/scrollarrows.png) left center no-repeat; - height: 42px; -} -.highslide-thumbstrip-horizontal .highslide-scroll-down { - display: none; - position: absolute; - top: 3px; - right: 3px; - width: 25px; - height: 42px; -} -.highslide-thumbstrip-horizontal .highslide-scroll-down div { - margin-bottom: 10px; - cursor: pointer; - background: url(graphics/scrollarrows.png) center right no-repeat; - height: 42px; -} -.highslide-thumbstrip-horizontal table { - margin: 2px 0 10px 0; -} -.highslide-viewport .highslide-thumbstrip-horizontal table { - margin-left: 10px; -} -.highslide-thumbstrip-horizontal img { - width: auto; - height: 40px; -} -.highslide-thumbstrip-horizontal .highslide-marker { - top: 47px; - border-left-width: 6px; - border-right-width: 6px; - border-bottom: 6px solid gray; -} -.highslide-viewport .highslide-thumbstrip-horizontal .highslide-marker { - margin-left: 10px; -} -.dark .highslide-thumbstrip-horizontal .highslide-marker, .highslide-viewport .highslide-thumbstrip-horizontal .highslide-marker { - border-bottom-color: white !important; -} - -.highslide-thumbstrip-vertical-overlay { - overflow: hidden !important; -} -.highslide-thumbstrip-vertical div { - height: 100%; -} -.highslide-thumbstrip-vertical a { - display: block; -} -.highslide-thumbstrip-vertical .highslide-scroll-up { - display: none; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 25px; -} -.highslide-thumbstrip-vertical .highslide-scroll-up div { - margin-left: 10px; - cursor: pointer; - background: url(graphics/scrollarrows.png) top center no-repeat; - height: 25px; -} -.highslide-thumbstrip-vertical .highslide-scroll-down { - display: none; - position: absolute; - bottom: 0; - left: 0; - width: 100%; - height: 25px; -} -.highslide-thumbstrip-vertical .highslide-scroll-down div { - margin-left: 10px; - cursor: pointer; - background: url(graphics/scrollarrows.png) bottom center no-repeat; - height: 25px; -} -.highslide-thumbstrip-vertical table { - margin: 10px 0 0 10px; -} -.highslide-thumbstrip-vertical img { - width: 60px; /* t=5481 */ -} -.highslide-thumbstrip-vertical .highslide-marker { - left: 0; - margin-top: 8px; - border-top-width: 6px; - border-bottom-width: 6px; - border-left: 6px solid gray; -} -.dark .highslide-thumbstrip-vertical .highslide-marker, .highslide-viewport .highslide-thumbstrip-vertical .highslide-marker { - border-left-color: white; -} - -.highslide-viewport .highslide-thumbstrip-float { - overflow: auto; -} -.highslide-thumbstrip-float ul { - margin: 2px 0; - padding: 0; -} -.highslide-thumbstrip-float li { - display: block; - height: 60px; - margin: 0 2px; - list-style: none; - float: left; -} -.highslide-thumbstrip-float img { - display: inline; - border-color: silver; - max-height: 56px; -} -.highslide-thumbstrip-float .highslide-active-anchor img { - border-color: black; -} -.highslide-thumbstrip-float .highslide-scroll-up div, .highslide-thumbstrip-float .highslide-scroll-down div { - display: none; -} -.highslide-thumbstrip-float .highslide-marker { - display: none; -} diff --git a/gal/highslide/nav/back.png b/gal/highslide/nav/back.png deleted file mode 100644 index 501909c..0000000 Binary files a/gal/highslide/nav/back.png and /dev/null differ diff --git a/gal/highslide/nav/next.png b/gal/highslide/nav/next.png deleted file mode 100644 index bdd1011..0000000 Binary files a/gal/highslide/nav/next.png and /dev/null differ diff --git a/gal/highslide/nav/prev.png b/gal/highslide/nav/prev.png deleted file mode 100644 index 68b2514..0000000 Binary files a/gal/highslide/nav/prev.png and /dev/null differ diff --git a/gal/nrt-blue/back.png b/gal/nrt-blue/back.png deleted file mode 100644 index 501909c..0000000 Binary files a/gal/nrt-blue/back.png and /dev/null differ diff --git a/gal/nrt-blue/back.xcf b/gal/nrt-blue/back.xcf deleted file mode 100644 index 5d8848d..0000000 Binary files a/gal/nrt-blue/back.xcf and /dev/null differ diff --git a/gal/nrt-blue/bot.png b/gal/nrt-blue/bot.png deleted file mode 100644 index 86bb5f2..0000000 Binary files a/gal/nrt-blue/bot.png and /dev/null differ diff --git a/gal/nrt-blue/bot.xcf b/gal/nrt-blue/bot.xcf deleted file mode 100644 index a3dbedc..0000000 Binary files a/gal/nrt-blue/bot.xcf and /dev/null differ diff --git a/gal/nrt-blue/left.png b/gal/nrt-blue/left.png deleted file mode 100644 index a9852c7..0000000 Binary files a/gal/nrt-blue/left.png and /dev/null differ diff --git a/gal/nrt-blue/left.xcf b/gal/nrt-blue/left.xcf deleted file mode 100644 index 76912ed..0000000 Binary files a/gal/nrt-blue/left.xcf and /dev/null differ diff --git a/gal/nrt-blue/next.png b/gal/nrt-blue/next.png deleted file mode 100644 index bdd1011..0000000 Binary files a/gal/nrt-blue/next.png and /dev/null differ diff --git a/gal/nrt-blue/next.xcf b/gal/nrt-blue/next.xcf deleted file mode 100644 index ad06333..0000000 Binary files a/gal/nrt-blue/next.xcf and /dev/null differ diff --git a/gal/nrt-blue/prev.png b/gal/nrt-blue/prev.png deleted file mode 100644 index 68b2514..0000000 Binary files a/gal/nrt-blue/prev.png and /dev/null differ diff --git a/gal/nrt-blue/prev.xcf b/gal/nrt-blue/prev.xcf deleted file mode 100644 index dbf35d5..0000000 Binary files a/gal/nrt-blue/prev.xcf and /dev/null differ diff --git a/gal/nrt-blue/right.png b/gal/nrt-blue/right.png deleted file mode 100644 index 7af66ba..0000000 Binary files a/gal/nrt-blue/right.png and /dev/null differ diff --git a/gal/nrt-blue/right.xcf b/gal/nrt-blue/right.xcf deleted file mode 100644 index 0668d05..0000000 Binary files a/gal/nrt-blue/right.xcf and /dev/null differ diff --git a/gal/nrt-blue/style.css b/gal/nrt-blue/style.css deleted file mode 100644 index de50720..0000000 --- a/gal/nrt-blue/style.css +++ /dev/null @@ -1,17 +0,0 @@ -.thf { margin: 0 0 0 0; padding: 0 0 0 0; float: left; border: none; } -.thumb { position: relative; width: 154px; height: 134px; background-color: black; } -.tt { position: absolute; top: 0; left: 0; } -.tl { position: absolute; top: 14px; left: 0; } -.ti { position: absolute; } -.tr { position: absolute; top: 14px; right: 0; } -.tb { position: absolute; bottom: 0; right: 0; } -IMG { border: none; } -H1 { text-align: center; } -H2 { text-align: center; margin-bottom: 3ex; } -P { clear: both; } -H2 { clear: both; } -.parent { text-align: center; } -.large { text-align: center; } -.back { float: left; } -.fwd { float: right; } -A[href]:hover { background-color: #11001d; } diff --git a/gal/nrt-blue/top.png b/gal/nrt-blue/top.png deleted file mode 100644 index db5a677..0000000 Binary files a/gal/nrt-blue/top.png and /dev/null differ diff --git a/gal/nrt-blue/top.xcf b/gal/nrt-blue/top.xcf deleted file mode 100644 index 8298369..0000000 Binary files a/gal/nrt-blue/top.xcf and /dev/null differ diff --git a/gal/plain/back.png b/gal/plain/back.png deleted file mode 100644 index 501909c..0000000 Binary files a/gal/plain/back.png and /dev/null differ diff --git a/gal/plain/next.png b/gal/plain/next.png deleted file mode 100644 index bdd1011..0000000 Binary files a/gal/plain/next.png and /dev/null differ diff --git a/gal/plain/prev.png b/gal/plain/prev.png deleted file mode 100644 index 68b2514..0000000 Binary files a/gal/plain/prev.png and /dev/null differ diff --git a/gal/plain/style.css b/gal/plain/style.css deleted file mode 100644 index 4e86e31..0000000 --- a/gal/plain/style.css +++ /dev/null @@ -1,13 +0,0 @@ -.thf { margin: 0 0 0 0; padding: 0 0 0 0; float: left; border: none; } -.thumb { position: relative; width: 130px; height: 110px; } -.ti { position: absolute; border: 1px solid #505; } -IMG { border: none; } -H1 { text-align: center; } -H2 { text-align: center; margin-bottom: 3ex; } -P { clear: both; } -H2 { clear: both; } -.parent { text-align: center; } -.large { text-align: center; } -.back { float: left; } -.fwd { float: right; } -A[href]:hover { background-color: #11001d; } diff --git a/highslide/custom.css b/highslide/custom.css new file mode 100644 index 0000000..6ef8df9 --- /dev/null +++ b/highslide/custom.css @@ -0,0 +1,66 @@ +/* + * Site-specific stylesheet for Highslide JS gallery theme + */ + +H1 { text-align: center; } +H2 { text-align: center; margin-bottom: 3ex; } +.parent { text-align: center; } +.large { text-align: center; } + +.fwd { + float: right; + width: 48px; + height: 48px; +} +.back { + float: left; + width: 48px; + height: 48px; +} +.up { + width: 48px; + height: 48px; +} + +A[href]:hover { background-color: inherit; } + +.highslide-wrapper, .highslide-outline { + background: #111111; +} +.highslide img { + border: 1px solid #D0D0D0; +} +.highslide:hover img { + border-color: #A0A0A0; +} +.highslide-active-anchor img { + visibility: visible; + border-color: #808080 !important; +} +.highslide-image { + border: 2px solid #111111; +} +.highslide-caption { + color: #CCCCCC; + padding: 2px; +} +.highslide-loading { + color: black; + border: 1px solid black; + background-color: white; + background-image: url(graphics/loader.white.gif); +} +.highslide-controls { + position: static !important; + margin: 0; + width: 120px !important; +} +.highslide-gallery ul li { + width: 130px; + height: 110px; + border: 1px solid #505; + margin: 2px; +} +.highslide-dimming { + background: black; +} diff --git a/highslide/custom.js b/highslide/custom.js new file mode 100644 index 0000000..ae76993 --- /dev/null +++ b/highslide/custom.js @@ -0,0 +1,95 @@ +/* + * Site-specific configuration settings for Highslide JS + */ + +hs.showCredits = true; +hs.creditsPosition = 'top right'; +hs.outlineType = 'rounded-black'; +hs.fadeInOut = false; +hs.align = 'center'; +hs.useBox = true; +hs.width = 1080; +hs.height = 840; +// hs.allowMultipleInstances = false; +hs.captionEval = 'this.thumb.title'; +hs.captionOverlay = { position: "top" }; +hs.expandDuration = 100; +hs.restoreDuration = 100; +hs.dimmingDuration = 100; +hs.dimmingOpacity = 0.8; +hs.transitionDuration = 100; +hs.thumbnailId = 'thumb1'; +hs.numberPosition = 'caption'; +hs.transitions = ['expand', 'crossfade']; + +hs.addSlideshow({ + interval: 5000, + repeat: false, + useControls: true, + fixedControls: 'fit', + overlayOptions: { + className: 'controls-in-heading', + opacity: 0.6, + position: 'top center', + hideOnMouseOut: true + }, + thumbstrip: { + mode: 'horizontal', + position: 'below', + relativeTo: 'expander' + } +}); + + +//////////////////////////////////////////////// +// Dynamic change of the hash part of the URL // +//////////////////////////////////////////////// +var hashTag = ''; +// Use hashDelimiter to "hide" name of the anchor (and to not scroll the page) +var hashDelimiter = '_'; + +hs.extend (hs.Expander.prototype, { + onAfterExpand: function(sender) { + hashTag = this.a.id; + window.location.hash = hashDelimiter + hashTag; + }, + + onBeforeClose: function(sender) { + window.location.hash = ''; + } +}); + +function showDefaultImage() { + var hashParts = window.location.hash.split(hashDelimiter); + var myThumb = document.getElementById(hashParts[1]) + if (hashParts[1] && myThumb) myThumb.click(); + else window.location.hash = ''; +} +// If the new hash is not equal to the hash we store internally, +// then it must be the user hitting the "back/forward" button +function checkHashChange() { + var hashParts = window.location.hash.split(hashDelimiter); + if (hashParts[1] != hashTag) { + hs.close(); + window.location.hash = ''; + } +} + +// Add onLoad and onHashChange functions +if(window.onload) { + var current = window.onload; + var newAction = function() { + current(); + showDefaultImage(); + }; + window.onload = newAction; +} else window.onload = showDefaultImage; + +if(window.onhashchange) { + var current = window.onHashChange; + var newAction = function() { + current(); + checkHashChange(); + }; + window.onhashchange = newAction; +} else window.onhashchange = checkHashChange; diff --git a/highslide/graphics/close.png b/highslide/graphics/close.png new file mode 100644 index 0000000..4de4396 Binary files /dev/null and b/highslide/graphics/close.png differ diff --git a/highslide/graphics/closeX.png b/highslide/graphics/closeX.png new file mode 100644 index 0000000..cf5d018 Binary files /dev/null and b/highslide/graphics/closeX.png differ diff --git a/highslide/graphics/controlbar-black-border.gif b/highslide/graphics/controlbar-black-border.gif new file mode 100644 index 0000000..e2403fe Binary files /dev/null and b/highslide/graphics/controlbar-black-border.gif differ diff --git a/highslide/graphics/controlbar-text-buttons.png b/highslide/graphics/controlbar-text-buttons.png new file mode 100644 index 0000000..d2f72e0 Binary files /dev/null and b/highslide/graphics/controlbar-text-buttons.png differ diff --git a/highslide/graphics/controlbar-white-small.gif b/highslide/graphics/controlbar-white-small.gif new file mode 100644 index 0000000..462fce7 Binary files /dev/null and b/highslide/graphics/controlbar-white-small.gif differ diff --git a/highslide/graphics/controlbar-white.gif b/highslide/graphics/controlbar-white.gif new file mode 100644 index 0000000..1f143f5 Binary files /dev/null and b/highslide/graphics/controlbar-white.gif differ diff --git a/highslide/graphics/controlbar2.gif b/highslide/graphics/controlbar2.gif new file mode 100644 index 0000000..39ad652 Binary files /dev/null and b/highslide/graphics/controlbar2.gif differ diff --git a/highslide/graphics/controlbar3.gif b/highslide/graphics/controlbar3.gif new file mode 100644 index 0000000..3eebb81 Binary files /dev/null and b/highslide/graphics/controlbar3.gif differ diff --git a/highslide/graphics/controlbar4-hover.gif b/highslide/graphics/controlbar4-hover.gif new file mode 100644 index 0000000..ca08b59 Binary files /dev/null and b/highslide/graphics/controlbar4-hover.gif differ diff --git a/highslide/graphics/controlbar4.gif b/highslide/graphics/controlbar4.gif new file mode 100644 index 0000000..7a3ad34 Binary files /dev/null and b/highslide/graphics/controlbar4.gif differ diff --git a/highslide/graphics/fullexpand.gif b/highslide/graphics/fullexpand.gif new file mode 100644 index 0000000..26d9ed0 Binary files /dev/null and b/highslide/graphics/fullexpand.gif differ diff --git a/highslide/graphics/geckodimmer.png b/highslide/graphics/geckodimmer.png new file mode 100644 index 0000000..309bb27 Binary files /dev/null and b/highslide/graphics/geckodimmer.png differ diff --git a/highslide/graphics/icon.gif b/highslide/graphics/icon.gif new file mode 100644 index 0000000..b74a073 Binary files /dev/null and b/highslide/graphics/icon.gif differ diff --git a/highslide/graphics/loader.big.black.gif b/highslide/graphics/loader.big.black.gif new file mode 100644 index 0000000..c95d05a Binary files /dev/null and b/highslide/graphics/loader.big.black.gif differ diff --git a/highslide/graphics/loader.big.white.gif b/highslide/graphics/loader.big.white.gif new file mode 100644 index 0000000..3288d10 Binary files /dev/null and b/highslide/graphics/loader.big.white.gif differ diff --git a/highslide/graphics/loader.black.gif b/highslide/graphics/loader.black.gif new file mode 100644 index 0000000..0b31f6f Binary files /dev/null and b/highslide/graphics/loader.black.gif differ diff --git a/highslide/graphics/loader.white.gif b/highslide/graphics/loader.white.gif new file mode 100644 index 0000000..f2a1bc0 Binary files /dev/null and b/highslide/graphics/loader.white.gif differ diff --git a/highslide/graphics/outlines/beveled.png b/highslide/graphics/outlines/beveled.png new file mode 100644 index 0000000..fc428f4 Binary files /dev/null and b/highslide/graphics/outlines/beveled.png differ diff --git a/highslide/graphics/outlines/custom.png b/highslide/graphics/outlines/custom.png new file mode 100644 index 0000000..2f20f70 Binary files /dev/null and b/highslide/graphics/outlines/custom.png differ diff --git a/highslide/graphics/outlines/drop-shadow.png b/highslide/graphics/outlines/drop-shadow.png new file mode 100644 index 0000000..0186c2e Binary files /dev/null and b/highslide/graphics/outlines/drop-shadow.png differ diff --git a/highslide/graphics/outlines/glossy-dark.png b/highslide/graphics/outlines/glossy-dark.png new file mode 100644 index 0000000..3c64c0d Binary files /dev/null and b/highslide/graphics/outlines/glossy-dark.png differ diff --git a/highslide/graphics/outlines/outer-glow.png b/highslide/graphics/outlines/outer-glow.png new file mode 100644 index 0000000..288d43f Binary files /dev/null and b/highslide/graphics/outlines/outer-glow.png differ diff --git a/highslide/graphics/outlines/rounded-black.png b/highslide/graphics/outlines/rounded-black.png new file mode 100644 index 0000000..a77e65d Binary files /dev/null and b/highslide/graphics/outlines/rounded-black.png differ diff --git a/highslide/graphics/outlines/rounded-white.png b/highslide/graphics/outlines/rounded-white.png new file mode 100644 index 0000000..0d4b817 Binary files /dev/null and b/highslide/graphics/outlines/rounded-white.png differ diff --git a/highslide/graphics/resize.gif b/highslide/graphics/resize.gif new file mode 100644 index 0000000..9100de7 Binary files /dev/null and b/highslide/graphics/resize.gif differ diff --git a/highslide/graphics/scrollarrows.png b/highslide/graphics/scrollarrows.png new file mode 100644 index 0000000..b3d5575 Binary files /dev/null and b/highslide/graphics/scrollarrows.png differ diff --git a/highslide/graphics/zoom.png b/highslide/graphics/zoom.png new file mode 100644 index 0000000..908612e Binary files /dev/null and b/highslide/graphics/zoom.png differ diff --git a/highslide/graphics/zoomin.cur b/highslide/graphics/zoomin.cur new file mode 100644 index 0000000..cb79124 Binary files /dev/null and b/highslide/graphics/zoomin.cur differ diff --git a/highslide/graphics/zoomout.cur b/highslide/graphics/zoomout.cur new file mode 100644 index 0000000..acf6199 Binary files /dev/null and b/highslide/graphics/zoomout.cur differ diff --git a/highslide/highslide-ie6.css b/highslide/highslide-ie6.css new file mode 100644 index 0000000..b4d5484 --- /dev/null +++ b/highslide/highslide-ie6.css @@ -0,0 +1,76 @@ +.closebutton { + /* NOTE! This URL is relative to the HTML page, not the CSS */ + filter:progid:DXImageTransform.Microsoft.AlphaImageLoader( + src='../highslide/graphics/close.png', sizingMethod='scale'); + + background: none; + cursor: hand; +} + +/* Viewport fixed hack */ +.highslide-viewport { + position: absolute; + left: expression( ( ( ignoreMe1 = document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft ) ) + 'px' ); + top: expression( ( ignoreMe2 = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop ) + 'px' ); + width: expression( ( ( ignoreMe3 = document.documentElement.clientWidth ? document.documentElement.clientWidth : document.body.clientWidth ) ) + 'px' ); + height: expression( ( ( ignoreMe4 = document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight ) ) + 'px' ); +} + +/* Thumbstrip PNG fix */ +.highslide-scroll-down, .highslide-scroll-up { + position: relative; + overflow: hidden; +} +.highslide-scroll-down div, .highslide-scroll-up div { + /* NOTE! This URL is relative to the HTML page, not the CSS */ + filter:progid:DXImageTransform.Microsoft.AlphaImageLoader( + src='../highslide/graphics/scrollarrows.png', sizingMethod='scale'); + background: none !important; + position: absolute; + cursor: hand; + width: 75px; + height: 75px !important; +} +.highslide-thumbstrip-horizontal .highslide-scroll-down div { + left: -50px; + top: -15px; +} +.highslide-thumbstrip-horizontal .highslide-scroll-up div { + top: -15px; +} +.highslide-thumbstrip-vertical .highslide-scroll-down div { + top: -50px; +} + +/* Thumbstrip marker arrow trasparent background fix */ +.highslide-thumbstrip .highslide-marker { + border-color: white; /* match the background */ +} +.dark .highslide-thumbstrip-horizontal .highslide-marker { + border-color: #111; +} +.highslide-viewport .highslide-marker { + border-color: #333; +} +.highslide-thumbstrip { + float: left; +} + +/* Positioning fixes for the control bar */ +.text-controls .highslide-controls { + width: 480px; +} +.text-controls a span { + width: 4em; +} +.text-controls .highslide-full-expand a span { + width: 0; +} +.text-controls .highslide-close a span { + width: 0; +} + +/* Special */ +.in-page .highslide-thumbstrip-horizontal .highslide-marker { + border-bottom: gray; +} diff --git a/highslide/highslide-with-gallery.js b/highslide/highslide-with-gallery.js new file mode 100644 index 0000000..5576e5a --- /dev/null +++ b/highslide/highslide-with-gallery.js @@ -0,0 +1,2687 @@ +/** + * Name: Highslide JS + * Version: 4.1.13 (2011-10-06) + * Config: default +events +slideshow +positioning +transitions +viewport +thumbstrip + * Author: Torstein Hønsi + * Support: www.highslide.com/support + * License: www.highslide.com/#license + */ +if (!hs) { var hs = { +// Language strings +lang : { + cssDirection: 'ltr', + loadingText : 'Loading...', + loadingTitle : 'Click to cancel', + focusTitle : 'Click to bring to front', + fullExpandTitle : 'Expand to actual size (f)', + creditsText : 'Powered by Highslide JS', + creditsTitle : 'Go to the Highslide JS homepage', + previousText : 'Previous', + nextText : 'Next', + moveText : 'Move', + closeText : 'Close', + closeTitle : 'Close (esc)', + resizeTitle : 'Resize', + playText : 'Play', + playTitle : 'Play slideshow (spacebar)', + pauseText : 'Pause', + pauseTitle : 'Pause slideshow (spacebar)', + previousTitle : 'Previous (arrow left)', + nextTitle : 'Next (arrow right)', + moveTitle : 'Move', + fullExpandText : '1:1', + number: 'Image %1 of %2', + restoreTitle : 'Click to close image, click and drag to move. Use arrow keys for next and previous.' +}, +// See http://highslide.com/ref for examples of settings +graphicsDir : 'highslide/graphics/', +expandCursor : 'zoomin.cur', // null disables +restoreCursor : 'zoomout.cur', // null disables +expandDuration : 250, // milliseconds +restoreDuration : 250, +marginLeft : 15, +marginRight : 15, +marginTop : 15, +marginBottom : 15, +zIndexCounter : 1001, // adjust to other absolutely positioned elements +loadingOpacity : 0.75, +allowMultipleInstances: true, +numberOfImagesToPreload : 5, +outlineWhileAnimating : 2, // 0 = never, 1 = always, 2 = HTML only +outlineStartOffset : 3, // ends at 10 +padToMinWidth : false, // pad the popup width to make room for wide caption +fullExpandPosition : 'bottom right', +fullExpandOpacity : 1, +showCredits : true, // you can set this to false if you want +creditsHref : 'http://highslide.com/', +creditsTarget : '_self', +enableKeyListener : true, +openerTagNames : ['a'], // Add more to allow slideshow indexing +transitions : [], +transitionDuration: 250, +dimmingOpacity: 0, // Lightbox style dimming background +dimmingDuration: 50, // 0 for instant dimming + +anchor : 'auto', // where the image expands from +align : 'auto', // position in the client (overrides anchor) +targetX: null, // the id of a target element +targetY: null, +dragByHeading: true, +minWidth: 200, +minHeight: 200, +allowSizeReduction: true, // allow the image to reduce to fit client size. If false, this overrides minWidth and minHeight +outlineType : 'drop-shadow', // set null to disable outlines +skin : { + controls: + '
    ' +}, +// END OF YOUR SETTINGS + + +// declare internal properties +preloadTheseImages : [], +continuePreloading: true, +expanders : [], +overrides : [ + 'allowSizeReduction', + 'useBox', + 'anchor', + 'align', + 'targetX', + 'targetY', + 'outlineType', + 'outlineWhileAnimating', + 'captionId', + 'captionText', + 'captionEval', + 'captionOverlay', + 'headingId', + 'headingText', + 'headingEval', + 'headingOverlay', + 'creditsPosition', + 'dragByHeading', + 'autoplay', + 'numberPosition', + 'transitions', + 'dimmingOpacity', + + 'width', + 'height', + + 'wrapperClassName', + 'minWidth', + 'minHeight', + 'maxWidth', + 'maxHeight', + 'pageOrigin', + 'slideshowGroup', + 'easing', + 'easingClose', + 'fadeInOut', + 'src' +], +overlays : [], +idCounter : 0, +oPos : { + x: ['leftpanel', 'left', 'center', 'right', 'rightpanel'], + y: ['above', 'top', 'middle', 'bottom', 'below'] +}, +mouse: {}, +headingOverlay: {}, +captionOverlay: {}, +timers : [], + +slideshows : [], + +pendingOutlines : {}, +clones : {}, +onReady: [], +uaVersion: /Trident\/4\.0/.test(navigator.userAgent) ? 8 : + parseFloat((navigator.userAgent.toLowerCase().match( /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [0,'0'])[1]), +ie : (document.all && !window.opera), +//ie : navigator && /MSIE [678]/.test(navigator.userAgent), // ie9 compliant? +safari : /Safari/.test(navigator.userAgent), +geckoMac : /Macintosh.+rv:1\.[0-8].+Gecko/.test(navigator.userAgent), + +$ : function (id) { + if (id) return document.getElementById(id); +}, + +push : function (arr, val) { + arr[arr.length] = val; +}, + +createElement : function (tag, attribs, styles, parent, nopad) { + var el = document.createElement(tag); + if (attribs) hs.extend(el, attribs); + if (nopad) hs.setStyles(el, {padding: 0, border: 'none', margin: 0}); + if (styles) hs.setStyles(el, styles); + if (parent) parent.appendChild(el); + return el; +}, + +extend : function (el, attribs) { + for (var x in attribs) el[x] = attribs[x]; + return el; +}, + +setStyles : function (el, styles) { + for (var x in styles) { + if (hs.ieLt9 && x == 'opacity') { + if (styles[x] > 0.99) el.style.removeAttribute('filter'); + else el.style.filter = 'alpha(opacity='+ (styles[x] * 100) +')'; + } + else el.style[x] = styles[x]; + } +}, +animate: function(el, prop, opt) { + var start, + end, + unit; + if (typeof opt != 'object' || opt === null) { + var args = arguments; + opt = { + duration: args[2], + easing: args[3], + complete: args[4] + }; + } + if (typeof opt.duration != 'number') opt.duration = 250; + opt.easing = Math[opt.easing] || Math.easeInQuad; + opt.curAnim = hs.extend({}, prop); + for (var name in prop) { + var e = new hs.fx(el, opt , name ); + + start = parseFloat(hs.css(el, name)) || 0; + end = parseFloat(prop[name]); + unit = name != 'opacity' ? 'px' : ''; + + e.custom( start, end, unit ); + } +}, +css: function(el, prop) { + if (el.style[prop]) { + return el.style[prop]; + } else if (document.defaultView) { + return document.defaultView.getComputedStyle(el, null).getPropertyValue(prop); + + } else { + if (prop == 'opacity') prop = 'filter'; + var val = el.currentStyle[prop.replace(/\-(\w)/g, function (a, b){ return b.toUpperCase(); })]; + if (prop == 'filter') + val = val.replace(/alpha\(opacity=([0-9]+)\)/, + function (a, b) { return b / 100 }); + return val === '' ? 1 : val; + } +}, + +getPageSize : function () { + var d = document, w = window, iebody = d.compatMode && d.compatMode != 'BackCompat' + ? d.documentElement : d.body, + ieLt9 = hs.ie && (hs.uaVersion < 9 || typeof pageXOffset == 'undefined'); + + var width = ieLt9 ? iebody.clientWidth : + (d.documentElement.clientWidth || self.innerWidth), + height = ieLt9 ? iebody.clientHeight : self.innerHeight; + hs.page = { + width: width, + height: height, + scrollLeft: ieLt9 ? iebody.scrollLeft : pageXOffset, + scrollTop: ieLt9 ? iebody.scrollTop : pageYOffset + }; + return hs.page; +}, + +getPosition : function(el) { + var p = { x: el.offsetLeft, y: el.offsetTop }; + while (el.offsetParent) { + el = el.offsetParent; + p.x += el.offsetLeft; + p.y += el.offsetTop; + if (el != document.body && el != document.documentElement) { + p.x -= el.scrollLeft; + p.y -= el.scrollTop; + } + } + return p; +}, + +expand : function(a, params, custom, type) { + if (!a) a = hs.createElement('a', null, { display: 'none' }, hs.container); + if (typeof a.getParams == 'function') return params; + try { + new hs.Expander(a, params, custom); + return false; + } catch (e) { return true; } +}, +getElementByClass : function (el, tagName, className) { + var els = el.getElementsByTagName(tagName); + for (var i = 0; i < els.length; i++) { + if ((new RegExp(className)).test(els[i].className)) { + return els[i]; + } + } + return null; +}, +replaceLang : function(s) { + s = s.replace(/\s/g, ' '); + var re = /{hs\.lang\.([^}]+)\}/g, + matches = s.match(re), + lang; + if (matches) for (var i = 0; i < matches.length; i++) { + lang = matches[i].replace(re, "$1"); + if (typeof hs.lang[lang] != 'undefined') s = s.replace(matches[i], hs.lang[lang]); + } + return s; +}, + + +focusTopmost : function() { + var topZ = 0, + topmostKey = -1, + expanders = hs.expanders, + exp, + zIndex; + for (var i = 0; i < expanders.length; i++) { + exp = expanders[i]; + if (exp) { + zIndex = exp.wrapper.style.zIndex; + if (zIndex && zIndex > topZ) { + topZ = zIndex; + topmostKey = i; + } + } + } + if (topmostKey == -1) hs.focusKey = -1; + else expanders[topmostKey].focus(); +}, + +getParam : function (a, param) { + a.getParams = a.onclick; + var p = a.getParams ? a.getParams() : null; + a.getParams = null; + + return (p && typeof p[param] != 'undefined') ? p[param] : + (typeof hs[param] != 'undefined' ? hs[param] : null); +}, + +getSrc : function (a) { + var src = hs.getParam(a, 'src'); + if (src) return src; + return a.href; +}, + +getNode : function (id) { + var node = hs.$(id), clone = hs.clones[id], a = {}; + if (!node && !clone) return null; + if (!clone) { + clone = node.cloneNode(true); + clone.id = ''; + hs.clones[id] = clone; + return node; + } else { + return clone.cloneNode(true); + } +}, + +discardElement : function(d) { + if (d) hs.garbageBin.appendChild(d); + hs.garbageBin.innerHTML = ''; +}, +dim : function(exp) { + if (!hs.dimmer) { + isNew = true; + hs.dimmer = hs.createElement ('div', { + className: 'highslide-dimming highslide-viewport-size', + owner: '', + onclick: function() { + if (hs.fireEvent(hs, 'onDimmerClick')) + + hs.close(); + } + }, { + visibility: 'visible', + opacity: 0 + }, hs.container, true); + + if (/(Android|iPad|iPhone|iPod)/.test(navigator.userAgent)) { + var body = document.body; + function pixDimmerSize() { + hs.setStyles(hs.dimmer, { + width: body.scrollWidth +'px', + height: body.scrollHeight +'px' + }); + } + pixDimmerSize(); + hs.addEventListener(window, 'resize', pixDimmerSize); + } + } + hs.dimmer.style.display = ''; + + var isNew = hs.dimmer.owner == ''; + hs.dimmer.owner += '|'+ exp.key; + + if (isNew) { + if (hs.geckoMac && hs.dimmingGeckoFix) + hs.setStyles(hs.dimmer, { + background: 'url('+ hs.graphicsDir + 'geckodimmer.png)', + opacity: 1 + }); + else + hs.animate(hs.dimmer, { opacity: exp.dimmingOpacity }, hs.dimmingDuration); + } +}, +undim : function(key) { + if (!hs.dimmer) return; + if (typeof key != 'undefined') hs.dimmer.owner = hs.dimmer.owner.replace('|'+ key, ''); + + if ( + (typeof key != 'undefined' && hs.dimmer.owner != '') + || (hs.upcoming && hs.getParam(hs.upcoming, 'dimmingOpacity')) + ) return; + + if (hs.geckoMac && hs.dimmingGeckoFix) hs.dimmer.style.display = 'none'; + else hs.animate(hs.dimmer, { opacity: 0 }, hs.dimmingDuration, null, function() { + hs.dimmer.style.display = 'none'; + }); +}, +transit : function (adj, exp) { + var last = exp || hs.getExpander(); + exp = last; + if (hs.upcoming) return false; + else hs.last = last; + hs.removeEventListener(document, window.opera ? 'keypress' : 'keydown', hs.keyHandler); + try { + hs.upcoming = adj; + adj.onclick(); + } catch (e){ + hs.last = hs.upcoming = null; + } + try { + if (!adj || exp.transitions[1] != 'crossfade') + exp.close(); + } catch (e) {} + return false; +}, + +previousOrNext : function (el, op) { + var exp = hs.getExpander(el); + if (exp) return hs.transit(exp.getAdjacentAnchor(op), exp); + else return false; +}, + +previous : function (el) { + return hs.previousOrNext(el, -1); +}, + +next : function (el) { + return hs.previousOrNext(el, 1); +}, + +keyHandler : function(e) { + if (!e) e = window.event; + if (!e.target) e.target = e.srcElement; // ie + if (typeof e.target.form != 'undefined') return true; // form element has focus + if (!hs.fireEvent(hs, 'onKeyDown', e)) return true; + var exp = hs.getExpander(); + + var op = null; + switch (e.keyCode) { + case 70: // f + if (exp) exp.doFullExpand(); + return true; + case 32: // Space + op = 2; + break; + case 34: // Page Down + case 39: // Arrow right + case 40: // Arrow down + op = 1; + break; + case 8: // Backspace + case 33: // Page Up + case 37: // Arrow left + case 38: // Arrow up + op = -1; + break; + case 27: // Escape + case 13: // Enter + op = 0; + } + if (op !== null) {if (op != 2)hs.removeEventListener(document, window.opera ? 'keypress' : 'keydown', hs.keyHandler); + if (!hs.enableKeyListener) return true; + + if (e.preventDefault) e.preventDefault(); + else e.returnValue = false; + if (exp) { + if (op == 0) { + exp.close(); + } else if (op == 2) { + if (exp.slideshow) exp.slideshow.hitSpace(); + } else { + if (exp.slideshow) exp.slideshow.pause(); + hs.previousOrNext(exp.key, op); + } + return false; + } + } + return true; +}, + + +registerOverlay : function (overlay) { + hs.push(hs.overlays, hs.extend(overlay, { hsId: 'hsId'+ hs.idCounter++ } )); +}, + + +addSlideshow : function (options) { + var sg = options.slideshowGroup; + if (typeof sg == 'object') { + for (var i = 0; i < sg.length; i++) { + var o = {}; + for (var x in options) o[x] = options[x]; + o.slideshowGroup = sg[i]; + hs.push(hs.slideshows, o); + } + } else { + hs.push(hs.slideshows, options); + } +}, + +getWrapperKey : function (element, expOnly) { + var el, re = /^highslide-wrapper-([0-9]+)$/; + // 1. look in open expanders + el = element; + while (el.parentNode) { + if (el.hsKey !== undefined) return el.hsKey; + if (el.id && re.test(el.id)) return el.id.replace(re, "$1"); + el = el.parentNode; + } + // 2. look in thumbnail + if (!expOnly) { + el = element; + while (el.parentNode) { + if (el.tagName && hs.isHsAnchor(el)) { + for (var key = 0; key < hs.expanders.length; key++) { + var exp = hs.expanders[key]; + if (exp && exp.a == el) return key; + } + } + el = el.parentNode; + } + } + return null; +}, + +getExpander : function (el, expOnly) { + if (typeof el == 'undefined') return hs.expanders[hs.focusKey] || null; + if (typeof el == 'number') return hs.expanders[el] || null; + if (typeof el == 'string') el = hs.$(el); + return hs.expanders[hs.getWrapperKey(el, expOnly)] || null; +}, + +isHsAnchor : function (a) { + return (a.onclick && a.onclick.toString().replace(/\s/g, ' ').match(/hs.(htmlE|e)xpand/)); +}, + +reOrder : function () { + for (var i = 0; i < hs.expanders.length; i++) + if (hs.expanders[i] && hs.expanders[i].isExpanded) hs.focusTopmost(); +}, +fireEvent : function (obj, evt, args) { + return obj && obj[evt] ? (obj[evt](obj, args) !== false) : true; +}, + +mouseClickHandler : function(e) +{ + if (!e) e = window.event; + if (e.button > 1) return true; + if (!e.target) e.target = e.srcElement; + + var el = e.target; + while (el.parentNode + && !(/highslide-(image|move|html|resize)/.test(el.className))) + { + el = el.parentNode; + } + var exp = hs.getExpander(el); + if (exp && (exp.isClosing || !exp.isExpanded)) return true; + + if (exp && e.type == 'mousedown') { + if (e.target.form) return true; + var match = el.className.match(/highslide-(image|move|resize)/); + if (match) { + hs.dragArgs = { + exp: exp , + type: match[1], + left: exp.x.pos, + width: exp.x.size, + top: exp.y.pos, + height: exp.y.size, + clickX: e.clientX, + clickY: e.clientY + }; + + + hs.addEventListener(document, 'mousemove', hs.dragHandler); + if (e.preventDefault) e.preventDefault(); // FF + + if (/highslide-(image|html)-blur/.test(exp.content.className)) { + exp.focus(); + hs.hasFocused = true; + } + return false; + } + } else if (e.type == 'mouseup') { + + hs.removeEventListener(document, 'mousemove', hs.dragHandler); + + if (hs.dragArgs) { + if (hs.styleRestoreCursor && hs.dragArgs.type == 'image') + hs.dragArgs.exp.content.style.cursor = hs.styleRestoreCursor; + var hasDragged = hs.dragArgs.hasDragged; + + if (!hasDragged &&!hs.hasFocused && !/(move|resize)/.test(hs.dragArgs.type)) { + if (hs.fireEvent(exp, 'onImageClick')) + exp.close(); + } + else if (hasDragged || (!hasDragged && hs.hasHtmlExpanders)) { + hs.dragArgs.exp.doShowHide('hidden'); + } + + if (hasDragged) hs.fireEvent(hs.dragArgs.exp, 'onDrop', hs.dragArgs); + hs.hasFocused = false; + hs.dragArgs = null; + + } else if (/highslide-image-blur/.test(el.className)) { + el.style.cursor = hs.styleRestoreCursor; + } + } + return false; +}, + +dragHandler : function(e) +{ + if (!hs.dragArgs) return true; + if (!e) e = window.event; + var a = hs.dragArgs, exp = a.exp; + + a.dX = e.clientX - a.clickX; + a.dY = e.clientY - a.clickY; + + var distance = Math.sqrt(Math.pow(a.dX, 2) + Math.pow(a.dY, 2)); + if (!a.hasDragged) a.hasDragged = (a.type != 'image' && distance > 0) + || (distance > (hs.dragSensitivity || 5)); + + if (a.hasDragged && e.clientX > 5 && e.clientY > 5) { + if (!hs.fireEvent(exp, 'onDrag', a)) return false; + + if (a.type == 'resize') exp.resize(a); + else { + exp.moveTo(a.left + a.dX, a.top + a.dY); + if (a.type == 'image') exp.content.style.cursor = 'move'; + } + } + return false; +}, + +wrapperMouseHandler : function (e) { + try { + if (!e) e = window.event; + var over = /mouseover/i.test(e.type); + if (!e.target) e.target = e.srcElement; // ie + if (!e.relatedTarget) e.relatedTarget = + over ? e.fromElement : e.toElement; // ie + var exp = hs.getExpander(e.target); + if (!exp.isExpanded) return; + if (!exp || !e.relatedTarget || hs.getExpander(e.relatedTarget, true) == exp + || hs.dragArgs) return; + hs.fireEvent(exp, over ? 'onMouseOver' : 'onMouseOut', e); + for (var i = 0; i < exp.overlays.length; i++) (function() { + var o = hs.$('hsId'+ exp.overlays[i]); + if (o && o.hideOnMouseOut) { + if (over) hs.setStyles(o, { visibility: 'visible', display: '' }); + hs.animate(o, { opacity: over ? o.opacity : 0 }, o.dur); + } + })(); + } catch (e) {} +}, +addEventListener : function (el, event, func) { + if (el == document && event == 'ready') { + hs.push(hs.onReady, func); + } + try { + el.addEventListener(event, func, false); + } catch (e) { + try { + el.detachEvent('on'+ event, func); + el.attachEvent('on'+ event, func); + } catch (e) { + el['on'+ event] = func; + } + } +}, + +removeEventListener : function (el, event, func) { + try { + el.removeEventListener(event, func, false); + } catch (e) { + try { + el.detachEvent('on'+ event, func); + } catch (e) { + el['on'+ event] = null; + } + } +}, + +preloadFullImage : function (i) { + if (hs.continuePreloading && hs.preloadTheseImages[i] && hs.preloadTheseImages[i] != 'undefined') { + var img = document.createElement('img'); + img.onload = function() { + img = null; + hs.preloadFullImage(i + 1); + }; + img.src = hs.preloadTheseImages[i]; + } +}, +preloadImages : function (number) { + if (number && typeof number != 'object') hs.numberOfImagesToPreload = number; + + var arr = hs.getAnchors(); + for (var i = 0; i < arr.images.length && i < hs.numberOfImagesToPreload; i++) { + hs.push(hs.preloadTheseImages, hs.getSrc(arr.images[i])); + } + + // preload outlines + if (hs.outlineType) new hs.Outline(hs.outlineType, function () { hs.preloadFullImage(0)} ); + else + + hs.preloadFullImage(0); + + // preload cursor + if (hs.restoreCursor) var cur = hs.createElement('img', { src: hs.graphicsDir + hs.restoreCursor }); +}, + + +init : function () { + if (!hs.container) { + + hs.ieLt7 = hs.ie && hs.uaVersion < 7; + hs.ieLt9 = hs.ie && hs.uaVersion < 9; + + hs.getPageSize(); + for (var x in hs.langDefaults) { + if (typeof hs[x] != 'undefined') hs.lang[x] = hs[x]; + else if (typeof hs.lang[x] == 'undefined' && typeof hs.langDefaults[x] != 'undefined') + hs.lang[x] = hs.langDefaults[x]; + } + + hs.container = hs.createElement('div', { + className: 'highslide-container' + }, { + position: 'absolute', + left: 0, + top: 0, + width: '100%', + zIndex: hs.zIndexCounter, + direction: 'ltr' + }, + document.body, + true + ); + hs.loading = hs.createElement('a', { + className: 'highslide-loading', + title: hs.lang.loadingTitle, + innerHTML: hs.lang.loadingText, + href: 'javascript:;' + }, { + position: 'absolute', + top: '-9999px', + opacity: hs.loadingOpacity, + zIndex: 1 + }, hs.container + ); + hs.garbageBin = hs.createElement('div', null, { display: 'none' }, hs.container); + hs.viewport = hs.createElement('div', { + className: 'highslide-viewport highslide-viewport-size' + }, { + visibility: (hs.safari && hs.uaVersion < 525) ? 'visible' : 'hidden' + }, hs.container, 1 + ); + + // http://www.robertpenner.com/easing/ + Math.linearTween = function (t, b, c, d) { + return c*t/d + b; + }; + Math.easeInQuad = function (t, b, c, d) { + return c*(t/=d)*t + b; + }; + Math.easeOutQuad = function (t, b, c, d) { + return -c *(t/=d)*(t-2) + b; + }; + + hs.hideSelects = hs.ieLt7; + hs.hideIframes = ((window.opera && hs.uaVersion < 9) || navigator.vendor == 'KDE' + || (hs.ieLt7 && hs.uaVersion < 5.5)); + hs.fireEvent(this, 'onActivate'); + } +}, +ready : function() { + if (hs.isReady) return; + hs.isReady = true; + for (var i = 0; i < hs.onReady.length; i++) hs.onReady[i](); +}, + +updateAnchors : function() { + var el, els, all = [], images = [],groups = {}, re; + + for (var i = 0; i < hs.openerTagNames.length; i++) { + els = document.getElementsByTagName(hs.openerTagNames[i]); + for (var j = 0; j < els.length; j++) { + el = els[j]; + re = hs.isHsAnchor(el); + if (re) { + hs.push(all, el); + if (re[0] == 'hs.expand') hs.push(images, el); + var g = hs.getParam(el, 'slideshowGroup') || 'none'; + if (!groups[g]) groups[g] = []; + hs.push(groups[g], el); + } + } + } + hs.anchors = { all: all, groups: groups, images: images }; + return hs.anchors; + +}, + +getAnchors : function() { + return hs.anchors || hs.updateAnchors(); +}, + + +close : function(el) { + var exp = hs.getExpander(el); + if (exp) exp.close(); + return false; +} +}; // end hs object +hs.fx = function( elem, options, prop ){ + this.options = options; + this.elem = elem; + this.prop = prop; + + if (!options.orig) options.orig = {}; +}; +hs.fx.prototype = { + update: function(){ + (hs.fx.step[this.prop] || hs.fx.step._default)(this); + + if (this.options.step) + this.options.step.call(this.elem, this.now, this); + + }, + custom: function(from, to, unit){ + this.startTime = (new Date()).getTime(); + this.start = from; + this.end = to; + this.unit = unit;// || this.unit || "px"; + this.now = this.start; + this.pos = this.state = 0; + + var self = this; + function t(gotoEnd){ + return self.step(gotoEnd); + } + + t.elem = this.elem; + + if ( t() && hs.timers.push(t) == 1 ) { + hs.timerId = setInterval(function(){ + var timers = hs.timers; + + for ( var i = 0; i < timers.length; i++ ) + if ( !timers[i]() ) + timers.splice(i--, 1); + + if ( !timers.length ) { + clearInterval(hs.timerId); + } + }, 13); + } + }, + step: function(gotoEnd){ + var t = (new Date()).getTime(); + if ( gotoEnd || t >= this.options.duration + this.startTime ) { + this.now = this.end; + this.pos = this.state = 1; + this.update(); + + this.options.curAnim[ this.prop ] = true; + + var done = true; + for ( var i in this.options.curAnim ) + if ( this.options.curAnim[i] !== true ) + done = false; + + if ( done ) { + if (this.options.complete) this.options.complete.call(this.elem); + } + return false; + } else { + var n = t - this.startTime; + this.state = n / this.options.duration; + this.pos = this.options.easing(n, 0, 1, this.options.duration); + this.now = this.start + ((this.end - this.start) * this.pos); + this.update(); + } + return true; + } + +}; + +hs.extend( hs.fx, { + step: { + + opacity: function(fx){ + hs.setStyles(fx.elem, { opacity: fx.now }); + }, + + _default: function(fx){ + try { + if ( fx.elem.style && fx.elem.style[ fx.prop ] != null ) + fx.elem.style[ fx.prop ] = fx.now + fx.unit; + else + fx.elem[ fx.prop ] = fx.now; + } catch (e) {} + } + } +}); + +hs.Outline = function (outlineType, onLoad) { + this.onLoad = onLoad; + this.outlineType = outlineType; + var v = hs.uaVersion, tr; + + this.hasAlphaImageLoader = hs.ie && hs.uaVersion < 7; + if (!outlineType) { + if (onLoad) onLoad(); + return; + } + + hs.init(); + this.table = hs.createElement( + 'table', { + cellSpacing: 0 + }, { + visibility: 'hidden', + position: 'absolute', + borderCollapse: 'collapse', + width: 0 + }, + hs.container, + true + ); + var tbody = hs.createElement('tbody', null, null, this.table, 1); + + this.td = []; + for (var i = 0; i <= 8; i++) { + if (i % 3 == 0) tr = hs.createElement('tr', null, { height: 'auto' }, tbody, true); + this.td[i] = hs.createElement('td', null, null, tr, true); + var style = i != 4 ? { lineHeight: 0, fontSize: 0} : { position : 'relative' }; + hs.setStyles(this.td[i], style); + } + this.td[4].className = outlineType +' highslide-outline'; + + this.preloadGraphic(); +}; + +hs.Outline.prototype = { +preloadGraphic : function () { + var src = hs.graphicsDir + (hs.outlinesDir || "outlines/")+ this.outlineType +".png"; + + var appendTo = hs.safari && hs.uaVersion < 525 ? hs.container : null; + this.graphic = hs.createElement('img', null, { position: 'absolute', + top: '-9999px' }, appendTo, true); // for onload trigger + + var pThis = this; + this.graphic.onload = function() { pThis.onGraphicLoad(); }; + + this.graphic.src = src; +}, + +onGraphicLoad : function () { + var o = this.offset = this.graphic.width / 4, + pos = [[0,0],[0,-4],[-2,0],[0,-8],0,[-2,-8],[0,-2],[0,-6],[-2,-2]], + dim = { height: (2*o) +'px', width: (2*o) +'px' }; + for (var i = 0; i <= 8; i++) { + if (pos[i]) { + if (this.hasAlphaImageLoader) { + var w = (i == 1 || i == 7) ? '100%' : this.graphic.width +'px'; + var div = hs.createElement('div', null, { width: '100%', height: '100%', position: 'relative', overflow: 'hidden'}, this.td[i], true); + hs.createElement ('div', null, { + filter: "progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=scale, src='"+ this.graphic.src + "')", + position: 'absolute', + width: w, + height: this.graphic.height +'px', + left: (pos[i][0]*o)+'px', + top: (pos[i][1]*o)+'px' + }, + div, + true); + } else { + hs.setStyles(this.td[i], { background: 'url('+ this.graphic.src +') '+ (pos[i][0]*o)+'px '+(pos[i][1]*o)+'px'}); + } + + if (window.opera && (i == 3 || i ==5)) + hs.createElement('div', null, dim, this.td[i], true); + + hs.setStyles (this.td[i], dim); + } + } + this.graphic = null; + if (hs.pendingOutlines[this.outlineType]) hs.pendingOutlines[this.outlineType].destroy(); + hs.pendingOutlines[this.outlineType] = this; + if (this.onLoad) this.onLoad(); +}, + +setPosition : function (pos, offset, vis, dur, easing) { + var exp = this.exp, + stl = exp.wrapper.style, + offset = offset || 0, + pos = pos || { + x: exp.x.pos + offset, + y: exp.y.pos + offset, + w: exp.x.get('wsize') - 2 * offset, + h: exp.y.get('wsize') - 2 * offset + }; + if (vis) this.table.style.visibility = (pos.h >= 4 * this.offset) + ? 'visible' : 'hidden'; + hs.setStyles(this.table, { + left: (pos.x - this.offset) +'px', + top: (pos.y - this.offset) +'px', + width: (pos.w + 2 * this.offset) +'px' + }); + + pos.w -= 2 * this.offset; + pos.h -= 2 * this.offset; + hs.setStyles (this.td[4], { + width: pos.w >= 0 ? pos.w +'px' : 0, + height: pos.h >= 0 ? pos.h +'px' : 0 + }); + if (this.hasAlphaImageLoader) this.td[3].style.height + = this.td[5].style.height = this.td[4].style.height; + +}, + +destroy : function(hide) { + if (hide) this.table.style.visibility = 'hidden'; + else hs.discardElement(this.table); +} +}; + +hs.Dimension = function(exp, dim) { + this.exp = exp; + this.dim = dim; + this.ucwh = dim == 'x' ? 'Width' : 'Height'; + this.wh = this.ucwh.toLowerCase(); + this.uclt = dim == 'x' ? 'Left' : 'Top'; + this.lt = this.uclt.toLowerCase(); + this.ucrb = dim == 'x' ? 'Right' : 'Bottom'; + this.rb = this.ucrb.toLowerCase(); + this.p1 = this.p2 = 0; +}; +hs.Dimension.prototype = { +get : function(key) { + switch (key) { + case 'loadingPos': + return this.tpos + this.tb + (this.t - hs.loading['offset'+ this.ucwh]) / 2; + case 'loadingPosXfade': + return this.pos + this.cb+ this.p1 + (this.size - hs.loading['offset'+ this.ucwh]) / 2; + case 'wsize': + return this.size + 2 * this.cb + this.p1 + this.p2; + case 'fitsize': + return this.clientSize - this.marginMin - this.marginMax; + case 'maxsize': + return this.get('fitsize') - 2 * this.cb - this.p1 - this.p2 ; + case 'opos': + return this.pos - (this.exp.outline ? this.exp.outline.offset : 0); + case 'osize': + return this.get('wsize') + (this.exp.outline ? 2*this.exp.outline.offset : 0); + case 'imgPad': + return this.imgSize ? Math.round((this.size - this.imgSize) / 2) : 0; + + } +}, +calcBorders: function() { + // correct for borders + this.cb = (this.exp.content['offset'+ this.ucwh] - this.t) / 2; + + this.marginMax = hs['margin'+ this.ucrb]; +}, +calcThumb: function() { + this.t = this.exp.el[this.wh] ? parseInt(this.exp.el[this.wh]) : + this.exp.el['offset'+ this.ucwh]; + this.tpos = this.exp.tpos[this.dim]; + this.tb = (this.exp.el['offset'+ this.ucwh] - this.t) / 2; + if (this.tpos == 0 || this.tpos == -1) { + this.tpos = (hs.page[this.wh] / 2) + hs.page['scroll'+ this.uclt]; + }; +}, +calcExpanded: function() { + var exp = this.exp; + this.justify = 'auto'; + + // get alignment + if (exp.align == 'center') this.justify = 'center'; + else if (new RegExp(this.lt).test(exp.anchor)) this.justify = null; + else if (new RegExp(this.rb).test(exp.anchor)) this.justify = 'max'; + + + // size and position + this.pos = this.tpos - this.cb + this.tb; + + if (this.maxHeight && this.dim == 'x') + exp.maxWidth = Math.min(exp.maxWidth || this.full, exp.maxHeight * this.full / exp.y.full); + + this.size = Math.min(this.full, exp['max'+ this.ucwh] || this.full); + this.minSize = exp.allowSizeReduction ? + Math.min(exp['min'+ this.ucwh], this.full) :this.full; + if (exp.isImage && exp.useBox) { + this.size = exp[this.wh]; + this.imgSize = this.full; + } + if (this.dim == 'x' && hs.padToMinWidth) this.minSize = exp.minWidth; + this.target = exp['target'+ this.dim.toUpperCase()]; + this.marginMin = hs['margin'+ this.uclt]; + this.scroll = hs.page['scroll'+ this.uclt]; + this.clientSize = hs.page[this.wh]; +}, +setSize: function(i) { + var exp = this.exp; + if (exp.isImage && (exp.useBox || hs.padToMinWidth)) { + this.imgSize = i; + this.size = Math.max(this.size, this.imgSize); + exp.content.style[this.lt] = this.get('imgPad')+'px'; + } else + this.size = i; + + exp.content.style[this.wh] = i +'px'; + exp.wrapper.style[this.wh] = this.get('wsize') +'px'; + if (exp.outline) exp.outline.setPosition(); + if (this.dim == 'x' && exp.overlayBox) exp.sizeOverlayBox(true); + if (this.dim == 'x' && exp.slideshow && exp.isImage) { + if (i == this.full) exp.slideshow.disable('full-expand'); + else exp.slideshow.enable('full-expand'); + } +}, +setPos: function(i) { + this.pos = i; + this.exp.wrapper.style[this.lt] = i +'px'; + + if (this.exp.outline) this.exp.outline.setPosition(); + +} +}; + +hs.Expander = function(a, params, custom, contentType) { + if (document.readyState && hs.ie && !hs.isReady) { + hs.addEventListener(document, 'ready', function() { + new hs.Expander(a, params, custom, contentType); + }); + return; + } + this.a = a; + this.custom = custom; + this.contentType = contentType || 'image'; + this.isImage = !this.isHtml; + + hs.continuePreloading = false; + this.overlays = []; + this.last = hs.last; + hs.last = null; + hs.init(); + var key = this.key = hs.expanders.length; + // override inline parameters + for (var i = 0; i < hs.overrides.length; i++) { + var name = hs.overrides[i]; + this[name] = params && typeof params[name] != 'undefined' ? + params[name] : hs[name]; + } + if (!this.src) this.src = a.href; + + // get thumb + var el = (params && params.thumbnailId) ? hs.$(params.thumbnailId) : a; + el = this.thumb = el.getElementsByTagName('img')[0] || el; + this.thumbsUserSetId = el.id || a.id; + if (!hs.fireEvent(this, 'onInit')) return true; + + // check if already open + for (var i = 0; i < hs.expanders.length; i++) { + if (hs.expanders[i] && hs.expanders[i].a == a + && !(this.last && this.transitions[1] == 'crossfade')) { + hs.expanders[i].focus(); + return false; + } + } + + // cancel other + if (!hs.allowSimultaneousLoading) for (var i = 0; i < hs.expanders.length; i++) { + if (hs.expanders[i] && hs.expanders[i].thumb != el && !hs.expanders[i].onLoadStarted) { + hs.expanders[i].cancelLoading(); + } + } + hs.expanders[key] = this; + if (!hs.allowMultipleInstances && !hs.upcoming) { + if (hs.expanders[key-1]) hs.expanders[key-1].close(); + if (typeof hs.focusKey != 'undefined' && hs.expanders[hs.focusKey]) + hs.expanders[hs.focusKey].close(); + } + + // initiate metrics + this.el = el; + this.tpos = this.pageOrigin || hs.getPosition(el); + hs.getPageSize(); + var x = this.x = new hs.Dimension(this, 'x'); + x.calcThumb(); + var y = this.y = new hs.Dimension(this, 'y'); + y.calcThumb(); + this.wrapper = hs.createElement( + 'div', { + id: 'highslide-wrapper-'+ this.key, + className: 'highslide-wrapper '+ this.wrapperClassName + }, { + visibility: 'hidden', + position: 'absolute', + zIndex: hs.zIndexCounter += 2 + }, null, true ); + + this.wrapper.onmouseover = this.wrapper.onmouseout = hs.wrapperMouseHandler; + if (this.contentType == 'image' && this.outlineWhileAnimating == 2) + this.outlineWhileAnimating = 0; + + // get the outline + if (!this.outlineType + || (this.last && this.isImage && this.transitions[1] == 'crossfade')) { + this[this.contentType +'Create'](); + + } else if (hs.pendingOutlines[this.outlineType]) { + this.connectOutline(); + this[this.contentType +'Create'](); + + } else { + this.showLoading(); + var exp = this; + new hs.Outline(this.outlineType, + function () { + exp.connectOutline(); + exp[exp.contentType +'Create'](); + } + ); + } + return true; +}; + +hs.Expander.prototype = { +error : function(e) { + if (hs.debug) alert ('Line '+ e.lineNumber +': '+ e.message); + else window.location.href = this.src; +}, + +connectOutline : function() { + var outline = this.outline = hs.pendingOutlines[this.outlineType]; + outline.exp = this; + outline.table.style.zIndex = this.wrapper.style.zIndex - 1; + hs.pendingOutlines[this.outlineType] = null; +}, + +showLoading : function() { + if (this.onLoadStarted || this.loading) return; + + this.loading = hs.loading; + var exp = this; + this.loading.onclick = function() { + exp.cancelLoading(); + }; + + + if (!hs.fireEvent(this, 'onShowLoading')) return; + var exp = this, + l = this.x.get('loadingPos') +'px', + t = this.y.get('loadingPos') +'px'; + if (!tgt && this.last && this.transitions[1] == 'crossfade') + var tgt = this.last; + if (tgt) { + l = tgt.x.get('loadingPosXfade') +'px'; + t = tgt.y.get('loadingPosXfade') +'px'; + this.loading.style.zIndex = hs.zIndexCounter++; + } + setTimeout(function () { + if (exp.loading) hs.setStyles(exp.loading, { left: l, top: t, zIndex: hs.zIndexCounter++ })} + , 100); +}, + +imageCreate : function() { + var exp = this; + + var img = document.createElement('img'); + this.content = img; + img.onload = function () { + if (hs.expanders[exp.key]) exp.contentLoaded(); + }; + if (hs.blockRightClick) img.oncontextmenu = function() { return false; }; + img.className = 'highslide-image'; + hs.setStyles(img, { + visibility: 'hidden', + display: 'block', + position: 'absolute', + maxWidth: '9999px', + zIndex: 3 + }); + img.title = hs.lang.restoreTitle; + if (hs.safari && hs.uaVersion < 525) hs.container.appendChild(img); + if (hs.ie && hs.flushImgSize) img.src = null; + img.src = this.src; + + this.showLoading(); +}, + +contentLoaded : function() { + try { + if (!this.content) return; + this.content.onload = null; + if (this.onLoadStarted) return; + else this.onLoadStarted = true; + + var x = this.x, y = this.y; + + if (this.loading) { + hs.setStyles(this.loading, { top: '-9999px' }); + this.loading = null; + hs.fireEvent(this, 'onHideLoading'); + } + x.full = this.content.width; + y.full = this.content.height; + + hs.setStyles(this.content, { + width: x.t +'px', + height: y.t +'px' + }); + this.wrapper.appendChild(this.content); + hs.container.appendChild(this.wrapper); + + x.calcBorders(); + y.calcBorders(); + + hs.setStyles (this.wrapper, { + left: (x.tpos + x.tb - x.cb) +'px', + top: (y.tpos + x.tb - y.cb) +'px' + }); + + + this.initSlideshow(); + this.getOverlays(); + + var ratio = x.full / y.full; + x.calcExpanded(); + this.justify(x); + + y.calcExpanded(); + this.justify(y); + if (this.overlayBox) this.sizeOverlayBox(0, 1); + + + if (this.allowSizeReduction) { + this.correctRatio(ratio); + var ss = this.slideshow; + if (ss && this.last && ss.controls && ss.fixedControls) { + var pos = ss.overlayOptions.position || '', p; + for (var dim in hs.oPos) for (var i = 0; i < 5; i++) { + p = this[dim]; + if (pos.match(hs.oPos[dim][i])) { + p.pos = this.last[dim].pos + + (this.last[dim].p1 - p.p1) + + (this.last[dim].size - p.size) * [0, 0, .5, 1, 1][i]; + if (ss.fixedControls == 'fit') { + if (p.pos + p.size + p.p1 + p.p2 > p.scroll + p.clientSize - p.marginMax) + p.pos = p.scroll + p.clientSize - p.size - p.marginMin - p.marginMax - p.p1 - p.p2; + if (p.pos < p.scroll + p.marginMin) p.pos = p.scroll + p.marginMin; + } + } + } + } + if (this.isImage && this.x.full > (this.x.imgSize || this.x.size)) { + this.createFullExpand(); + if (this.overlays.length == 1) this.sizeOverlayBox(); + } + } + this.show(); + + } catch (e) { + this.error(e); + } +}, + +justify : function (p, moveOnly) { + var tgtArr, tgt = p.target, dim = p == this.x ? 'x' : 'y'; + + if (tgt && tgt.match(/ /)) { + tgtArr = tgt.split(' '); + tgt = tgtArr[0]; + } + if (tgt && hs.$(tgt)) { + p.pos = hs.getPosition(hs.$(tgt))[dim]; + if (tgtArr && tgtArr[1] && tgtArr[1].match(/^[-]?[0-9]+px$/)) + p.pos += parseInt(tgtArr[1]); + if (p.size < p.minSize) p.size = p.minSize; + + } else if (p.justify == 'auto' || p.justify == 'center') { + + var hasMovedMin = false; + + var allowReduce = p.exp.allowSizeReduction; + if (p.justify == 'center') + p.pos = Math.round(p.scroll + (p.clientSize + p.marginMin - p.marginMax - p.get('wsize')) / 2); + else + p.pos = Math.round(p.pos - ((p.get('wsize') - p.t) / 2)); + if (p.pos < p.scroll + p.marginMin) { + p.pos = p.scroll + p.marginMin; + hasMovedMin = true; + } + if (!moveOnly && p.size < p.minSize) { + p.size = p.minSize; + allowReduce = false; + } + if (p.pos + p.get('wsize') > p.scroll + p.clientSize - p.marginMax) { + if (!moveOnly && hasMovedMin && allowReduce) { + p.size = Math.min(p.size, p.get(dim == 'y' ? 'fitsize' : 'maxsize')); + } else if (p.get('wsize') < p.get('fitsize')) { + p.pos = p.scroll + p.clientSize - p.marginMax - p.get('wsize'); + } else { // image larger than viewport + p.pos = p.scroll + p.marginMin; + if (!moveOnly && allowReduce) p.size = p.get(dim == 'y' ? 'fitsize' : 'maxsize'); + } + } + + if (!moveOnly && p.size < p.minSize) { + p.size = p.minSize; + allowReduce = false; + } + + + } else if (p.justify == 'max') { + p.pos = Math.floor(p.pos - p.size + p.t); + } + + + if (p.pos < p.marginMin) { + var tmpMin = p.pos; + p.pos = p.marginMin; + + if (allowReduce && !moveOnly) p.size = p.size - (p.pos - tmpMin); + + } +}, + +correctRatio : function(ratio) { + var x = this.x, + y = this.y, + changed = false, + xSize = Math.min(x.full, x.size), + ySize = Math.min(y.full, y.size), + useBox = (this.useBox || hs.padToMinWidth); + + if (xSize / ySize > ratio) { // width greater + xSize = ySize * ratio; + if (xSize < x.minSize) { // below minWidth + xSize = x.minSize; + ySize = xSize / ratio; + } + changed = true; + + } else if (xSize / ySize < ratio) { // height greater + ySize = xSize / ratio; + changed = true; + } + + if (hs.padToMinWidth && x.full < x.minSize) { + x.imgSize = x.full; + y.size = y.imgSize = y.full; + } else if (this.useBox) { + x.imgSize = xSize; + y.imgSize = ySize; + } else { + x.size = xSize; + y.size = ySize; + } + changed = this.fitOverlayBox(this.useBox ? null : ratio, changed); + if (useBox && y.size < y.imgSize) { + y.imgSize = y.size; + x.imgSize = y.size * ratio; + } + if (changed || useBox) { + x.pos = x.tpos - x.cb + x.tb; + x.minSize = x.size; + this.justify(x, true); + + y.pos = y.tpos - y.cb + y.tb; + y.minSize = y.size; + this.justify(y, true); + if (this.overlayBox) this.sizeOverlayBox(); + } + + +}, +fitOverlayBox : function(ratio, changed) { + var x = this.x, y = this.y; + if (this.overlayBox) { + while (y.size > this.minHeight && x.size > this.minWidth + && y.get('wsize') > y.get('fitsize')) { + y.size -= 10; + if (ratio) x.size = y.size * ratio; + this.sizeOverlayBox(0, 1); + changed = true; + } + } + return changed; +}, + +show : function () { + var x = this.x, y = this.y; + this.doShowHide('hidden'); + hs.fireEvent(this, 'onBeforeExpand'); + if (this.slideshow && this.slideshow.thumbstrip) this.slideshow.thumbstrip.selectThumb(); + + // Apply size change + this.changeSize( + 1, { + wrapper: { + width : x.get('wsize'), + height : y.get('wsize'), + left: x.pos, + top: y.pos + }, + content: { + left: x.p1 + x.get('imgPad'), + top: y.p1 + y.get('imgPad'), + width:x.imgSize ||x.size, + height:y.imgSize ||y.size + } + }, + hs.expandDuration + ); +}, + +changeSize : function(up, to, dur) { + // transition + var trans = this.transitions, + other = up ? (this.last ? this.last.a : null) : hs.upcoming, + t = (trans[1] && other + && hs.getParam(other, 'transitions')[1] == trans[1]) ? + trans[1] : trans[0]; + + if (this[t] && t != 'expand') { + this[t](up, to); + return; + } + + if (this.outline && !this.outlineWhileAnimating) { + if (up) this.outline.setPosition(); + else this.outline.destroy(); + } + + + if (!up) this.destroyOverlays(); + + var exp = this, + x = exp.x, + y = exp.y, + easing = this.easing; + if (!up) easing = this.easingClose || easing; + var after = up ? + function() { + + if (exp.outline) exp.outline.table.style.visibility = "visible"; + setTimeout(function() { + exp.afterExpand(); + }, 50); + } : + function() { + exp.afterClose(); + }; + if (up) hs.setStyles( this.wrapper, { + width: x.t +'px', + height: y.t +'px' + }); + if (this.fadeInOut) { + hs.setStyles(this.wrapper, { opacity: up ? 0 : 1 }); + hs.extend(to.wrapper, { opacity: up }); + } + hs.animate( this.wrapper, to.wrapper, { + duration: dur, + easing: easing, + step: function(val, args) { + if (exp.outline && exp.outlineWhileAnimating && args.prop == 'top') { + var fac = up ? args.pos : 1 - args.pos; + var pos = { + w: x.t + (x.get('wsize') - x.t) * fac, + h: y.t + (y.get('wsize') - y.t) * fac, + x: x.tpos + (x.pos - x.tpos) * fac, + y: y.tpos + (y.pos - y.tpos) * fac + }; + exp.outline.setPosition(pos, 0, 1); + } + } + }); + hs.animate( this.content, to.content, dur, easing, after); + if (up) { + this.wrapper.style.visibility = 'visible'; + this.content.style.visibility = 'visible'; + this.a.className += ' highslide-active-anchor'; + } +}, + + + +fade : function(up, to) { + this.outlineWhileAnimating = false; + var exp = this, t = up ? hs.expandDuration : 0; + + if (up) { + hs.animate(this.wrapper, to.wrapper, 0); + hs.setStyles(this.wrapper, { opacity: 0, visibility: 'visible' }); + hs.animate(this.content, to.content, 0); + this.content.style.visibility = 'visible'; + + hs.animate(this.wrapper, { opacity: 1 }, t, null, + function() { exp.afterExpand(); }); + } + + if (this.outline) { + this.outline.table.style.zIndex = this.wrapper.style.zIndex; + var dir = up || -1, + offset = this.outline.offset, + startOff = up ? 3 : offset, + endOff = up? offset : 3; + for (var i = startOff; dir * i <= dir * endOff; i += dir, t += 25) { + (function() { + var o = up ? endOff - i : startOff - i; + setTimeout(function() { + exp.outline.setPosition(0, o, 1); + }, t); + })(); + } + } + + + if (up) {}//setTimeout(function() { exp.afterExpand(); }, t+50); + else { + setTimeout( function() { + if (exp.outline) exp.outline.destroy(exp.preserveContent); + + exp.destroyOverlays(); + + hs.animate( exp.wrapper, { opacity: 0 }, hs.restoreDuration, null, function(){ + exp.afterClose(); + }); + }, t); + } +}, +crossfade : function (up, to, from) { + if (!up) return; + var exp = this, + last = this.last, + x = this.x, + y = this.y, + lastX = last.x, + lastY = last.y, + wrapper = this.wrapper, + content = this.content, + overlayBox = this.overlayBox; + hs.removeEventListener(document, 'mousemove', hs.dragHandler); + + hs.setStyles(content, { + width: (x.imgSize || x.size) +'px', + height: (y.imgSize || y.size) +'px' + }); + if (overlayBox) overlayBox.style.overflow = 'visible'; + this.outline = last.outline; + if (this.outline) this.outline.exp = exp; + last.outline = null; + var fadeBox = hs.createElement('div', { + className: 'highslide-'+ this.contentType + }, { + position: 'absolute', + zIndex: 4, + overflow: 'hidden', + display: 'none' + } + ); + var names = { oldImg: last, newImg: this }; + for (var n in names) { + this[n] = names[n].content.cloneNode(1); + hs.setStyles(this[n], { + position: 'absolute', + border: 0, + visibility: 'visible' + }); + fadeBox.appendChild(this[n]); + } + wrapper.appendChild(fadeBox); + if (overlayBox) { + overlayBox.className = ''; + wrapper.appendChild(overlayBox); + } + fadeBox.style.display = ''; + last.content.style.display = 'none'; + + + if (hs.safari && hs.uaVersion < 525) { + this.wrapper.style.visibility = 'visible'; + } + hs.animate(wrapper, { + width: x.size + }, { + duration: hs.transitionDuration, + step: function(val, args) { + var pos = args.pos, + invPos = 1 - pos; + var prop, + size = {}, + props = ['pos', 'size', 'p1', 'p2']; + for (var n in props) { + prop = props[n]; + size['x'+ prop] = Math.round(invPos * lastX[prop] + pos * x[prop]); + size['y'+ prop] = Math.round(invPos * lastY[prop] + pos * y[prop]); + size.ximgSize = Math.round( + invPos * (lastX.imgSize || lastX.size) + pos * (x.imgSize || x.size)); + size.ximgPad = Math.round(invPos * lastX.get('imgPad') + pos * x.get('imgPad')); + size.yimgSize = Math.round( + invPos * (lastY.imgSize || lastY.size) + pos * (y.imgSize || y.size)); + size.yimgPad = Math.round(invPos * lastY.get('imgPad') + pos * y.get('imgPad')); + } + if (exp.outline) exp.outline.setPosition({ + x: size.xpos, + y: size.ypos, + w: size.xsize + size.xp1 + size.xp2 + 2 * x.cb, + h: size.ysize + size.yp1 + size.yp2 + 2 * y.cb + }); + last.wrapper.style.clip = 'rect(' + + (size.ypos - lastY.pos)+'px, ' + + (size.xsize + size.xp1 + size.xp2 + size.xpos + 2 * lastX.cb - lastX.pos) +'px, ' + + (size.ysize + size.yp1 + size.yp2 + size.ypos + 2 * lastY.cb - lastY.pos) +'px, ' + + (size.xpos - lastX.pos)+'px)'; + + hs.setStyles(content, { + top: (size.yp1 + y.get('imgPad')) +'px', + left: (size.xp1 + x.get('imgPad')) +'px', + marginTop: (y.pos - size.ypos) +'px', + marginLeft: (x.pos - size.xpos) +'px' + }); + hs.setStyles(wrapper, { + top: size.ypos +'px', + left: size.xpos +'px', + width: (size.xp1 + size.xp2 + size.xsize + 2 * x.cb)+ 'px', + height: (size.yp1 + size.yp2 + size.ysize + 2 * y.cb) + 'px' + }); + hs.setStyles(fadeBox, { + width: (size.ximgSize || size.xsize) + 'px', + height: (size.yimgSize || size.ysize) +'px', + left: (size.xp1 + size.ximgPad) +'px', + top: (size.yp1 + size.yimgPad) +'px', + visibility: 'visible' + }); + + hs.setStyles(exp.oldImg, { + top: (lastY.pos - size.ypos + lastY.p1 - size.yp1 + lastY.get('imgPad') - size.yimgPad)+'px', + left: (lastX.pos - size.xpos + lastX.p1 - size.xp1 + lastX.get('imgPad') - size.ximgPad)+'px' + }); + + hs.setStyles(exp.newImg, { + opacity: pos, + top: (y.pos - size.ypos + y.p1 - size.yp1 + y.get('imgPad') - size.yimgPad) +'px', + left: (x.pos - size.xpos + x.p1 - size.xp1 + x.get('imgPad') - size.ximgPad) +'px' + }); + if (overlayBox) hs.setStyles(overlayBox, { + width: size.xsize + 'px', + height: size.ysize +'px', + left: (size.xp1 + x.cb) +'px', + top: (size.yp1 + y.cb) +'px' + }); + }, + complete: function () { + wrapper.style.visibility = content.style.visibility = 'visible'; + content.style.display = 'block'; + hs.discardElement(fadeBox); + exp.afterExpand(); + last.afterClose(); + exp.last = null; + } + + }); +}, +reuseOverlay : function(o, el) { + if (!this.last) return false; + for (var i = 0; i < this.last.overlays.length; i++) { + var oDiv = hs.$('hsId'+ this.last.overlays[i]); + if (oDiv && oDiv.hsId == o.hsId) { + this.genOverlayBox(); + oDiv.reuse = this.key; + hs.push(this.overlays, this.last.overlays[i]); + return true; + } + } + return false; +}, + + +afterExpand : function() { + this.isExpanded = true; + this.focus(); + if (this.dimmingOpacity) hs.dim(this); + if (hs.upcoming && hs.upcoming == this.a) hs.upcoming = null; + this.prepareNextOutline(); + var p = hs.page, mX = hs.mouse.x + p.scrollLeft, mY = hs.mouse.y + p.scrollTop; + this.mouseIsOver = this.x.pos < mX && mX < this.x.pos + this.x.get('wsize') + && this.y.pos < mY && mY < this.y.pos + this.y.get('wsize'); + if (this.overlayBox) this.showOverlays(); + hs.fireEvent(this, 'onAfterExpand'); + +}, + + +prepareNextOutline : function() { + var key = this.key; + var outlineType = this.outlineType; + new hs.Outline(outlineType, + function () { try { hs.expanders[key].preloadNext(); } catch (e) {} }); +}, + + +preloadNext : function() { + var next = this.getAdjacentAnchor(1); + if (next && next.onclick.toString().match(/hs\.expand/)) + var img = hs.createElement('img', { src: hs.getSrc(next) }); +}, + + +getAdjacentAnchor : function(op) { + var current = this.getAnchorIndex(), as = hs.anchors.groups[this.slideshowGroup || 'none']; + if (as && !as[current + op] && this.slideshow && this.slideshow.repeat) { + if (op == 1) return as[0]; + else if (op == -1) return as[as.length-1]; + } + return (as && as[current + op]) || null; +}, + +getAnchorIndex : function() { + var arr = hs.getAnchors().groups[this.slideshowGroup || 'none']; + if (arr) for (var i = 0; i < arr.length; i++) { + if (arr[i] == this.a) return i; + } + return null; +}, + + +getNumber : function() { + if (this[this.numberPosition]) { + var arr = hs.anchors.groups[this.slideshowGroup || 'none']; + if (arr) { + var s = hs.lang.number.replace('%1', this.getAnchorIndex() + 1).replace('%2', arr.length); + this[this.numberPosition].innerHTML = + '
    '+ s +'
    '+ this[this.numberPosition].innerHTML; + } + } +}, +initSlideshow : function() { + if (!this.last) { + for (var i = 0; i < hs.slideshows.length; i++) { + var ss = hs.slideshows[i], sg = ss.slideshowGroup; + if (typeof sg == 'undefined' || sg === null || sg === this.slideshowGroup) + this.slideshow = new hs.Slideshow(this.key, ss); + } + } else { + this.slideshow = this.last.slideshow; + } + var ss = this.slideshow; + if (!ss) return; + var key = ss.expKey = this.key; + + ss.checkFirstAndLast(); + ss.disable('full-expand'); + if (ss.controls) { + this.createOverlay(hs.extend(ss.overlayOptions || {}, { + overlayId: ss.controls, + hsId: 'controls', + zIndex: 5 + })); + } + if (ss.thumbstrip) ss.thumbstrip.add(this); + if (!this.last && this.autoplay) ss.play(true); + if (ss.autoplay) { + ss.autoplay = setTimeout(function() { + hs.next(key); + }, (ss.interval || 500)); + } +}, + +cancelLoading : function() { + hs.discardElement (this.wrapper); + hs.expanders[this.key] = null; + if (hs.upcoming == this.a) hs.upcoming = null; + hs.undim(this.key); + if (this.loading) hs.loading.style.left = '-9999px'; + hs.fireEvent(this, 'onHideLoading'); +}, + +writeCredits : function () { + if (this.credits) return; + this.credits = hs.createElement('a', { + href: hs.creditsHref, + target: hs.creditsTarget, + className: 'highslide-credits', + innerHTML: hs.lang.creditsText, + title: hs.lang.creditsTitle + }); + this.createOverlay({ + overlayId: this.credits, + position: this.creditsPosition || 'top left', + hsId: 'credits' + }); +}, + +getInline : function(types, addOverlay) { + for (var i = 0; i < types.length; i++) { + var type = types[i], s = null; + if (type == 'caption' && !hs.fireEvent(this, 'onBeforeGetCaption')) return; + else if (type == 'heading' && !hs.fireEvent(this, 'onBeforeGetHeading')) return; + if (!this[type +'Id'] && this.thumbsUserSetId) + this[type +'Id'] = type +'-for-'+ this.thumbsUserSetId; + if (this[type +'Id']) this[type] = hs.getNode(this[type +'Id']); + if (!this[type] && !this[type +'Text'] && this[type +'Eval']) try { + s = eval(this[type +'Eval']); + } catch (e) {} + if (!this[type] && this[type +'Text']) { + s = this[type +'Text']; + } + if (!this[type] && !s) { + this[type] = hs.getNode(this.a['_'+ type + 'Id']); + if (!this[type]) { + var next = this.a.nextSibling; + while (next && !hs.isHsAnchor(next)) { + if ((new RegExp('highslide-'+ type)).test(next.className || null)) { + if (!next.id) this.a['_'+ type + 'Id'] = next.id = 'hsId'+ hs.idCounter++; + this[type] = hs.getNode(next.id); + break; + } + next = next.nextSibling; + } + } + } + if (!this[type] && !s && this.numberPosition == type) s = '\n'; + + if (!this[type] && s) this[type] = hs.createElement('div', + { className: 'highslide-'+ type, innerHTML: s } ); + + if (addOverlay && this[type]) { + var o = { position: (type == 'heading') ? 'above' : 'below' }; + for (var x in this[type+'Overlay']) o[x] = this[type+'Overlay'][x]; + o.overlayId = this[type]; + this.createOverlay(o); + } + } +}, + + +// on end move and resize +doShowHide : function(visibility) { + if (hs.hideSelects) this.showHideElements('SELECT', visibility); + if (hs.hideIframes) this.showHideElements('IFRAME', visibility); + if (hs.geckoMac) this.showHideElements('*', visibility); +}, +showHideElements : function (tagName, visibility) { + var els = document.getElementsByTagName(tagName); + var prop = tagName == '*' ? 'overflow' : 'visibility'; + for (var i = 0; i < els.length; i++) { + if (prop == 'visibility' || (document.defaultView.getComputedStyle( + els[i], "").getPropertyValue('overflow') == 'auto' + || els[i].getAttribute('hidden-by') != null)) { + var hiddenBy = els[i].getAttribute('hidden-by'); + if (visibility == 'visible' && hiddenBy) { + hiddenBy = hiddenBy.replace('['+ this.key +']', ''); + els[i].setAttribute('hidden-by', hiddenBy); + if (!hiddenBy) els[i].style[prop] = els[i].origProp; + } else if (visibility == 'hidden') { // hide if behind + var elPos = hs.getPosition(els[i]); + elPos.w = els[i].offsetWidth; + elPos.h = els[i].offsetHeight; + if (!this.dimmingOpacity) { // hide all if dimming + + var clearsX = (elPos.x + elPos.w < this.x.get('opos') + || elPos.x > this.x.get('opos') + this.x.get('osize')); + var clearsY = (elPos.y + elPos.h < this.y.get('opos') + || elPos.y > this.y.get('opos') + this.y.get('osize')); + } + var wrapperKey = hs.getWrapperKey(els[i]); + if (!clearsX && !clearsY && wrapperKey != this.key) { // element falls behind image + if (!hiddenBy) { + els[i].setAttribute('hidden-by', '['+ this.key +']'); + els[i].origProp = els[i].style[prop]; + els[i].style[prop] = 'hidden'; + + } else if (hiddenBy.indexOf('['+ this.key +']') == -1) { + els[i].setAttribute('hidden-by', hiddenBy + '['+ this.key +']'); + } + } else if ((hiddenBy == '['+ this.key +']' || hs.focusKey == wrapperKey) + && wrapperKey != this.key) { // on move + els[i].setAttribute('hidden-by', ''); + els[i].style[prop] = els[i].origProp || ''; + } else if (hiddenBy && hiddenBy.indexOf('['+ this.key +']') > -1) { + els[i].setAttribute('hidden-by', hiddenBy.replace('['+ this.key +']', '')); + } + + } + } + } +}, + +focus : function() { + this.wrapper.style.zIndex = hs.zIndexCounter += 2; + // blur others + for (var i = 0; i < hs.expanders.length; i++) { + if (hs.expanders[i] && i == hs.focusKey) { + var blurExp = hs.expanders[i]; + blurExp.content.className += ' highslide-'+ blurExp.contentType +'-blur'; + blurExp.content.style.cursor = hs.ieLt7 ? 'hand' : 'pointer'; + blurExp.content.title = hs.lang.focusTitle; + hs.fireEvent(blurExp, 'onBlur'); + } + } + + // focus this + if (this.outline) this.outline.table.style.zIndex + = this.wrapper.style.zIndex - 1; + this.content.className = 'highslide-'+ this.contentType; + this.content.title = hs.lang.restoreTitle; + + if (hs.restoreCursor) { + hs.styleRestoreCursor = window.opera ? 'pointer' : 'url('+ hs.graphicsDir + hs.restoreCursor +'), pointer'; + if (hs.ieLt7 && hs.uaVersion < 6) hs.styleRestoreCursor = 'hand'; + this.content.style.cursor = hs.styleRestoreCursor; + } + + hs.focusKey = this.key; + hs.addEventListener(document, window.opera ? 'keypress' : 'keydown', hs.keyHandler); + hs.fireEvent(this, 'onFocus'); +}, +moveTo: function(x, y) { + this.x.setPos(x); + this.y.setPos(y); +}, +resize : function (e) { + var w, h, r = e.width / e.height; + w = Math.max(e.width + e.dX, Math.min(this.minWidth, this.x.full)); + if (this.isImage && Math.abs(w - this.x.full) < 12) w = this.x.full; + h = w / r; + if (h < Math.min(this.minHeight, this.y.full)) { + h = Math.min(this.minHeight, this.y.full); + if (this.isImage) w = h * r; + } + this.resizeTo(w, h); +}, +resizeTo: function(w, h) { + this.y.setSize(h); + this.x.setSize(w); + this.wrapper.style.height = this.y.get('wsize') +'px'; +}, + +close : function() { + if (this.isClosing || !this.isExpanded) return; + if (this.transitions[1] == 'crossfade' && hs.upcoming) { + hs.getExpander(hs.upcoming).cancelLoading(); + hs.upcoming = null; + } + if (!hs.fireEvent(this, 'onBeforeClose')) return; + this.isClosing = true; + if (this.slideshow && !hs.upcoming) this.slideshow.pause(); + + hs.removeEventListener(document, window.opera ? 'keypress' : 'keydown', hs.keyHandler); + + try { + this.content.style.cursor = 'default'; + this.changeSize( + 0, { + wrapper: { + width : this.x.t, + height : this.y.t, + left: this.x.tpos - this.x.cb + this.x.tb, + top: this.y.tpos - this.y.cb + this.y.tb + }, + content: { + left: 0, + top: 0, + width: this.x.t, + height: this.y.t + } + }, hs.restoreDuration + ); + } catch (e) { this.afterClose(); } +}, + +createOverlay : function (o) { + var el = o.overlayId, + relToVP = (o.relativeTo == 'viewport' && !/panel$/.test(o.position)); + if (typeof el == 'string') el = hs.getNode(el); + if (o.html) el = hs.createElement('div', { innerHTML: o.html }); + if (!el || typeof el == 'string') return; + if (!hs.fireEvent(this, 'onCreateOverlay', { overlay: el })) return; + el.style.display = 'block'; + o.hsId = o.hsId || o.overlayId; + if (this.transitions[1] == 'crossfade' && this.reuseOverlay(o, el)) return; + this.genOverlayBox(); + var width = o.width && /^[0-9]+(px|%)$/.test(o.width) ? o.width : 'auto'; + if (/^(left|right)panel$/.test(o.position) && !/^[0-9]+px$/.test(o.width)) width = '200px'; + var overlay = hs.createElement( + 'div', { + id: 'hsId'+ hs.idCounter++, + hsId: o.hsId + }, { + position: 'absolute', + visibility: 'hidden', + width: width, + direction: hs.lang.cssDirection || '', + opacity: 0 + }, + relToVP ? hs.viewport :this.overlayBox, + true + ); + if (relToVP) overlay.hsKey = this.key; + + overlay.appendChild(el); + hs.extend(overlay, { + opacity: 1, + offsetX: 0, + offsetY: 0, + dur: (o.fade === 0 || o.fade === false || (o.fade == 2 && hs.ie)) ? 0 : 250 + }); + hs.extend(overlay, o); + + + if (this.gotOverlays) { + this.positionOverlay(overlay); + if (!overlay.hideOnMouseOut || this.mouseIsOver) + hs.animate(overlay, { opacity: overlay.opacity }, overlay.dur); + } + hs.push(this.overlays, hs.idCounter - 1); +}, +positionOverlay : function(overlay) { + var p = overlay.position || 'middle center', + relToVP = (overlay.relativeTo == 'viewport'), + offX = overlay.offsetX, + offY = overlay.offsetY; + if (relToVP) { + hs.viewport.style.display = 'block'; + overlay.hsKey = this.key; + if (overlay.offsetWidth > overlay.parentNode.offsetWidth) + overlay.style.width = '100%'; + } else + if (overlay.parentNode != this.overlayBox) this.overlayBox.appendChild(overlay); + if (/left$/.test(p)) overlay.style.left = offX +'px'; + + if (/center$/.test(p)) hs.setStyles (overlay, { + left: '50%', + marginLeft: (offX - Math.round(overlay.offsetWidth / 2)) +'px' + }); + + if (/right$/.test(p)) overlay.style.right = - offX +'px'; + + if (/^leftpanel$/.test(p)) { + hs.setStyles(overlay, { + right: '100%', + marginRight: this.x.cb +'px', + top: - this.y.cb +'px', + bottom: - this.y.cb +'px', + overflow: 'auto' + }); + this.x.p1 = overlay.offsetWidth; + + } else if (/^rightpanel$/.test(p)) { + hs.setStyles(overlay, { + left: '100%', + marginLeft: this.x.cb +'px', + top: - this.y.cb +'px', + bottom: - this.y.cb +'px', + overflow: 'auto' + }); + this.x.p2 = overlay.offsetWidth; + } + var parOff = overlay.parentNode.offsetHeight; + overlay.style.height = 'auto'; + if (relToVP && overlay.offsetHeight > parOff) + overlay.style.height = hs.ieLt7 ? parOff +'px' : '100%'; + + if (/^top/.test(p)) overlay.style.top = offY +'px'; + if (/^middle/.test(p)) hs.setStyles (overlay, { + top: '50%', + marginTop: (offY - Math.round(overlay.offsetHeight / 2)) +'px' + }); + if (/^bottom/.test(p)) overlay.style.bottom = - offY +'px'; + if (/^above$/.test(p)) { + hs.setStyles(overlay, { + left: (- this.x.p1 - this.x.cb) +'px', + right: (- this.x.p2 - this.x.cb) +'px', + bottom: '100%', + marginBottom: this.y.cb +'px', + width: 'auto' + }); + this.y.p1 = overlay.offsetHeight; + + } else if (/^below$/.test(p)) { + hs.setStyles(overlay, { + position: 'relative', + left: (- this.x.p1 - this.x.cb) +'px', + right: (- this.x.p2 - this.x.cb) +'px', + top: '100%', + marginTop: this.y.cb +'px', + width: 'auto' + }); + this.y.p2 = overlay.offsetHeight; + overlay.style.position = 'absolute'; + } +}, + +getOverlays : function() { + this.getInline(['heading', 'caption'], true); + this.getNumber(); + if (this.caption) hs.fireEvent(this, 'onAfterGetCaption'); + if (this.heading) hs.fireEvent(this, 'onAfterGetHeading'); + if (this.heading && this.dragByHeading) this.heading.className += ' highslide-move'; + if (hs.showCredits) this.writeCredits(); + for (var i = 0; i < hs.overlays.length; i++) { + var o = hs.overlays[i], tId = o.thumbnailId, sg = o.slideshowGroup; + if ((!tId && !sg) || (tId && tId == this.thumbsUserSetId) + || (sg && sg === this.slideshowGroup)) { + this.createOverlay(o); + } + } + var os = []; + for (var i = 0; i < this.overlays.length; i++) { + var o = hs.$('hsId'+ this.overlays[i]); + if (/panel$/.test(o.position)) this.positionOverlay(o); + else hs.push(os, o); + } + for (var i = 0; i < os.length; i++) this.positionOverlay(os[i]); + this.gotOverlays = true; +}, +genOverlayBox : function() { + if (!this.overlayBox) this.overlayBox = hs.createElement ( + 'div', { + className: this.wrapperClassName + }, { + position : 'absolute', + width: (this.x.size || (this.useBox ? this.width : null) + || this.x.full) +'px', + height: (this.y.size || this.y.full) +'px', + visibility : 'hidden', + overflow : 'hidden', + zIndex : hs.ie ? 4 : 'auto' + }, + hs.container, + true + ); +}, +sizeOverlayBox : function(doWrapper, doPanels) { + var overlayBox = this.overlayBox, + x = this.x, + y = this.y; + hs.setStyles( overlayBox, { + width: x.size +'px', + height: y.size +'px' + }); + if (doWrapper || doPanels) { + for (var i = 0; i < this.overlays.length; i++) { + var o = hs.$('hsId'+ this.overlays[i]); + var ie6 = (hs.ieLt7 || document.compatMode == 'BackCompat'); + if (o && /^(above|below)$/.test(o.position)) { + if (ie6) { + o.style.width = (overlayBox.offsetWidth + 2 * x.cb + + x.p1 + x.p2) +'px'; + } + y[o.position == 'above' ? 'p1' : 'p2'] = o.offsetHeight; + } + if (o && ie6 && /^(left|right)panel$/.test(o.position)) { + o.style.height = (overlayBox.offsetHeight + 2* y.cb) +'px'; + } + } + } + if (doWrapper) { + hs.setStyles(this.content, { + top: y.p1 +'px' + }); + hs.setStyles(overlayBox, { + top: (y.p1 + y.cb) +'px' + }); + } +}, + +showOverlays : function() { + var b = this.overlayBox; + b.className = ''; + hs.setStyles(b, { + top: (this.y.p1 + this.y.cb) +'px', + left: (this.x.p1 + this.x.cb) +'px', + overflow : 'visible' + }); + if (hs.safari) b.style.visibility = 'visible'; + this.wrapper.appendChild (b); + for (var i = 0; i < this.overlays.length; i++) { + var o = hs.$('hsId'+ this.overlays[i]); + o.style.zIndex = o.zIndex || 4; + if (!o.hideOnMouseOut || this.mouseIsOver) { + o.style.visibility = 'visible'; + hs.setStyles(o, { visibility: 'visible', display: '' }); + hs.animate(o, { opacity: o.opacity }, o.dur); + } + } +}, + +destroyOverlays : function() { + if (!this.overlays.length) return; + if (this.slideshow) { + var c = this.slideshow.controls; + if (c && hs.getExpander(c) == this) c.parentNode.removeChild(c); + } + for (var i = 0; i < this.overlays.length; i++) { + var o = hs.$('hsId'+ this.overlays[i]); + if (o && o.parentNode == hs.viewport && hs.getExpander(o) == this) hs.discardElement(o); + } + hs.discardElement(this.overlayBox); +}, + + + +createFullExpand : function () { + if (this.slideshow && this.slideshow.controls) { + this.slideshow.enable('full-expand'); + return; + } + this.fullExpandLabel = hs.createElement( + 'a', { + href: 'javascript:hs.expanders['+ this.key +'].doFullExpand();', + title: hs.lang.fullExpandTitle, + className: 'highslide-full-expand' + } + ); + if (!hs.fireEvent(this, 'onCreateFullExpand')) return; + + this.createOverlay({ + overlayId: this.fullExpandLabel, + position: hs.fullExpandPosition, + hideOnMouseOut: true, + opacity: hs.fullExpandOpacity + }); +}, + +doFullExpand : function () { + try { + if (!hs.fireEvent(this, 'onDoFullExpand')) return; + if (this.fullExpandLabel) hs.discardElement(this.fullExpandLabel); + + this.focus(); + var xSize = this.x.size, + ySize = this.y.size; + this.resizeTo(this.x.full, this.y.full); + + var xpos = this.x.pos - (this.x.size - xSize) / 2; + if (xpos < hs.marginLeft) xpos = hs.marginLeft; + + var ypos = this.y.pos - (this.y.size - ySize) / 2; + if (ypos < hs.marginTop) ypos = hs.marginTop; + + this.moveTo(xpos, ypos); + this.doShowHide('hidden'); + + } catch (e) { + this.error(e); + } +}, + + +afterClose : function () { + this.a.className = this.a.className.replace('highslide-active-anchor', ''); + + this.doShowHide('visible'); + if (this.outline && this.outlineWhileAnimating) this.outline.destroy(); + + hs.discardElement(this.wrapper); + this.destroyOverlays(); + if (!hs.viewport.childNodes.length) hs.viewport.style.display = 'none'; + + if (this.dimmingOpacity) hs.undim(this.key); + hs.fireEvent(this, 'onAfterClose'); + hs.expanders[this.key] = null; + hs.reOrder(); +} + +}; + + +hs.Slideshow = function (expKey, options) { + if (hs.dynamicallyUpdateAnchors !== false) hs.updateAnchors(); + this.expKey = expKey; + for (var x in options) this[x] = options[x]; + if (this.useControls) this.getControls(); + if (this.thumbstrip) this.thumbstrip = hs.Thumbstrip(this); +}; +hs.Slideshow.prototype = { +getControls: function() { + this.controls = hs.createElement('div', { innerHTML: hs.replaceLang(hs.skin.controls) }, + null, hs.container); + + var buttons = ['play', 'pause', 'previous', 'next', 'move', 'full-expand', 'close']; + this.btn = {}; + var pThis = this; + for (var i = 0; i < buttons.length; i++) { + this.btn[buttons[i]] = hs.getElementByClass(this.controls, 'li', 'highslide-'+ buttons[i]); + this.enable(buttons[i]); + } + this.btn.pause.style.display = 'none'; + //this.disable('full-expand'); +}, +checkFirstAndLast: function() { + if (this.repeat || !this.controls) return; + var exp = hs.expanders[this.expKey], + cur = exp.getAnchorIndex(), + re = /disabled$/; + if (cur == 0) + this.disable('previous'); + else if (re.test(this.btn.previous.getElementsByTagName('a')[0].className)) + this.enable('previous'); + if (cur + 1 == hs.anchors.groups[exp.slideshowGroup || 'none'].length) { + this.disable('next'); + this.disable('play'); + } else if (re.test(this.btn.next.getElementsByTagName('a')[0].className)) { + this.enable('next'); + this.enable('play'); + } +}, +enable: function(btn) { + if (!this.btn) return; + var sls = this, a = this.btn[btn].getElementsByTagName('a')[0], re = /disabled$/; + a.onclick = function() { + sls[btn](); + return false; + }; + if (re.test(a.className)) a.className = a.className.replace(re, ''); +}, +disable: function(btn) { + if (!this.btn) return; + var a = this.btn[btn].getElementsByTagName('a')[0]; + a.onclick = function() { return false; }; + if (!/disabled$/.test(a.className)) a.className += ' disabled'; +}, +hitSpace: function() { + if (this.autoplay) this.pause(); + else this.play(); +}, +play: function(wait) { + if (this.btn) { + this.btn.play.style.display = 'none'; + this.btn.pause.style.display = ''; + } + + this.autoplay = true; + if (!wait) hs.next(this.expKey); +}, +pause: function() { + if (this.btn) { + this.btn.pause.style.display = 'none'; + this.btn.play.style.display = ''; + } + + clearTimeout(this.autoplay); + this.autoplay = null; +}, +previous: function() { + this.pause(); + hs.previous(this.btn.previous); +}, +next: function() { + this.pause(); + hs.next(this.btn.next); +}, +move: function() {}, +'full-expand': function() { + hs.getExpander().doFullExpand(); +}, +close: function() { + hs.close(this.btn.close); +} +}; +hs.Thumbstrip = function(slideshow) { + function add (exp) { + hs.extend(options || {}, { + overlayId: dom, + hsId: 'thumbstrip', + className: 'highslide-thumbstrip-'+ mode +'-overlay ' + (options.className || '') + }); + if (hs.ieLt7) options.fade = 0; + exp.createOverlay(options); + hs.setStyles(dom.parentNode, { overflow: 'hidden' }); + }; + + function scroll (delta) { + selectThumb(undefined, Math.round(delta * dom[isX ? 'offsetWidth' : 'offsetHeight'] * 0.7)); + }; + + function selectThumb (i, scrollBy) { + if (i === undefined) for (var j = 0; j < group.length; j++) { + if (group[j] == hs.expanders[slideshow.expKey].a) { + i = j; + break; + } + } + if (i === undefined) return; + var as = dom.getElementsByTagName('a'), + active = as[i], + cell = active.parentNode, + left = isX ? 'Left' : 'Top', + right = isX ? 'Right' : 'Bottom', + width = isX ? 'Width' : 'Height', + offsetLeft = 'offset' + left, + offsetWidth = 'offset' + width, + overlayWidth = div.parentNode.parentNode[offsetWidth], + minTblPos = overlayWidth - table[offsetWidth], + curTblPos = parseInt(table.style[isX ? 'left' : 'top']) || 0, + tblPos = curTblPos, + mgnRight = 20; + if (scrollBy !== undefined) { + tblPos = curTblPos - scrollBy; + + if (minTblPos > 0) minTblPos = 0; + if (tblPos > 0) tblPos = 0; + if (tblPos < minTblPos) tblPos = minTblPos; + + + } else { + for (var j = 0; j < as.length; j++) as[j].className = ''; + active.className = 'highslide-active-anchor'; + var activeLeft = i > 0 ? as[i - 1].parentNode[offsetLeft] : cell[offsetLeft], + activeRight = cell[offsetLeft] + cell[offsetWidth] + + (as[i + 1] ? as[i + 1].parentNode[offsetWidth] : 0); + if (activeRight > overlayWidth - curTblPos) tblPos = overlayWidth - activeRight; + else if (activeLeft < -curTblPos) tblPos = -activeLeft; + } + var markerPos = cell[offsetLeft] + (cell[offsetWidth] - marker[offsetWidth]) / 2 + tblPos; + hs.animate(table, isX ? { left: tblPos } : { top: tblPos }, null, 'easeOutQuad'); + hs.animate(marker, isX ? { left: markerPos } : { top: markerPos }, null, 'easeOutQuad'); + scrollUp.style.display = tblPos < 0 ? 'block' : 'none'; + scrollDown.style.display = (tblPos > minTblPos) ? 'block' : 'none'; + + }; + + + // initialize + var group = hs.anchors.groups[hs.expanders[slideshow.expKey].slideshowGroup || 'none'], + options = slideshow.thumbstrip, + mode = options.mode || 'horizontal', + floatMode = (mode == 'float'), + tree = floatMode ? ['div', 'ul', 'li', 'span'] : ['table', 'tbody', 'tr', 'td'], + isX = (mode == 'horizontal'), + dom = hs.createElement('div', { + className: 'highslide-thumbstrip highslide-thumbstrip-'+ mode, + innerHTML: + '
    '+ + '<'+ tree[0] +'><'+ tree[1] +'>
    '+ + '
    '+ + '
    '+ + '
    ' + }, { + display: 'none' + }, hs.container), + domCh = dom.childNodes, + div = domCh[0], + scrollUp = domCh[1], + scrollDown = domCh[2], + marker = domCh[3], + table = div.firstChild, + tbody = dom.getElementsByTagName(tree[1])[0], + tr; + for (var i = 0; i < group.length; i++) { + if (i == 0 || !isX) tr = hs.createElement(tree[2], null, null, tbody); + (function(){ + var a = group[i], + cell = hs.createElement(tree[3], null, null, tr), + pI = i; + hs.createElement('a', { + href: a.href, + title: a.title, + onclick: function() { + if (/highslide-active-anchor/.test(this.className)) return false; + hs.getExpander(this).focus(); + return hs.transit(a); + }, + innerHTML: hs.stripItemFormatter ? hs.stripItemFormatter(a) : a.innerHTML + }, null, cell); + })(); + } + if (!floatMode) { + scrollUp.onclick = function () { scroll(-1); }; + scrollDown.onclick = function() { scroll(1); }; + hs.addEventListener(tbody, document.onmousewheel !== undefined ? + 'mousewheel' : 'DOMMouseScroll', function(e) { + var delta = 0; + e = e || window.event; + if (e.wheelDelta) { + delta = e.wheelDelta/120; + if (hs.opera) delta = -delta; + } else if (e.detail) { + delta = -e.detail/3; + } + if (delta) scroll(-delta * 0.2); + if (e.preventDefault) e.preventDefault(); + e.returnValue = false; + }); + } + + return { + add: add, + selectThumb: selectThumb + } +}; +hs.langDefaults = hs.lang; +// history +var HsExpander = hs.Expander; +if (hs.ie && window == window.top) { + (function () { + try { + document.documentElement.doScroll('left'); + } catch (e) { + setTimeout(arguments.callee, 50); + return; + } + hs.ready(); + })(); +} +hs.addEventListener(document, 'DOMContentLoaded', hs.ready); +hs.addEventListener(window, 'load', hs.ready); + +// set handlers +hs.addEventListener(document, 'ready', function() { + if (hs.expandCursor || hs.dimmingOpacity) { + var style = hs.createElement('style', { type: 'text/css' }, null, + document.getElementsByTagName('HEAD')[0]), + backCompat = document.compatMode == 'BackCompat'; + + + function addRule(sel, dec) { + if (hs.ie && (hs.uaVersion < 9 || backCompat)) { + var last = document.styleSheets[document.styleSheets.length - 1]; + if (typeof(last.addRule) == "object") last.addRule(sel, dec); + } else { + style.appendChild(document.createTextNode(sel + " {" + dec + "}")); + } + } + function fix(prop) { + return 'expression( ( ( ignoreMe = document.documentElement.'+ prop + + ' ? document.documentElement.'+ prop +' : document.body.'+ prop +' ) ) + \'px\' );'; + } + if (hs.expandCursor) addRule ('.highslide img', + 'cursor: url('+ hs.graphicsDir + hs.expandCursor +'), pointer !important;'); + addRule ('.highslide-viewport-size', + hs.ie && (hs.uaVersion < 7 || backCompat) ? + 'position: absolute; '+ + 'left:'+ fix('scrollLeft') + + 'top:'+ fix('scrollTop') + + 'width:'+ fix('clientWidth') + + 'height:'+ fix('clientHeight') : + 'position: fixed; width: 100%; height: 100%; left: 0; top: 0'); + } +}); +hs.addEventListener(window, 'resize', function() { + hs.getPageSize(); + if (hs.viewport) for (var i = 0; i < hs.viewport.childNodes.length; i++) { + var node = hs.viewport.childNodes[i], + exp = hs.getExpander(node); + exp.positionOverlay(node); + if (node.hsId == 'thumbstrip') exp.slideshow.thumbstrip.selectThumb(); + } +}); +hs.addEventListener(document, 'mousemove', function(e) { + hs.mouse = { x: e.clientX, y: e.clientY }; +}); +hs.addEventListener(document, 'mousedown', hs.mouseClickHandler); +hs.addEventListener(document, 'mouseup', hs.mouseClickHandler); + +hs.addEventListener(document, 'ready', hs.getAnchors); +hs.addEventListener(window, 'load', hs.preloadImages); +} diff --git a/highslide/highslide.css b/highslide/highslide.css new file mode 100644 index 0000000..452b862 --- /dev/null +++ b/highslide/highslide.css @@ -0,0 +1,888 @@ +/** +* @file: highslide.css +* @version: 4.1.13 +*/ +.highslide-container div { + font-family: Verdana, Helvetica; + font-size: 10pt; +} +.highslide-container table { + background: none; + table-layout: auto; +} +.highslide { + outline: none; + text-decoration: none; +} +.highslide img { + border: 2px solid silver; +} +.highslide:hover img { + border-color: gray; +} +.highslide-active-anchor img { + visibility: hidden; +} +.highslide-gallery .highslide-active-anchor img { + border-color: black; + visibility: visible; + cursor: default; +} +.highslide-image { + border-width: 2px; + border-style: solid; + border-color: white; +} +.highslide-wrapper, .highslide-outline { + background: white; +} +.glossy-dark { + background: #111; +} + +.highslide-image-blur { +} +.highslide-number { + font-weight: bold; + color: gray; + font-size: .9em; +} +.highslide-caption { + display: none; + font-size: 1em; + padding: 5px; + /*background: white;*/ +} +.highslide-heading { + display: none; + font-weight: bold; + margin: 0.4em; +} +.highslide-dimming { + /*position: absolute;*/ + background: black; +} +a.highslide-full-expand { + background: url(graphics/fullexpand.gif) no-repeat; + display: block; + margin: 0 10px 10px 0; + width: 34px; + height: 34px; +} +.highslide-loading { + display: block; + color: black; + font-size: 9px; + font-weight: bold; + text-transform: uppercase; + text-decoration: none; + padding: 3px; + border: 1px solid white; + background-color: white; + padding-left: 22px; + background-image: url(graphics/loader.white.gif); + background-repeat: no-repeat; + background-position: 3px 1px; +} +a.highslide-credits, +a.highslide-credits i { + padding: 2px; + color: silver; + text-decoration: none; + font-size: 10px; +} +a.highslide-credits:hover, +a.highslide-credits:hover i { + color: white; + background-color: gray; +} +.highslide-move, .highslide-move * { + cursor: move; +} + +.highslide-viewport { + display: none; + position: fixed; + width: 100%; + height: 100%; + z-index: 1; + background: none; + left: 0; + top: 0; +} +.highslide-overlay { + display: none; +} +.hidden-container { + display: none; +} +/* Example of a semitransparent, offset closebutton */ +.closebutton { + position: relative; + top: -15px; + left: 15px; + width: 30px; + height: 30px; + cursor: pointer; + background: url(graphics/close.png); + /* NOTE! For IE6, you also need to update the highslide-ie6.css file. */ +} + +/*****************************************************************************/ +/* Thumbnail boxes for the galleries. */ +/* Remove these if you are not using a gallery. */ +/*****************************************************************************/ +.highslide-gallery ul { + list-style-type: none; + margin: 0; + padding: 0; +} +.highslide-gallery ul li { + display: block; + position: relative; + float: left; + width: 106px; + height: 106px; + margin: 2px; + padding: 0; + line-height: 0; + overflow: hidden; +} +.highslide-gallery ul a { + position: absolute; + top: 50%; + left: 50%; +} +.highslide-gallery ul img { + position: relative; + top: -50%; + left: -50%; +} +html>/**/body .highslide-gallery ul li { + display: table; + text-align: center; +} +html>/**/body .highslide-gallery ul li { + text-align: center; +} +html>/**/body .highslide-gallery ul a { + position: static; + display: table-cell; + vertical-align: middle; +} +html>/**/body .highslide-gallery ul img { + position: static; +} + +/*****************************************************************************/ +/* Controls for the galleries. */ +/* Remove these if you are not using a gallery */ +/*****************************************************************************/ +.highslide-controls { + width: 195px; + height: 40px; + background: url(graphics/controlbar-white.gif) 0 -90px no-repeat; + margin: 20px 15px 10px 0; +} +.highslide-controls ul { + position: relative; + left: 15px; + height: 40px; + list-style: none; + margin: 0; + padding: 0; + background: url(graphics/controlbar-white.gif) right -90px no-repeat; + +} +.highslide-controls li { + float: left; + padding: 5px 0; + margin:0; + list-style: none; +} +.highslide-controls a { + background-image: url(graphics/controlbar-white.gif); + display: block; + float: left; + height: 30px; + width: 30px; + outline: none; +} +.highslide-controls a.disabled { + cursor: default; +} +.highslide-controls a.disabled span { + cursor: default; +} +.highslide-controls a span { + /* hide the text for these graphic buttons */ + display: none; + cursor: pointer; +} + + +/* The CSS sprites for the controlbar - see http://www.google.com/search?q=css+sprites */ +.highslide-controls .highslide-previous a { + background-position: 0 0; +} +.highslide-controls .highslide-previous a:hover { + background-position: 0 -30px; +} +.highslide-controls .highslide-previous a.disabled { + background-position: 0 -60px !important; +} +.highslide-controls .highslide-play a { + background-position: -30px 0; +} +.highslide-controls .highslide-play a:hover { + background-position: -30px -30px; +} +.highslide-controls .highslide-play a.disabled { + background-position: -30px -60px !important; +} +.highslide-controls .highslide-pause a { + background-position: -60px 0; +} +.highslide-controls .highslide-pause a:hover { + background-position: -60px -30px; +} +.highslide-controls .highslide-next a { + background-position: -90px 0; +} +.highslide-controls .highslide-next a:hover { + background-position: -90px -30px; +} +.highslide-controls .highslide-next a.disabled { + background-position: -90px -60px !important; +} +.highslide-controls .highslide-move a { + background-position: -120px 0; +} +.highslide-controls .highslide-move a:hover { + background-position: -120px -30px; +} +.highslide-controls .highslide-full-expand a { + background-position: -150px 0; +} +.highslide-controls .highslide-full-expand a:hover { + background-position: -150px -30px; +} +.highslide-controls .highslide-full-expand a.disabled { + background-position: -150px -60px !important; +} +.highslide-controls .highslide-close a { + background-position: -180px 0; +} +.highslide-controls .highslide-close a:hover { + background-position: -180px -30px; +} + +/*****************************************************************************/ +/* Styles for the HTML popups */ +/* Remove these if you are not using Highslide HTML */ +/*****************************************************************************/ +.highslide-maincontent { + display: none; +} +.highslide-html { + background-color: white; +} +.mobile .highslide-html { + border: 1px solid silver; +} +.highslide-html-content { + display: none; + width: 400px; + padding: 0 5px 5px 5px; +} +.highslide-header { + padding-bottom: 5px; +} +.highslide-header ul { + margin: 0; + padding: 0; + text-align: right; +} +.highslide-header ul li { + display: inline; + padding-left: 1em; +} +.highslide-header ul li.highslide-previous, .highslide-header ul li.highslide-next { + display: none; +} +.highslide-header a { + font-weight: bold; + color: gray; + text-transform: uppercase; + text-decoration: none; +} +.highslide-header a:hover { + color: black; +} +.highslide-header .highslide-move a { + cursor: move; +} +.highslide-footer { + height: 16px; +} +.highslide-footer .highslide-resize { + display: block; + float: right; + margin-top: 5px; + height: 11px; + width: 11px; + background: url(graphics/resize.gif) no-repeat; +} +.highslide-footer .highslide-resize span { + display: none; +} +.highslide-body { +} +.highslide-resize { + cursor: nw-resize; +} + +/*****************************************************************************/ +/* Styles for the Individual wrapper class names. */ +/* See www.highslide.com/ref/hs.wrapperClassName */ +/* You can safely remove the class name themes you don't use */ +/*****************************************************************************/ + +/* hs.wrapperClassName = 'draggable-header' */ +.draggable-header .highslide-header { + height: 18px; + border-bottom: 1px solid #dddddd; +} +.draggable-header .highslide-heading { + position: absolute; + margin: 2px 0.4em; +} + +.draggable-header .highslide-header .highslide-move { + cursor: move; + display: block; + height: 16px; + position: absolute; + right: 24px; + top: 0; + width: 100%; + z-index: 1; +} +.draggable-header .highslide-header .highslide-move * { + display: none; +} +.draggable-header .highslide-header .highslide-close { + position: absolute; + right: 2px; + top: 2px; + z-index: 5; + padding: 0; +} +.draggable-header .highslide-header .highslide-close a { + display: block; + height: 16px; + width: 16px; + background-image: url(graphics/closeX.png); +} +.draggable-header .highslide-header .highslide-close a:hover { + background-position: 0 16px; +} +.draggable-header .highslide-header .highslide-close span { + display: none; +} +.draggable-header .highslide-maincontent { + padding-top: 1em; +} + +/* hs.wrapperClassName = 'titlebar' */ +.titlebar .highslide-header { + height: 18px; + border-bottom: 1px solid #dddddd; +} +.titlebar .highslide-heading { + position: absolute; + width: 90%; + margin: 1px 0 1px 5px; + color: #666666; +} + +.titlebar .highslide-header .highslide-move { + cursor: move; + display: block; + height: 16px; + position: absolute; + right: 24px; + top: 0; + width: 100%; + z-index: 1; +} +.titlebar .highslide-header .highslide-move * { + display: none; +} +.titlebar .highslide-header li { + position: relative; + top: 3px; + z-index: 2; + padding: 0 0 0 1em; +} +.titlebar .highslide-maincontent { + padding-top: 1em; +} + +/* hs.wrapperClassName = 'no-footer' */ +.no-footer .highslide-footer { + display: none; +} + +/* hs.wrapperClassName = 'wide-border' */ +.wide-border { + background: white; +} +.wide-border .highslide-image { + border-width: 10px; +} +.wide-border .highslide-caption { + padding: 0 10px 10px 10px; +} + +/* hs.wrapperClassName = 'borderless' */ +.borderless .highslide-image { + border: none; +} +.borderless .highslide-caption { + border-bottom: 1px solid white; + border-top: 1px solid white; + background: silver; +} + +/* hs.wrapperClassName = 'outer-glow' */ +.outer-glow { + background: #444; +} +.outer-glow .highslide-image { + border: 5px solid #444444; +} +.outer-glow .highslide-caption { + border: 5px solid #444444; + border-top: none; + padding: 5px; + background-color: gray; +} + +/* hs.wrapperClassName = 'colored-border' */ +.colored-border { + background: white; +} +.colored-border .highslide-image { + border: 2px solid green; +} +.colored-border .highslide-caption { + border: 2px solid green; + border-top: none; +} + +/* hs.wrapperClassName = 'dark' */ +.dark { + background: #111; +} +.dark .highslide-image { + border-color: black black #202020 black; + background: gray; +} +.dark .highslide-caption { + color: white; + background: #111; +} +.dark .highslide-controls, +.dark .highslide-controls ul, +.dark .highslide-controls a { + background-image: url(graphics/controlbar-black-border.gif); +} + +/* hs.wrapperClassName = 'floating-caption' */ +.floating-caption .highslide-caption { + position: absolute; + padding: 1em 0 0 0; + background: none; + color: white; + border: none; + font-weight: bold; +} + +/* hs.wrapperClassName = 'controls-in-heading' */ +.controls-in-heading .highslide-heading { + color: gray; + font-weight: bold; + height: 20px; + overflow: hidden; + cursor: default; + padding: 0 0 0 22px; + margin: 0; + background: url(graphics/icon.gif) no-repeat 0 1px; +} +.controls-in-heading .highslide-controls { + width: 105px; + height: 20px; + position: relative; + margin: 0; + top: -23px; + left: 7px; + background: none; +} +.controls-in-heading .highslide-controls ul { + position: static; + height: 20px; + background: none; +} +.controls-in-heading .highslide-controls li { + padding: 0; +} +.controls-in-heading .highslide-controls a { + background-image: url(graphics/controlbar-white-small.gif); + height: 20px; + width: 20px; +} + +.controls-in-heading .highslide-controls .highslide-move { + display: none; +} + +.controls-in-heading .highslide-controls .highslide-previous a { + background-position: 0 0; +} +.controls-in-heading .highslide-controls .highslide-previous a:hover { + background-position: 0 -20px; +} +.controls-in-heading .highslide-controls .highslide-previous a.disabled { + background-position: 0 -40px !important; +} +.controls-in-heading .highslide-controls .highslide-play a { + background-position: -20px 0; +} +.controls-in-heading .highslide-controls .highslide-play a:hover { + background-position: -20px -20px; +} +.controls-in-heading .highslide-controls .highslide-play a.disabled { + background-position: -20px -40px !important; +} +.controls-in-heading .highslide-controls .highslide-pause a { + background-position: -40px 0; +} +.controls-in-heading .highslide-controls .highslide-pause a:hover { + background-position: -40px -20px; +} +.controls-in-heading .highslide-controls .highslide-next a { + background-position: -60px 0; +} +.controls-in-heading .highslide-controls .highslide-next a:hover { + background-position: -60px -20px; +} +.controls-in-heading .highslide-controls .highslide-next a.disabled { + background-position: -60px -40px !important; +} +.controls-in-heading .highslide-controls .highslide-full-expand a { + background-position: -100px 0; +} +.controls-in-heading .highslide-controls .highslide-full-expand a:hover { + background-position: -100px -20px; +} +.controls-in-heading .highslide-controls .highslide-full-expand a.disabled { + background-position: -100px -40px !important; +} +.controls-in-heading .highslide-controls .highslide-close a { + background-position: -120px 0; +} +.controls-in-heading .highslide-controls .highslide-close a:hover { + background-position: -120px -20px; +} + +/*****************************************************************************/ +/* Styles for text based controls. */ +/* You can safely remove this if you don't use text based controls */ +/*****************************************************************************/ + +.text-controls .highslide-controls { + width: auto; + height: auto; + margin: 0; + text-align: center; + background: none; +} +.text-controls ul { + position: static; + background: none; + height: auto; + left: 0; +} +.text-controls .highslide-move { + display: none; +} +.text-controls li { + background-image: url(graphics/controlbar-text-buttons.png); + background-position: right top !important; + padding: 0; + margin-left: 15px; + display: block; + width: auto; +} +.text-controls a { + background: url(graphics/controlbar-text-buttons.png) no-repeat; + background-position: left top !important; + position: relative; + left: -10px; + display: block; + width: auto; + height: auto; + text-decoration: none !important; +} +.text-controls a span { + background: url(graphics/controlbar-text-buttons.png) no-repeat; + margin: 1px 2px 1px 10px; + display: block; + min-width: 4em; + height: 18px; + line-height: 18px; + padding: 1px 0 1px 18px; + color: #333; + font-family: "Trebuchet MS", Arial, sans-serif; + font-size: 12px; + font-weight: bold; + white-space: nowrap; +} +.text-controls .highslide-next { + margin-right: 1em; +} +.text-controls .highslide-full-expand a span { + min-width: 0; + margin: 1px 0; + padding: 1px 0 1px 10px; +} +.text-controls .highslide-close a span { + min-width: 0; +} +.text-controls a:hover span { + color: black; +} +.text-controls a.disabled span { + color: #999; +} + +.text-controls .highslide-previous span { + background-position: 0 -40px; +} +.text-controls .highslide-previous a.disabled { + background-position: left top !important; +} +.text-controls .highslide-previous a.disabled span { + background-position: 0 -140px; +} +.text-controls .highslide-play span { + background-position: 0 -60px; +} +.text-controls .highslide-play a.disabled { + background-position: left top !important; +} +.text-controls .highslide-play a.disabled span { + background-position: 0 -160px; +} +.text-controls .highslide-pause span { + background-position: 0 -80px; +} +.text-controls .highslide-next span { + background-position: 0 -100px; +} +.text-controls .highslide-next a.disabled { + background-position: left top !important; +} +.text-controls .highslide-next a.disabled span { + background-position: 0 -200px; +} +.text-controls .highslide-full-expand span { + background: none; +} +.text-controls .highslide-full-expand a.disabled { + background-position: left top !important; +} +.text-controls .highslide-close span { + background-position: 0 -120px; +} + + +/*****************************************************************************/ +/* Styles for the thumbstrip. */ +/* See www.highslide.com/ref/hs.addSlideshow */ +/* You can safely remove this if you don't use a thumbstrip */ +/*****************************************************************************/ + +.highslide-thumbstrip { + height: 100%; + direction: ltr; +} +.highslide-thumbstrip div { + overflow: hidden; +} +.highslide-thumbstrip table { + position: relative; + padding: 0; + border-collapse: collapse; +} +.highslide-thumbstrip td { + padding: 1px; + /*text-align: center;*/ +} +.highslide-thumbstrip a { + outline: none; +} +.highslide-thumbstrip img { + display: block; + border: 1px solid gray; + margin: 0 auto; +} +.highslide-thumbstrip .highslide-active-anchor img { + visibility: visible; +} +.highslide-thumbstrip .highslide-marker { + position: absolute; + width: 0; + height: 0; + border-width: 0; + border-style: solid; + border-color: transparent; /* change this to actual background color in highslide-ie6.css */ +} +.highslide-thumbstrip-horizontal div { + width: auto; + /* width: 100% breaks in small strips in IE */ +} +.highslide-thumbstrip-horizontal .highslide-scroll-up { + display: none; + position: absolute; + top: 3px; + left: 3px; + width: 25px; + height: 42px; +} +.highslide-thumbstrip-horizontal .highslide-scroll-up div { + margin-bottom: 10px; + cursor: pointer; + background: url(graphics/scrollarrows.png) left center no-repeat; + height: 42px; +} +.highslide-thumbstrip-horizontal .highslide-scroll-down { + display: none; + position: absolute; + top: 3px; + right: 3px; + width: 25px; + height: 42px; +} +.highslide-thumbstrip-horizontal .highslide-scroll-down div { + margin-bottom: 10px; + cursor: pointer; + background: url(graphics/scrollarrows.png) center right no-repeat; + height: 42px; +} +.highslide-thumbstrip-horizontal table { + margin: 2px 0 10px 0; +} +.highslide-viewport .highslide-thumbstrip-horizontal table { + margin-left: 10px; +} +.highslide-thumbstrip-horizontal img { + width: auto; + height: 40px; +} +.highslide-thumbstrip-horizontal .highslide-marker { + top: 47px; + border-left-width: 6px; + border-right-width: 6px; + border-bottom: 6px solid gray; +} +.highslide-viewport .highslide-thumbstrip-horizontal .highslide-marker { + margin-left: 10px; +} +.dark .highslide-thumbstrip-horizontal .highslide-marker, .highslide-viewport .highslide-thumbstrip-horizontal .highslide-marker { + border-bottom-color: white !important; +} + +.highslide-thumbstrip-vertical-overlay { + overflow: hidden !important; +} +.highslide-thumbstrip-vertical div { + height: 100%; +} +.highslide-thumbstrip-vertical a { + display: block; +} +.highslide-thumbstrip-vertical .highslide-scroll-up { + display: none; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 25px; +} +.highslide-thumbstrip-vertical .highslide-scroll-up div { + margin-left: 10px; + cursor: pointer; + background: url(graphics/scrollarrows.png) top center no-repeat; + height: 25px; +} +.highslide-thumbstrip-vertical .highslide-scroll-down { + display: none; + position: absolute; + bottom: 0; + left: 0; + width: 100%; + height: 25px; +} +.highslide-thumbstrip-vertical .highslide-scroll-down div { + margin-left: 10px; + cursor: pointer; + background: url(graphics/scrollarrows.png) bottom center no-repeat; + height: 25px; +} +.highslide-thumbstrip-vertical table { + margin: 10px 0 0 10px; +} +.highslide-thumbstrip-vertical img { + width: 60px; /* t=5481 */ +} +.highslide-thumbstrip-vertical .highslide-marker { + left: 0; + margin-top: 8px; + border-top-width: 6px; + border-bottom-width: 6px; + border-left: 6px solid gray; +} +.dark .highslide-thumbstrip-vertical .highslide-marker, .highslide-viewport .highslide-thumbstrip-vertical .highslide-marker { + border-left-color: white; +} + +.highslide-viewport .highslide-thumbstrip-float { + overflow: auto; +} +.highslide-thumbstrip-float ul { + margin: 2px 0; + padding: 0; +} +.highslide-thumbstrip-float li { + display: block; + height: 60px; + margin: 0 2px; + list-style: none; + float: left; +} +.highslide-thumbstrip-float img { + display: inline; + border-color: silver; + max-height: 56px; +} +.highslide-thumbstrip-float .highslide-active-anchor img { + border-color: black; +} +.highslide-thumbstrip-float .highslide-scroll-up div, .highslide-thumbstrip-float .highslide-scroll-down div { + display: none; +} +.highslide-thumbstrip-float .highslide-marker { + display: none; +} diff --git a/highslide/nav/back.png b/highslide/nav/back.png new file mode 100644 index 0000000..501909c Binary files /dev/null and b/highslide/nav/back.png differ diff --git a/highslide/nav/next.png b/highslide/nav/next.png new file mode 100644 index 0000000..bdd1011 Binary files /dev/null and b/highslide/nav/next.png differ diff --git a/highslide/nav/prev.png b/highslide/nav/prev.png new file mode 100644 index 0000000..68b2514 Binary files /dev/null and b/highslide/nav/prev.png differ diff --git a/lib/UCW/Gallery.pm b/lib/UCW/Gallery.pm new file mode 100644 index 0000000..748fe3c --- /dev/null +++ b/lib/UCW/Gallery.pm @@ -0,0 +1,216 @@ +# Simple Photo Gallery +# (c) 2003--2015 Martin Mares + +package UCW::Gallery; + +use strict; +use warnings; + +use File::Spec; +use Storable; + +### Class methods ### + +sub new($) { + my ($class) = @_; + my $self = { }; + $self->{cfg} = { + # Directories + OrigDir => '.', # Original images + PhotoDir => 'photo', # Scaled-down photos for web + CacheDir => 'cache', # Cache with meta-data and thumbnails + + # URL prefixes + PhotoUrlPrefix => 'photo/', + ThumbUrlPrefix => 'thumb/', + ThemeUrlPrefix => 'gal/', + + # Processing machinery settings + ScanDefaultTransform => 's', + PhotoMaxWidth => 1024, + PhotoMaxHeight => 1024, + ThumbFormats => [ "114x94" ], # Built-in themes use the first size, + # but more can be generated + CacheExif => 0, # Cache selected EXIF meta-data + CacheHashes => 1, # Let gal-scan cache file hashes + + # Titles and navigation + Title => 'An Unnamed Gallery', + SubTitle => "", + ParentURL => '../', + BackURL => "", + FwdURL => "", + + # Hacks + GeoHack => 0, + }; + return bless $self, $class; +} + +sub load_config($) { + my $cfg = "./gallery.cf"; + my $self = do $cfg; + unless (defined $self) { + if ($@) { + die "Error parsing $cfg: $@"; + } elsif ($!) { + die "Cannot load $cfg: $!\n"; + } else { + die "Cannot load $cfg, check that it returns true\n"; + } + } + return $self; +} + +### Object methods ### + +sub get($$) { + my ($self, $key) = @_; + if (exists $self->{cfg}->{$key}) { + my $val = $self->{cfg}->{$key}; + defined $val or warn "Gallery: Config item $key is not set\n"; + return $val; + } else { + warn "Gallery: Config item $key does not exist\n"; + return; + } +} + +sub try_get($$) { + my ($self, $key) = @_; + return $self->{cfg}->{$key}; +} + +sub def($@) { + my $self = shift; + while (my $key = shift @_) { + my $val = shift @_; + $self->{cfg}->{$key} //= $val; + } +} + +sub set($@) { + my $self = shift; + while (my $key = shift @_) { + $self->{cfg}->{$key} = shift @_; + } +} + +sub get_config_keys($) { + my ($self) = @_; + return keys %{$self->{cfg}}; +} + +our %list_attrs = ( + 'file' => 0, # 0 = not permitted as extended attribute + 'id' => 0, + 'orientation' => 1, # 1 = aliases for normal attributes + 'title' => 1, + 'xf' => 2, # 2 = ... and propagated to gal-gen + 'lat' => 3, # 3 = normal extended attributes, propagated to gal-gen + 'lon' => 3, + 'alt' => 3, + 't' => 3, +); + +sub write_list($$$) { + my ($self, $file, $images) = @_; + open my $fh, '>:utf8', "$file.new" or die "Cannot create $file.new: $!\n"; + for my $i (@$images) { + print $fh join("\t", + $i->{file}, + $i->{id}, + $i->{orientation}, + $i->{xf}, + ($i->{title} eq '' ? '-' : $i->{title}), + ), "\n"; + for my $k (keys %$i) { + print $fh "\t$k=", $i->{$k}, "\n" if $list_attrs{$k} >= 3; + } + } + close $fh; + rename "$file.new", $file or die "Cannot rename $file.new to $file: $!\n"; +} + +sub read_list_fh($$) { + my ($self, $fh) = @_; + my @images = (); + while (<$fh>) { + chomp; + /^$/ and next; + /^#/ and next; + if (/^\t/) { + @images or die "Misplaced continuation line before first image\n"; + if (my ($k, $v) = /^\t+(.*?)=(.*)/) { + # Continutation of previous line + my $i = $images[-1]; + if ($list_attrs{$k}) { + $i->{$k} = $v; + } else { + print STDERR "Ignoring unknown attribute $k for ", $i->{file}, "\n"; + } + } else { + die "Invalid continuation line. Expecting 'key=value'.\n"; + } + } else { + my $i = {}; + ($i->{file}, $i->{id}, $i->{orientation}, $i->{xf}, $i->{title}) = split /\t/; + if (!defined $i->{title} || $i->{title} eq '-') { $i->{title} = ""; } + push @images, $i; + } + } + return \@images; +} + +sub read_list($$) { + my ($self, $file) = @_; + open my $fh, '<:utf8', $file or return; + my $list = $self->read_list_fh($fh); + close $fh; + return $list; +} + +sub write_meta($$) { + my ($self, $file, $meta) = @_; + open my $fh, '>', "$file.new" or die "Cannot create $file.new: $!\n"; + Storable::nstore_fd($meta, $fh); + close $fh; + rename "$file.new", $file or die "Cannot rename $file.new to $file: $!\n"; +} + +sub read_meta($) { + my ($self, $file) = @_; + open my $fh, '<', $file or die "Cannot read $file: $!\n"; + my $meta = Storable::fd_retrieve($fh) or die "Cannot parse $file\n"; + close $fh; + return $meta; +} + +sub photo_meta_name($) { + my ($self) = @_; + return File::Spec->catfile($self->get('PhotoDir'), 'gallery.meta'); +} + +sub cache_meta_name($) { + my ($self) = @_; + return File::Spec->catfile($self->get('CacheDir'), 'cache.meta'); +} + +sub thumb_fmt_to_size($$) { + my ($self, $fmt) = @_; + my ($tw, $th) = ($fmt =~ m{^(\d+)x(\d+)$}) or die "Cannot parse thumbnail format $fmt\n"; + return ($tw, $th); +} + +sub photo_file_name($$$) { + my ($self, $photo_meta, $id) = @_; + return $id . '.' . ($photo_meta->{fmt} // 'jpg'); +} + +sub photo_name($$$) { + my ($self, $photo_meta, $id) = @_; + return File::Spec->catfile($self->get('PhotoDir'), $self->photo_file_name($photo_meta, $id)); +} + + +1; diff --git a/lib/UCW/Gallery/Archive.pm b/lib/UCW/Gallery/Archive.pm new file mode 100644 index 0000000..fb7c40b --- /dev/null +++ b/lib/UCW/Gallery/Archive.pm @@ -0,0 +1,34 @@ +# Simple Photo Gallery: Archiving +# (c) 2003--2012 Martin Mares + +package UCW::Gallery::Archive; + +use strict; +use warnings; + +use Archive::Zip; +use File::Spec; +use UCW::CGI; + +sub send_archive($$) { + my ($gal, $meta) = @_; + + if (!$gal->get('WebAllowArchives')) { + UCW::CGI::http_error('403 Archiving forbidden by server configuration'); + return; + } + + my $zip = Archive::Zip->new; + my $cnt = 0; + for my $id (@{$meta->{sequence}}) { + $zip->addFile(File::Spec->catfile($gal->get('PhotoDir'), "$id.jpg"), sprintf("%03d.jpg", $cnt)) or die; + $cnt++; + } + + print "Content-type: application/zip\n"; + print "Content-Disposition: attachment; filename=gallery.zip\n"; + print "\n"; + $zip->writeToFileHandle(\*STDOUT, 0); +} + +42; diff --git a/lib/UCW/Gallery/Hashes.pm b/lib/UCW/Gallery/Hashes.pm new file mode 100644 index 0000000..d0414e9 --- /dev/null +++ b/lib/UCW/Gallery/Hashes.pm @@ -0,0 +1,51 @@ +# Simple Photo Gallery: Image Hashes +# (c) 2015 Martin Mares + +package UCW::Gallery::Hashes; + +use strict; +use warnings; + +use File::stat (); +use Digest::SHA; + +sub new { + my ($class, $gal) = @_; + my $self = { gal => $gal }; + + if ($gal->get('CacheHashes') && -f 'gallery.cache') { + print "Loading gallery.cache\n"; + $self->{cache} = $gal->read_meta('gallery.cache'); + } else { + $self->{cache} = {}; + } + + return bless $self, $class; +} + +sub write { + my ($self) = @_; + my $gal = $self->{gal}; + if ($gal->get('CacheHashes')) { + print "Writing gallery.cache\n"; + $gal->write_meta('gallery.cache', $self->{cache}); + } +} + +sub hash_image { + my ($self, $path) = @_; + my $cache = $self->{cache}; + + my $st = File::stat::stat($path) or die "Cannot access $path: $!\n"; + my $key_text = join(":", $path, $st->dev, $st->ino, $st->mtime); + my $key = Digest::SHA->sha1_base64($key_text); + + if (!exists $cache->{$key}) { + my $sha = Digest::SHA->new(1); + $sha->addfile($path) or die "Cannot hash $path\n"; + $cache->{$key} = substr($sha->hexdigest, 0, 16); + } + return $cache->{$key}; +} + +42; diff --git a/lib/UCW/Gallery/Web.pm b/lib/UCW/Gallery/Web.pm new file mode 100644 index 0000000..ccca477 --- /dev/null +++ b/lib/UCW/Gallery/Web.pm @@ -0,0 +1,181 @@ +# Simple Photo Gallery: Web Interface +# (c) 2003--2012 Martin Mares + +package UCW::Gallery::Web; + +use strict; +use warnings; + +use UCW::Gallery; +use UCW::CGI; +use File::Spec; + +my $show_img; +my $want_archive; + +my %args = ( + 'i' => { 'var' => \$show_img, 'check' => '\d+' }, + 'a' => { 'var' => \$want_archive }, +); + +sub error($) { + print "

    Bad luck, the script is broken. Sorry.\n

    $_[0]\n"; + print "\n"; +} + +sub get($$) { + my ($self, $key) = @_; + return $self->{gal}->get($key); +} + +sub extras($$) { + my ($self, $key) = @_; + my $val = $self->get($key); + if (ref $val eq 'CODE') { + return &$val($self); + } else { + return $val; + } +} + +# For use by extras hooks +sub gallery($) { + my ($self) = @_; + return $self->{gal}; +} + +# For use by extras hooks: return true if we are showing an image page, false for index page +sub showing_image($) { + my ($self) = @_; + return $show_img ne ""; +} + +sub html_top($) { + my ($self) = @_; + my $title = UCW::CGI::html_escape($self->get('Title')); + my $hextras = $self->extras('WebHeadExtras'); + my $theme_hextras = $self->theme_head_extras; + print < + +$hextras$theme_hextras$title + +EOF + + $UCW::CGI::ErrorHandler::error_hook = \&error; + + # WebTopExtras are evaluated separately, since they can override the error hook + print $self->extras('WebTopExtras'); +} + +sub html_bot($) { + my ($self) = @_; + print $self->extras('WebBotExtras'), "\n"; +} + +sub show_img($) { + my ($self) = @_; + + if ($show_img < 1 || $show_img > $self->{num_photos}) { + UCW::CGI::http_error('404 No such photo'); + return; + } + + my $meta = $self->{meta}; + my $id = $meta->{sequence}->[$show_img-1]; + my $m = $meta->{photo}->{$id} or die; + $self->html_top; + + $self->show_links(($show_img > 1 ? ("?i=".($show_img-1)) : ""), + ".", + ($show_img < $self->{num_photos} ? ("?i=".($show_img+1)) : "")); + + my $t = UCW::CGI::html_escape($m->{title}); + my $w = $m->{w}; + my $h = $m->{h}; + print "

    $t

    \n" if $t ne ""; + my $img = $self->get('PhotoUrlPrefix') . $self->{gal}->photo_file_name($m, $id); + print "

    $t\n"; + + $self->html_bot; +} + +sub show_pre_thumbs($) { + my ($self) = @_; +} + +sub show_post_thumbs($) { + my ($self) = @_; +} + +sub show_list($) { + my ($self) = @_; + $self->html_top; + + $self->show_links($self->get('BackURL'), $self->get('ParentURL'), $self->get('FwdURL')); + print "

    ", $self->get('Title'), "

    \n"; + my $subtitle = $self->get('SubTitle'); + print "

    $subtitle

    \n" if $subtitle ne ""; + $self->show_pre_thumbs; + + my $meta = $self->{meta}; + for my $idx (1..$self->{num_photos}) { + my $id = $meta->{sequence}->[$idx-1]; + my $m = $meta->{photo}->{$id}; + my $click_url; + if ($self->get('WebImageSubpages')) { + $click_url = "?i=$idx"; + } else { + $click_url = $self->get('PhotoUrlPrefix') . $self->{gal}->photo_file_name($m, $id); + } + $self->show_thumb($meta, $id, $click_url); + } + + $self->show_post_thumbs; + $self->html_bot(); +} + +sub dispatch($) { + my ($self) = @_; + binmode STDOUT, ':utf8'; + UCW::CGI::parse_args(\%args); + $self->{meta} = $self->{gal}->read_meta(File::Spec->catfile($self->get('CacheDir'), 'cache.meta')); + $self->{num_photos} = scalar @{$self->{meta}->{sequence}}; + + if ($want_archive) { + require UCW::Gallery::Archive; + UCW::Gallery::Archive::send_archive($self->{gal}, $self->{meta}); + } elsif ($show_img ne "") { + $self->show_img; + } else { + $self->show_list; + } +} + +sub attach($$) { + my ($class, $gal) = @_; + my $self = { gal => $gal }; + $gal->def( + WebFE => $self, + + # Extras are either strings or functions called with the current gallery object as parameter + WebHeadExtras => "", + WebTopExtras => "", + WebBotExtras => "", + + # Used by the theming logic + WebThemeCSS => undef, + + # 1 if thumbnail link to sub-pages with images, 0 if they link directly to image files + WebImageSubpages => 1, + + # If enabled, calling the CGI with a=zip produces a ZIP archive with all photos. + WebAllowArchives => 1, + ); + bless $self, $class; + return $self; +} + +42; diff --git a/lib/UCW/Gallery/Web/HighSlide.pm b/lib/UCW/Gallery/Web/HighSlide.pm new file mode 100644 index 0000000..b1e503b --- /dev/null +++ b/lib/UCW/Gallery/Web/HighSlide.pm @@ -0,0 +1,86 @@ +# Highslide JS Theme for MJ's Photo Gallery +# (c) 2012 Martin Mares ; GPL'ed + +package UCW::Gallery::Web::HighSlide; + +use strict; +use warnings; +use utf8; + +use UCW::Gallery; +use UCW::Gallery::Web; + +our @ISA = qw(UCW::Gallery::Web); + +sub theme_head_extras($) { + my ($self) = @_; + my $hsdir = $self->get('ThemeUrlPrefix') . "highslide"; + return $self->showing_image ? < +AMEN_MINI + + + + + + +AMEN_FULL +} + +sub show_links($$$$) { + my ($self, $prev, $up, $next) = @_; + my $nav = $self->get('ThemeUrlPrefix') . "highslide/nav"; + print "

    "; + print "" if $prev ne ""; + printf "" if $next ne ""; + printf "" if $up ne ""; +} + +sub show_pre_thumbs($) { + my ($self) = @_; + print "\n

    \n\n"; +} + +sub show_thumb($) { + my ($self, $meta, $photo_id, $click_url) = @_; + my $m = $meta->{photo}->{$photo_id}; + my $annot = UCW::CGI::html_escape($m->{title}); + my $tf = $self->get('ThumbFormats')->[0]; + my $tm = $meta->{thumb}->{$tf}->{$photo_id} or die "No thumbnails for format $tf found!\n"; + my $tw = $tm->{w}; + my $th = $tm->{h}; + my $thumb = $self->get('ThumbUrlPrefix') . "$tf/$photo_id.jpg"; + # Highslide requires title either for all images, or for none + my $tit = " title=\"$annot\""; + my $aid = 'i'.(++$self->{hs_thumb_counter}); + my $photo_url = $self->get('PhotoUrlPrefix') . $self->{gal}->photo_file_name($m, $photo_id); + print "
  • "; + print "Photo\n"; + if ($self->get('GeoHack')) { + my ($lat, $lon) = ($m->{lat}, $m->{lon}); + if (defined $lat && defined $lon) { + my $local = "trasa"; + my $osm = "OSM"; + print "
    Ukázat na mapě: $local, $osm
    \n"; + } + } +} + +sub run($$) { + my ($class, $gal) = @_; + my $self = $class->SUPER::attach($gal); + $self->dispatch(); + return $self; +} + +1; diff --git a/lib/UCW/Gallery/Web/NrtBlue.pm b/lib/UCW/Gallery/Web/NrtBlue.pm new file mode 100644 index 0000000..7fc5637 --- /dev/null +++ b/lib/UCW/Gallery/Web/NrtBlue.pm @@ -0,0 +1,82 @@ +# NRT Theme for MJ's Photo Gallery +# (c) 2003--2012 Martin Mares ; GPL'ed +# Theme images taken from the cthumb package (c) Carlos Puchol + +package UCW::Gallery::Web::NrtBlue; + +use strict; +use warnings; + +use UCW::Gallery; +use UCW::Gallery::Web; + +our @ISA = qw(UCW::Gallery::Web); + +my $theme_name = "nrt-blue"; +my $navw = 48; +my $navh = 48; +my $interior_margin = 4; +my $left_w = 14; +my $right_w = 18; +my $top_h = 14; +my $bot_h = 18; + +sub theme_dir($) { + my ($self) = @_; + return $self->get('ThemeUrlPrefix') . $theme_name; +} + +sub theme_head_extras($) { + my ($self) = @_; + my $stylesheet = $self->theme_dir . "/style.css"; + return "\n"; +} + +sub show_links($$$$) { + my ($self, $prev, $up, $next) = @_; + my $theme = $self->theme_dir; + print "

    "; + print ""; + print "Back" if $prev ne ""; + print "\n"; + printf ""; + printf "Forward" if $next ne ""; + print "\n"; + printf "Up" if $up ne ""; +} + +sub show_thumb($) { + my ($self, $meta, $photo_id, $click_url) = @_; + my $theme = $self->theme_dir; + my $m = $meta->{photo}->{$photo_id}; + my $annot = UCW::CGI::html_escape($m->{title}); + my $tf = $self->get('ThumbFormats')->[0]; + my ($thumb_w, $thumb_h) = $self->{gal}->thumb_fmt_to_size($tf); + my $tm = $meta->{thumb}->{$tf}->{$photo_id} or die "No thumbnails for format $tf found!\n"; + my $tw = $tm->{w}; + my $th = $tm->{h}; + my $thumb = $self->get('ThumbUrlPrefix') . "$tf/$photo_id.jpg"; + my $side_w = $thumb_w + 2*$interior_margin; + my $side_h = $thumb_h + 2*$interior_margin; + my $box_w = $left_w + $side_w + $right_w; + my $box_h = $top_h + $side_h + $bot_h; + print "

    \n"; + print "\n"; + print "\n"; + my $ol = $left_w + $interior_margin + int(($thumb_w - $tw)/2); + my $ot = $top_h + $interior_margin + int(($thumb_h - $th)/2); + my $tit = ($annot ne "") ? " title=\"$annot\"" : ""; + print "Photo\n"; + print "\n"; + print "\n"; + print "
    \n\n"; +} + +sub run($$) { + my ($class, $gal) = @_; + my $self = $class->SUPER::attach($gal); + $self->dispatch(); + return $self; +} + +1; diff --git a/lib/UCW/Gallery/Web/Plain.pm b/lib/UCW/Gallery/Web/Plain.pm new file mode 100644 index 0000000..f7d213c --- /dev/null +++ b/lib/UCW/Gallery/Web/Plain.pm @@ -0,0 +1,70 @@ +# Plain Theme for MJ's Photo Gallery +# (c) 2012 Martin Mares ; GPL'ed + +package UCW::Gallery::Web::Plain; + +use strict; +use warnings; + +use UCW::Gallery; +use UCW::Gallery::Web; + +our @ISA = qw(UCW::Gallery::Web); + +my $theme_name = "plain"; +my $navw = 48; +my $navh = 48; +my $box_w = 130; +my $box_h = 110; + +sub theme_dir($) { + my ($self) = @_; + return $self->get('ThemeUrlPrefix') . $theme_name; +} + +sub theme_head_extras($) { + my ($self) = @_; + my $stylesheet = $self->theme_dir . "/style.css"; + return "\n"; +} + +sub show_links($$$$) { + my ($self, $prev, $up, $next) = @_; + my $theme = $self->theme_dir; + print "

    "; + print ""; + print "Back" if $prev ne ""; + print "\n"; + printf ""; + printf "Forward" if $next ne ""; + print "\n"; + printf "Up" if $up ne ""; +} + +sub show_thumb($) { + my ($self, $meta, $photo_id, $click_url) = @_; + my $theme = $self->theme_dir; + my $m = $meta->{photo}->{$photo_id}; + my $annot = UCW::CGI::html_escape($m->{title}); + my $tf = $self->get('ThumbFormats')->[0]; + my ($thumb_w, $thumb_h) = $self->{gal}->thumb_fmt_to_size($tf); + my $tm = $meta->{thumb}->{$tf}->{$photo_id} or die "No thumbnails for format $tf found!\n"; + my $tw = $tm->{w}; + my $th = $tm->{h}; + my $thumb = $self->get('ThumbUrlPrefix') . "$tf/$photo_id.jpg"; + print "

    \n"; + my $ol = int(($box_w - $tw)/2); + my $ot = int(($box_h - $th)/2); + my $tit = ($annot ne "") ? " title=\"$annot\"" : ""; + print "Photo\n"; + print "
    \n\n"; +} + +sub run($$) { + my ($class, $gal) = @_; + my $self = $class->SUPER::attach($gal); + $self->dispatch(); + return $self; +} + +1; diff --git a/nrt-blue/back.png b/nrt-blue/back.png new file mode 100644 index 0000000..501909c Binary files /dev/null and b/nrt-blue/back.png differ diff --git a/nrt-blue/back.xcf b/nrt-blue/back.xcf new file mode 100644 index 0000000..5d8848d Binary files /dev/null and b/nrt-blue/back.xcf differ diff --git a/nrt-blue/bot.png b/nrt-blue/bot.png new file mode 100644 index 0000000..86bb5f2 Binary files /dev/null and b/nrt-blue/bot.png differ diff --git a/nrt-blue/bot.xcf b/nrt-blue/bot.xcf new file mode 100644 index 0000000..a3dbedc Binary files /dev/null and b/nrt-blue/bot.xcf differ diff --git a/nrt-blue/left.png b/nrt-blue/left.png new file mode 100644 index 0000000..a9852c7 Binary files /dev/null and b/nrt-blue/left.png differ diff --git a/nrt-blue/left.xcf b/nrt-blue/left.xcf new file mode 100644 index 0000000..76912ed Binary files /dev/null and b/nrt-blue/left.xcf differ diff --git a/nrt-blue/next.png b/nrt-blue/next.png new file mode 100644 index 0000000..bdd1011 Binary files /dev/null and b/nrt-blue/next.png differ diff --git a/nrt-blue/next.xcf b/nrt-blue/next.xcf new file mode 100644 index 0000000..ad06333 Binary files /dev/null and b/nrt-blue/next.xcf differ diff --git a/nrt-blue/prev.png b/nrt-blue/prev.png new file mode 100644 index 0000000..68b2514 Binary files /dev/null and b/nrt-blue/prev.png differ diff --git a/nrt-blue/prev.xcf b/nrt-blue/prev.xcf new file mode 100644 index 0000000..dbf35d5 Binary files /dev/null and b/nrt-blue/prev.xcf differ diff --git a/nrt-blue/right.png b/nrt-blue/right.png new file mode 100644 index 0000000..7af66ba Binary files /dev/null and b/nrt-blue/right.png differ diff --git a/nrt-blue/right.xcf b/nrt-blue/right.xcf new file mode 100644 index 0000000..0668d05 Binary files /dev/null and b/nrt-blue/right.xcf differ diff --git a/nrt-blue/style.css b/nrt-blue/style.css new file mode 100644 index 0000000..de50720 --- /dev/null +++ b/nrt-blue/style.css @@ -0,0 +1,17 @@ +.thf { margin: 0 0 0 0; padding: 0 0 0 0; float: left; border: none; } +.thumb { position: relative; width: 154px; height: 134px; background-color: black; } +.tt { position: absolute; top: 0; left: 0; } +.tl { position: absolute; top: 14px; left: 0; } +.ti { position: absolute; } +.tr { position: absolute; top: 14px; right: 0; } +.tb { position: absolute; bottom: 0; right: 0; } +IMG { border: none; } +H1 { text-align: center; } +H2 { text-align: center; margin-bottom: 3ex; } +P { clear: both; } +H2 { clear: both; } +.parent { text-align: center; } +.large { text-align: center; } +.back { float: left; } +.fwd { float: right; } +A[href]:hover { background-color: #11001d; } diff --git a/nrt-blue/top.png b/nrt-blue/top.png new file mode 100644 index 0000000..db5a677 Binary files /dev/null and b/nrt-blue/top.png differ diff --git a/nrt-blue/top.xcf b/nrt-blue/top.xcf new file mode 100644 index 0000000..8298369 Binary files /dev/null and b/nrt-blue/top.xcf differ diff --git a/plain/back.png b/plain/back.png new file mode 100644 index 0000000..501909c Binary files /dev/null and b/plain/back.png differ diff --git a/plain/next.png b/plain/next.png new file mode 100644 index 0000000..bdd1011 Binary files /dev/null and b/plain/next.png differ diff --git a/plain/prev.png b/plain/prev.png new file mode 100644 index 0000000..68b2514 Binary files /dev/null and b/plain/prev.png differ diff --git a/plain/style.css b/plain/style.css new file mode 100644 index 0000000..4e86e31 --- /dev/null +++ b/plain/style.css @@ -0,0 +1,13 @@ +.thf { margin: 0 0 0 0; padding: 0 0 0 0; float: left; border: none; } +.thumb { position: relative; width: 130px; height: 110px; } +.ti { position: absolute; border: 1px solid #505; } +IMG { border: none; } +H1 { text-align: center; } +H2 { text-align: center; margin-bottom: 3ex; } +P { clear: both; } +H2 { clear: both; } +.parent { text-align: center; } +.large { text-align: center; } +.back { float: left; } +.fwd { float: right; } +A[href]:hover { background-color: #11001d; }