--- /dev/null
+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
--- /dev/null
+$(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))
--- /dev/null
+================================================================================
+
+ UCW::Gallery v2.0
+
+ (c) 2004--2015 Martin Mares <mj@ucw.cz>
+
+================================================================================
+
+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".
--- /dev/null
+#!/usr/bin/perl
+# UCW Gallery: Prepare cache
+# (c) 2004--2012 Martin Mares <mj@ucw.cz>
+
+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);
--- /dev/null
+#!/usr/bin/perl
+# UCW Gallery: Symlink photos according to their EXIF timestamps
+# (c) 2013 Martin Mares <mj@ucw.cz>
+
+use strict;
+use warnings;
+
+use Image::EXIF;
+use Getopt::Long;
+
+if (@ARGV && $ARGV[0] eq '--help') {
+ die <<AMEN ;
+Usage: cat list | gal date [<options>]
+ or: gal date [<options>] <files>
+
+Options:
+-d, --destdir=<dir> Create symlinks in specified directory (default: current dir)
+-n, --dry-run Perform a trial run with no changes made
+-o, --offset=<offset> Adjust timestamps by a given offset (<h>[:<m>[:<s>]])
+ --suffix=<s> 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 (<STDIN>) {
+ 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);
--- /dev/null
+#!/usr/bin/perl
+# UCW Gallery: Show configuration variables
+# (c) 2004--2012 Martin Mares <mj@ucw.cz>
+
+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;
+}
--- /dev/null
+#!/usr/bin/perl
+# UCW Gallery: Dump meta-data
+# (c) 2004--2012 Martin Mares <mj@ucw.cz>
+
+use strict;
+use warnings;
+
+use UCW::Gallery;
+use Data::Dumper;
+
+@ARGV == 1 or die "Usage: $0 <meta-file>\n";
+
+my $gal = UCW::Gallery->load_config;
+my $meta = $gal->read_meta($ARGV[0]);
+print Dumper($meta);
--- /dev/null
+#!/usr/bin/perl
+# UCW Gallery: Extract image list from GQview collection
+# (c) 2004--2012 Martin Mares <mj@ucw.cz>
+
+use strict;
+use warnings;
+
+while (<>) {
+ chomp;
+ /^#/ && next;
+ /^$/ && next;
+ if (/^"(.*)"$/) {
+ print "$1\n";
+ } else {
+ die "Error parsing collection: $_";
+ }
+}
--- /dev/null
+#!/usr/bin/perl
+# UCW Gallery: Generate published photos
+# (c) 2004--2015 Martin Mares <mj@ucw.cz>
+
+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;
--- /dev/null
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Cwd;
+use DBI;
+use Getopt::Long;
+
+if (@ARGV && $ARGV[0] eq '--help') {
+ die <<AMEN ;
+Usage: gal mj-digikam [<album>]
+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( <<AMEN, { Slice => {} },
+ 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;
--- /dev/null
+#!/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
--- /dev/null
+#!/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,
+}, ".");
--- /dev/null
+#!/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 (<T>) {
+ 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";
+ }
+ }
+}
--- /dev/null
+#!/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 <album>\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 (<M>) {
+ 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 (<S>) {
+ 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 (<I>) {
+ 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;
--- /dev/null
+#!/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";
--- /dev/null
+#!/usr/bin/perl
+# UCW Gallery: Scan images and generate image list
+# (c) 2004--2015 Martin Mares <mj@ucw.cz>
+
+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 <<AMEN ;
+Usage: cat list | gal scan <options>
+ or: gal scan <options> <files and directories>
+ or: gal scan <options> --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;
--- /dev/null
+#!/usr/bin/perl
+# UCW Gallery -- Master Program
+# (c) 2012 Martin Mares <mj@ucw.cz>
+
+use strict;
+use warnings;
+use Getopt::Long;
+
+use FindBin;
+my $gallery_root = $FindBin::Bin;
+
+my $all;
+my $parallel;
+
+sub show_help() {
+ print <<AMEN ;
+Usage: gal <general-options> <command> <command-options> <args>
+
+General options:
+ --all Run on all galleries in subdirectories
+-p, --parallel=<n> Run in parallel and use <n> 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 <mj\@ucw.cz>\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);
+++ /dev/null
-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
+++ /dev/null
-$(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))
+++ /dev/null
-================================================================================
-
- UCW::Gallery v2.0
-
- (c) 2004--2015 Martin Mares <mj@ucw.cz>
-
-================================================================================
-
-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".
+++ /dev/null
-# Simple Photo Gallery
-# (c) 2003--2015 Martin Mares <mj@ucw.cz>
-
-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;
+++ /dev/null
-# Simple Photo Gallery: Archiving
-# (c) 2003--2012 Martin Mares <mj@ucw.cz>
-
-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;
+++ /dev/null
-# Simple Photo Gallery: Image Hashes
-# (c) 2015 Martin Mares <mj@ucw.cz>
-
-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;
+++ /dev/null
-# Simple Photo Gallery: Web Interface
-# (c) 2003--2012 Martin Mares <mj@ucw.cz>
-
-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 "<p style='color:red'>Bad luck, the script is broken. Sorry.\n<p>$_[0]\n";
- print "</body></html>\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 <<EOF ;
-Content-Type: text/html
-
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
-<html><head>
-$hextras$theme_hextras<title>$title</title>
-</head><body>
-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'), "</body></html>\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 "<h1>$t</h1>\n" if $t ne "";
- my $img = $self->get('PhotoUrlPrefix') . $self->{gal}->photo_file_name($m, $id);
- print "<p class=large><img src='$img' width=$w height=$h alt='$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 "<h1>", $self->get('Title'), "</h1>\n";
- my $subtitle = $self->get('SubTitle');
- print "<h2>$subtitle</h2>\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;
+++ /dev/null
-# Highslide JS Theme for MJ's Photo Gallery
-# (c) 2012 Martin Mares <mj@ucw.cz>; 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 ;
-<link rel="stylesheet" type="text/css" href="$hsdir/custom.css">
-AMEN_MINI
-<script type="text/javascript" src="$hsdir/highslide-with-gallery.js"></script>
-<script type="text/javascript" src="$hsdir/custom.js" charset="utf-8"></script>
-<script type="text/javascript">
-hs.graphicsDir = '$hsdir/graphics/';
-</script>
-<link rel="stylesheet" type="text/css" href="$hsdir/highslide.css">
-<link rel="stylesheet" type="text/css" href="$hsdir/custom.css">
-<!--[if lt IE 7]>
-<link rel="stylesheet" type="text/css" href="$hsdir/highslide-ie6.css">
-<![endif]-->
-AMEN_FULL
-}
-
-sub show_links($$$$) {
- my ($self, $prev, $up, $next) = @_;
- my $nav = $self->get('ThemeUrlPrefix') . "highslide/nav";
- print "<p class=parent>";
- print "<a href='$prev'><img class=back prev src='$nav/prev.png'></a>" if $prev ne "";
- printf "<a href='$next'><img class=fwd src='$nav/next.png'></a>" if $next ne "";
- printf "<a href='$up'><img class=up src='$nav/back.png'></a>" if $up ne "";
-}
-
-sub show_pre_thumbs($) {
- my ($self) = @_;
- print "\n<div class='highslide-gallery'><ul>\n";
- $self->{hs_thumb_counter} = 0;
-}
-
-sub show_post_thumbs($) {
- my ($self) = @_;
- print "</ul></div>\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 "<li><a id='$aid' href='$click_url' class=highslide onclick='return hs.expand(this, { src: \"$photo_url\" })'>";
- print "<img src='$thumb' width=$tw height=$th alt='Photo'$tit></a>\n";
- if ($self->get('GeoHack')) {
- my ($lat, $lon) = ($m->{lat}, $m->{lon});
- if (defined $lat && defined $lon) {
- my $local = "<a href='map.cgi?i=" . $self->{hs_thumb_counter} . "'>trasa</a>";
- my $osm = "<a href='http://www.openstreetmap.org/?mlat=$lat&mlon=$lon#map=16/$lat/$lon'>OSM</a>";
- print "<div class='highslide-caption'>Ukázat na mapě: $local, $osm</div>\n";
- }
- }
-}
-
-sub run($$) {
- my ($class, $gal) = @_;
- my $self = $class->SUPER::attach($gal);
- $self->dispatch();
- return $self;
-}
-
-1;
+++ /dev/null
-# NRT Theme for MJ's Photo Gallery
-# (c) 2003--2012 Martin Mares <mj@ucw.cz>; 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 "<link rel=stylesheet href='$stylesheet' type='text/css' media=all>\n";
-}
-
-sub show_links($$$$) {
- my ($self, $prev, $up, $next) = @_;
- my $theme = $self->theme_dir;
- print "<p class=parent>";
- print "<span class=back style='width: ${navw}px; height: ${navh}px'>";
- print "<a href='$prev'><img src='$theme/prev.png' width=${navw} height=${navh} alt='Back'></a>" if $prev ne "";
- print "</span>\n";
- printf "<span class=fwd style='width: ${navw}px; height: ${navh}px'>";
- printf "<a href='$next'><img src='$theme/next.png' width=${navw} height=${navh} alt='Forward'></a>" if $next ne "";
- print "</span>\n";
- printf "<a href='$up'><img src='$theme/back.png' width=${navw} height=${navh} alt='Up'></a>" 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 "<div class=thf><div class=thumb>\n";
- print "<img src='$theme/top.png' width=$box_w height=$top_h alt='' class=tt>\n";
- print "<img src='$theme/left.png' width=$left_w height=$side_h alt='' class=tl>\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 "<a href='$click_url'><img src='$thumb' width=$tw height=$th alt='Photo'$tit class=ti style='left: ${ol}px; top: ${ot}px'></a>\n";
- print "<img src='$theme/right.png' width=$right_w height=$side_h alt='' class=tr>\n";
- print "<img src='$theme/bot.png' width=$box_w height=$bot_h alt='' class=tb>\n";
- print "</div></div>\n\n";
-}
-
-sub run($$) {
- my ($class, $gal) = @_;
- my $self = $class->SUPER::attach($gal);
- $self->dispatch();
- return $self;
-}
-
-1;
+++ /dev/null
-# Plain Theme for MJ's Photo Gallery
-# (c) 2012 Martin Mares <mj@ucw.cz>; 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 "<link rel=stylesheet href='$stylesheet' type='text/css' media=all>\n";
-}
-
-sub show_links($$$$) {
- my ($self, $prev, $up, $next) = @_;
- my $theme = $self->theme_dir;
- print "<p class=parent>";
- print "<span class=back style='width: ${navw}px; height: ${navh}px'>";
- print "<a href='$prev'><img src='$theme/prev.png' width=${navw} height=${navh} alt='Back'></a>" if $prev ne "";
- print "</span>\n";
- printf "<span class=fwd style='width: ${navw}px; height: ${navh}px'>";
- printf "<a href='$next'><img src='$theme/next.png' width=${navw} height=${navh} alt='Forward'></a>" if $next ne "";
- print "</span>\n";
- printf "<a href='$up'><img src='$theme/back.png' width=${navw} height=${navh} alt='Up'></a>" 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 "<div class=thf><div class=thumb>\n";
- my $ol = int(($box_w - $tw)/2);
- my $ot = int(($box_h - $th)/2);
- my $tit = ($annot ne "") ? " title=\"$annot\"" : "";
- print "<a href='$click_url'><img src='$thumb' width=$tw height=$th alt='Photo'$tit class=ti style='left: ${ol}px; top: ${ot}px'></a>\n";
- print "</div></div>\n\n";
-}
-
-sub run($$) {
- my ($class, $gal) = @_;
- my $self = $class->SUPER::attach($gal);
- $self->dispatch();
- return $self;
-}
-
-1;
+++ /dev/null
-#!/usr/bin/perl
-# UCW Gallery: Prepare cache
-# (c) 2004--2012 Martin Mares <mj@ucw.cz>
-
-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);
+++ /dev/null
-#!/usr/bin/perl
-# UCW Gallery: Symlink photos according to their EXIF timestamps
-# (c) 2013 Martin Mares <mj@ucw.cz>
-
-use strict;
-use warnings;
-
-use Image::EXIF;
-use Getopt::Long;
-
-if (@ARGV && $ARGV[0] eq '--help') {
- die <<AMEN ;
-Usage: cat list | gal date [<options>]
- or: gal date [<options>] <files>
-
-Options:
--d, --destdir=<dir> Create symlinks in specified directory (default: current dir)
--n, --dry-run Perform a trial run with no changes made
--o, --offset=<offset> Adjust timestamps by a given offset (<h>[:<m>[:<s>]])
- --suffix=<s> 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 (<STDIN>) {
- 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);
+++ /dev/null
-#!/usr/bin/perl
-# UCW Gallery: Show configuration variables
-# (c) 2004--2012 Martin Mares <mj@ucw.cz>
-
-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;
-}
+++ /dev/null
-#!/usr/bin/perl
-# UCW Gallery: Dump meta-data
-# (c) 2004--2012 Martin Mares <mj@ucw.cz>
-
-use strict;
-use warnings;
-
-use UCW::Gallery;
-use Data::Dumper;
-
-@ARGV == 1 or die "Usage: $0 <meta-file>\n";
-
-my $gal = UCW::Gallery->load_config;
-my $meta = $gal->read_meta($ARGV[0]);
-print Dumper($meta);
+++ /dev/null
-#!/usr/bin/perl
-# UCW Gallery: Extract image list from GQview collection
-# (c) 2004--2012 Martin Mares <mj@ucw.cz>
-
-use strict;
-use warnings;
-
-while (<>) {
- chomp;
- /^#/ && next;
- /^$/ && next;
- if (/^"(.*)"$/) {
- print "$1\n";
- } else {
- die "Error parsing collection: $_";
- }
-}
+++ /dev/null
-#!/usr/bin/perl
-# UCW Gallery: Generate published photos
-# (c) 2004--2015 Martin Mares <mj@ucw.cz>
-
-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;
+++ /dev/null
-#!/usr/bin/perl
-
-use strict;
-use warnings;
-
-use Cwd;
-use DBI;
-use Getopt::Long;
-
-if (@ARGV && $ARGV[0] eq '--help') {
- die <<AMEN ;
-Usage: gal mj-digikam [<album>]
-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( <<AMEN, { Slice => {} },
- 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;
+++ /dev/null
-#!/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
+++ /dev/null
-#!/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,
-}, ".");
+++ /dev/null
-#!/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 (<T>) {
- 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";
- }
- }
-}
+++ /dev/null
-#!/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 <album>\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 (<M>) {
- 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 (<S>) {
- 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 (<I>) {
- 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;
+++ /dev/null
-#!/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";
+++ /dev/null
-#!/usr/bin/perl
-# UCW Gallery: Scan images and generate image list
-# (c) 2004--2015 Martin Mares <mj@ucw.cz>
-
-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 <<AMEN ;
-Usage: cat list | gal scan <options>
- or: gal scan <options> <files and directories>
- or: gal scan <options> --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;
+++ /dev/null
-#!/usr/bin/perl
-# UCW Gallery -- Master Program
-# (c) 2012 Martin Mares <mj@ucw.cz>
-
-use strict;
-use warnings;
-use Getopt::Long;
-
-use FindBin;
-my $gallery_root = $FindBin::Bin;
-
-my $all;
-my $parallel;
-
-sub show_help() {
- print <<AMEN ;
-Usage: gal <general-options> <command> <command-options> <args>
-
-General options:
- --all Run on all galleries in subdirectories
--p, --parallel=<n> Run in parallel and use <n> 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 <mj\@ucw.cz>\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);
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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;
+++ /dev/null
-.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;
-}
+++ /dev/null
-/**
- * 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 <i>Highslide JS</i>',
- 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:
- '<div class="highslide-controls"><ul>'+
- '<li class="highslide-previous">'+
- '<a href="#" title="{hs.lang.previousTitle}">'+
- '<span>{hs.lang.previousText}</span></a>'+
- '</li>'+
- '<li class="highslide-play">'+
- '<a href="#" title="{hs.lang.playTitle}">'+
- '<span>{hs.lang.playText}</span></a>'+
- '</li>'+
- '<li class="highslide-pause">'+
- '<a href="#" title="{hs.lang.pauseTitle}">'+
- '<span>{hs.lang.pauseText}</span></a>'+
- '</li>'+
- '<li class="highslide-next">'+
- '<a href="#" title="{hs.lang.nextTitle}">'+
- '<span>{hs.lang.nextText}</span></a>'+
- '</li>'+
- '<li class="highslide-move">'+
- '<a href="#" title="{hs.lang.moveTitle}">'+
- '<span>{hs.lang.moveText}</span></a>'+
- '</li>'+
- '<li class="highslide-full-expand">'+
- '<a href="#" title="{hs.lang.fullExpandTitle}">'+
- '<span>{hs.lang.fullExpandText}</span></a>'+
- '</li>'+
- '<li class="highslide-close">'+
- '<a href="#" title="{hs.lang.closeTitle}" >'+
- '<span>{hs.lang.closeText}</span></a>'+
- '</li>'+
- '</ul></div>'
-},
-// 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 =
- '<div class="highslide-number">'+ s +'</div>'+ 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:
- '<div class="highslide-thumbstrip-inner">'+
- '<'+ tree[0] +'><'+ tree[1] +'></'+ tree[1] +'></'+ tree[0] +'></div>'+
- '<div class="highslide-scroll-up"><div></div></div>'+
- '<div class="highslide-scroll-down"><div></div></div>'+
- '<div class="highslide-marker"><div></div></div>'
- }, {
- 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);
-}
+++ /dev/null
-/**
-* @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;
-}
+++ /dev/null
-.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; }
+++ /dev/null
-.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; }
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
--- /dev/null
+.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;
+}
--- /dev/null
+/**
+ * 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 <i>Highslide JS</i>',
+ 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:
+ '<div class="highslide-controls"><ul>'+
+ '<li class="highslide-previous">'+
+ '<a href="#" title="{hs.lang.previousTitle}">'+
+ '<span>{hs.lang.previousText}</span></a>'+
+ '</li>'+
+ '<li class="highslide-play">'+
+ '<a href="#" title="{hs.lang.playTitle}">'+
+ '<span>{hs.lang.playText}</span></a>'+
+ '</li>'+
+ '<li class="highslide-pause">'+
+ '<a href="#" title="{hs.lang.pauseTitle}">'+
+ '<span>{hs.lang.pauseText}</span></a>'+
+ '</li>'+
+ '<li class="highslide-next">'+
+ '<a href="#" title="{hs.lang.nextTitle}">'+
+ '<span>{hs.lang.nextText}</span></a>'+
+ '</li>'+
+ '<li class="highslide-move">'+
+ '<a href="#" title="{hs.lang.moveTitle}">'+
+ '<span>{hs.lang.moveText}</span></a>'+
+ '</li>'+
+ '<li class="highslide-full-expand">'+
+ '<a href="#" title="{hs.lang.fullExpandTitle}">'+
+ '<span>{hs.lang.fullExpandText}</span></a>'+
+ '</li>'+
+ '<li class="highslide-close">'+
+ '<a href="#" title="{hs.lang.closeTitle}" >'+
+ '<span>{hs.lang.closeText}</span></a>'+
+ '</li>'+
+ '</ul></div>'
+},
+// 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 =
+ '<div class="highslide-number">'+ s +'</div>'+ 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:
+ '<div class="highslide-thumbstrip-inner">'+
+ '<'+ tree[0] +'><'+ tree[1] +'></'+ tree[1] +'></'+ tree[0] +'></div>'+
+ '<div class="highslide-scroll-up"><div></div></div>'+
+ '<div class="highslide-scroll-down"><div></div></div>'+
+ '<div class="highslide-marker"><div></div></div>'
+ }, {
+ 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);
+}
--- /dev/null
+/**
+* @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;
+}
--- /dev/null
+# Simple Photo Gallery
+# (c) 2003--2015 Martin Mares <mj@ucw.cz>
+
+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;
--- /dev/null
+# Simple Photo Gallery: Archiving
+# (c) 2003--2012 Martin Mares <mj@ucw.cz>
+
+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;
--- /dev/null
+# Simple Photo Gallery: Image Hashes
+# (c) 2015 Martin Mares <mj@ucw.cz>
+
+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;
--- /dev/null
+# Simple Photo Gallery: Web Interface
+# (c) 2003--2012 Martin Mares <mj@ucw.cz>
+
+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 "<p style='color:red'>Bad luck, the script is broken. Sorry.\n<p>$_[0]\n";
+ print "</body></html>\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 <<EOF ;
+Content-Type: text/html
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<html><head>
+$hextras$theme_hextras<title>$title</title>
+</head><body>
+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'), "</body></html>\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 "<h1>$t</h1>\n" if $t ne "";
+ my $img = $self->get('PhotoUrlPrefix') . $self->{gal}->photo_file_name($m, $id);
+ print "<p class=large><img src='$img' width=$w height=$h alt='$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 "<h1>", $self->get('Title'), "</h1>\n";
+ my $subtitle = $self->get('SubTitle');
+ print "<h2>$subtitle</h2>\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;
--- /dev/null
+# Highslide JS Theme for MJ's Photo Gallery
+# (c) 2012 Martin Mares <mj@ucw.cz>; 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 ;
+<link rel="stylesheet" type="text/css" href="$hsdir/custom.css">
+AMEN_MINI
+<script type="text/javascript" src="$hsdir/highslide-with-gallery.js"></script>
+<script type="text/javascript" src="$hsdir/custom.js" charset="utf-8"></script>
+<script type="text/javascript">
+hs.graphicsDir = '$hsdir/graphics/';
+</script>
+<link rel="stylesheet" type="text/css" href="$hsdir/highslide.css">
+<link rel="stylesheet" type="text/css" href="$hsdir/custom.css">
+<!--[if lt IE 7]>
+<link rel="stylesheet" type="text/css" href="$hsdir/highslide-ie6.css">
+<![endif]-->
+AMEN_FULL
+}
+
+sub show_links($$$$) {
+ my ($self, $prev, $up, $next) = @_;
+ my $nav = $self->get('ThemeUrlPrefix') . "highslide/nav";
+ print "<p class=parent>";
+ print "<a href='$prev'><img class=back prev src='$nav/prev.png'></a>" if $prev ne "";
+ printf "<a href='$next'><img class=fwd src='$nav/next.png'></a>" if $next ne "";
+ printf "<a href='$up'><img class=up src='$nav/back.png'></a>" if $up ne "";
+}
+
+sub show_pre_thumbs($) {
+ my ($self) = @_;
+ print "\n<div class='highslide-gallery'><ul>\n";
+ $self->{hs_thumb_counter} = 0;
+}
+
+sub show_post_thumbs($) {
+ my ($self) = @_;
+ print "</ul></div>\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 "<li><a id='$aid' href='$click_url' class=highslide onclick='return hs.expand(this, { src: \"$photo_url\" })'>";
+ print "<img src='$thumb' width=$tw height=$th alt='Photo'$tit></a>\n";
+ if ($self->get('GeoHack')) {
+ my ($lat, $lon) = ($m->{lat}, $m->{lon});
+ if (defined $lat && defined $lon) {
+ my $local = "<a href='map.cgi?i=" . $self->{hs_thumb_counter} . "'>trasa</a>";
+ my $osm = "<a href='http://www.openstreetmap.org/?mlat=$lat&mlon=$lon#map=16/$lat/$lon'>OSM</a>";
+ print "<div class='highslide-caption'>Ukázat na mapě: $local, $osm</div>\n";
+ }
+ }
+}
+
+sub run($$) {
+ my ($class, $gal) = @_;
+ my $self = $class->SUPER::attach($gal);
+ $self->dispatch();
+ return $self;
+}
+
+1;
--- /dev/null
+# NRT Theme for MJ's Photo Gallery
+# (c) 2003--2012 Martin Mares <mj@ucw.cz>; 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 "<link rel=stylesheet href='$stylesheet' type='text/css' media=all>\n";
+}
+
+sub show_links($$$$) {
+ my ($self, $prev, $up, $next) = @_;
+ my $theme = $self->theme_dir;
+ print "<p class=parent>";
+ print "<span class=back style='width: ${navw}px; height: ${navh}px'>";
+ print "<a href='$prev'><img src='$theme/prev.png' width=${navw} height=${navh} alt='Back'></a>" if $prev ne "";
+ print "</span>\n";
+ printf "<span class=fwd style='width: ${navw}px; height: ${navh}px'>";
+ printf "<a href='$next'><img src='$theme/next.png' width=${navw} height=${navh} alt='Forward'></a>" if $next ne "";
+ print "</span>\n";
+ printf "<a href='$up'><img src='$theme/back.png' width=${navw} height=${navh} alt='Up'></a>" 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 "<div class=thf><div class=thumb>\n";
+ print "<img src='$theme/top.png' width=$box_w height=$top_h alt='' class=tt>\n";
+ print "<img src='$theme/left.png' width=$left_w height=$side_h alt='' class=tl>\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 "<a href='$click_url'><img src='$thumb' width=$tw height=$th alt='Photo'$tit class=ti style='left: ${ol}px; top: ${ot}px'></a>\n";
+ print "<img src='$theme/right.png' width=$right_w height=$side_h alt='' class=tr>\n";
+ print "<img src='$theme/bot.png' width=$box_w height=$bot_h alt='' class=tb>\n";
+ print "</div></div>\n\n";
+}
+
+sub run($$) {
+ my ($class, $gal) = @_;
+ my $self = $class->SUPER::attach($gal);
+ $self->dispatch();
+ return $self;
+}
+
+1;
--- /dev/null
+# Plain Theme for MJ's Photo Gallery
+# (c) 2012 Martin Mares <mj@ucw.cz>; 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 "<link rel=stylesheet href='$stylesheet' type='text/css' media=all>\n";
+}
+
+sub show_links($$$$) {
+ my ($self, $prev, $up, $next) = @_;
+ my $theme = $self->theme_dir;
+ print "<p class=parent>";
+ print "<span class=back style='width: ${navw}px; height: ${navh}px'>";
+ print "<a href='$prev'><img src='$theme/prev.png' width=${navw} height=${navh} alt='Back'></a>" if $prev ne "";
+ print "</span>\n";
+ printf "<span class=fwd style='width: ${navw}px; height: ${navh}px'>";
+ printf "<a href='$next'><img src='$theme/next.png' width=${navw} height=${navh} alt='Forward'></a>" if $next ne "";
+ print "</span>\n";
+ printf "<a href='$up'><img src='$theme/back.png' width=${navw} height=${navh} alt='Up'></a>" 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 "<div class=thf><div class=thumb>\n";
+ my $ol = int(($box_w - $tw)/2);
+ my $ot = int(($box_h - $th)/2);
+ my $tit = ($annot ne "") ? " title=\"$annot\"" : "";
+ print "<a href='$click_url'><img src='$thumb' width=$tw height=$th alt='Photo'$tit class=ti style='left: ${ol}px; top: ${ot}px'></a>\n";
+ print "</div></div>\n\n";
+}
+
+sub run($$) {
+ my ($class, $gal) = @_;
+ my $self = $class->SUPER::attach($gal);
+ $self->dispatch();
+ return $self;
+}
+
+1;
--- /dev/null
+.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; }
--- /dev/null
+.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; }