# Lightbox Generator

Warning: this is a complex challenge.

## Overview

A lightbox, in the context of computing, is a visual effect used in image sites. A list of thumbnail images is displayed; each image can be selected and will be displayed on top of the contents of the page either at full size or to fill the screen.

The task is to generate a (fairly) self-contained web page which will display specified images in a lightbox.

## Input

The input will be from stdin and will specify (one item per line):

• The number of images (n)
• A pattern for transforming an image name into its thumbnail (substitution described in detail further down)
• A list of n images, each consisting of a URL for the full-sized image and a one-line description. You may assume that each URL ends with an extension (e.g. .jpg), although you cannot assume any extension in particular.

Sample input:

4
*_thumb.jpg
DSCF0001.jpg
My family
DSCF0002.jpg
Other animals
crop/DSCF0017.jpg
Aren't they cute?
http://www.example.com/fx.jpg
The bestest picture on the whole Internet!!!


Note: when the URLs are relative (the first three in this example), you may assume that they are relative to the current working directory. This is probably only relevant if you want to load them in your program to find their sizes.

Substitution into the thumbnail pattern works by removing the file extension from the base URL and replacing the * in the pattern. So for the four images in the example above, you would get

DSCF0001_thumb.jpg
DSCF0002_thumb.jpg
crop/DSCF0017_thumb.jpg
http://www.example.com/fx_thumb.jpg


If the pattern were thumbs/*.jpg you would get

thumbs/DSCF0001.jpg
thumbs/DSCF0002.jpg
thumbs/crop/DSCF0017.jpg
thumbs/http://www.example.com/fx.jpg


The fact that the last one there is clearly nonsense would be a bug in the input rather than in your program.

## Output

The output must include some HTML, and may additionally include some other files (e.g. separate JavaScript, CSS, or images). You may emit the main HTML to stdout or to a file named index.html. Please state which you choose to do.

The HTML must include a doctype and be valid for that doctype (I suggest using HTML5!). When it loads, it must display the thumbnails, which must have some separation (margin or padding) between them, and should not be one per line. Each thumbnail must either have a title attribute or be in an element with a title attribute which displays the corresponding image description. It must also have an alt attribute which is the same description.

Clicking on a thumbnail must open the lightbox. This consists of a translucent black overlay covering the entire screen, on top of which the following are displayed in a white rectangle with a padding of at least 8px:

• The full image (scaled as required - see below)
• The image description
• Buttons or links to go forward and back, and to close the lightbox

It is up to you whether you overlay the description and buttons over the image or put them in the margin around it.

If everything fits within the browser's viewport with the image at full size, it should be displayed at full size. Otherwise it should be scaled down, preserving aspect ratio, so that everything fits.

The forward and back (or next and previous - your choice) buttons must load the corresponding images. It is your choice as to whether they loop or stop at the ends. There should be a simple but smooth transition between the images - if the (scaled) images are different sizes, then it shouldn't jump, and you should use some kind of fade or sweep.

The following features of many lightboxes are not required:

• Closing if the user clicks on the translucent black overlay
• Resizing everything correctly if the browser window is resized

## Permitted external resources

Use of external resources is restricted to using at most one of the following JavaScript libraries:

• jQuery
• Prototype
• mootools

You may assume that it is in the same directory as the output html, but if you do so you should document the name which you expect it to have and the version which you require.

## Compatibility

Compatibility with IE is nice, but this is a code golf, and apart from that it's not something which is easily tested without a Windows machine, so it is not a requirement. Please state which browser(s) you have tested with in your answer. I will test with Chromium, and I also have Firefox 3.6 and Konqueror available at home (plus various versions of IE, Win Safari and Opera in my lunchtimes at work, so if you want me to test on one of those, say so).

Note: this question has been sitting in the Sandbox for two weeks, so if you think there's a problem with the specification, please make it a habit to comment on questions over there.

• I guess that the reason nobody wants to tackle this is because it's too much work involved. You basically want participants to develop a lightbox as a product of solving quiz with all things considered such as a programming language, css, javascript, html, and more. It sounds a full-time job for me. – Paul Sep 2 '12 at 0:57
• I think that if you can allow to include jquery ui, there might be some to try it out. – Paul Sep 2 '12 at 1:00
• @Paul, it's only been two days. For all you know, someone is already working on an answer. The intention was to post a more meaty challenge than the majority. – Peter Taylor Sep 2 '12 at 7:29
• Just one quick question - can I assume that jQuery is in the same directory as the page or do I need to include it from a CDN like Google's? – Gareth Sep 3 '12 at 16:26
• @Gareth, good question. Assume that it's in the same directory and give it a short name if you want - but document the assumption clearly. – Peter Taylor Sep 3 '12 at 19:10

## PHP, HTML, CSS and jQuery, 15511482 1511 characters

<?while($x=fgets(STDIN))$i[]=chop($x)?><!doctype html><title></title><style>.t{float:left;margin:10px;height:50px}#o,#p,p,b,i,q{position:fixed;top:0}#o{background:rgba(0,0,0,0.8);width:100%;height:100%;display:none;left:0}#p{display:none;z-index:1;border:10px solid #fff;top:50%;left:50%}p{padding:5px;background:rgba(50,50,50,0.7);color:#ccc;z-index:2;top:50%;left:50%}b,i,q{padding:10px;color:#f00;background:#ccc;z-index:2}b{right:0;font-weight:900}i{top:50%;left:0}q{top:50%;right:0}</style><script src=a></script><script>$(function(){$('img').click(function(){a=$(this);$('body').append('<b>X</b><i>&lt;</i><q>&gt;</q><div id=o>');z($(this).attr('data-l'),$(this).attr('alt'))});$('body').on('click','b',function(){$('b,i,q,p,#o,#p').fadeOut(function(){$(this).remove();})}).on('click','q,i',function(){f=$(this).is('q')?a.next('img'):a.prev();$('#p,p').fadeOut(function(){$(this).remove();a=f.size()==0?a:f;z(a.attr('data-l'),a.attr('alt'))})})});function z(l,m){if($('#p').size()<1){$('body').append('<img id=p src='+l+'><p>'+m);i=new Image();i.onload=function(){w=i.width;h=i.height;$('#p').css({maxWidth:$('body').innerWidth()-20,maxHeight:$('body').innerHeight()-20,marginLeft:0-w/2,marginTop:0-h/2});$('p').css({marginLeft:0-$('p').width()/2,marginTop:w/2-30});$('#o,#p').fadeIn()};i.src=l}}</script><?for(;$l<$i[0]*2;$l+=2){$a=str_replace('"','&quot;',$i[2+$l]);$b=$i[3+$l];echo'<img class=t src='.str_replace("*",substr($a,0,strrpos($a,".")),$i[1]).' title="'.$b.'" alt="'.$b.'" data-l='.$a.'>';}?>


I've assumed that the jQuery library is available in the same directory as this file, and that it is named a. The HTML takes advantage of the fact that a lot of the tags are optional but at it's base it is only:

<!doctype html>
<title></title>
<img alt=title src=http://placehold.it/60 data-l=http://placehold.it/180>
...more images here...


which is considered valid by the w3c validator.

The CSS still contains some repeated styles which probably offers some room for further compression.

I've created a jsFiddle which demonstrates the lightbox functionality for those who wish to try it. One time when I ran it there was a glitch with the images when they were first loaded - please let me know if it's a regular thing so I can have a look into it.

I've only tested it in Chrome on Mac OSX (using the above jsFiddle), and on Chrome on Windows XP using the php to generate the page (php -q lb.php > lb.html) with the following input:

4
http://lorempixel.com/60/60
http://lorempixel.com/250/250
My family
http://lorempixel.com/300/300
Other animals
http://lorempixel.com/350/350
Aren't they cute?
http://lorempixel.com/400/400
The bestest picture on the whole internet!!!


which generates the following page source:

<!doctype html><title></title><style>.t{float:left;margin:10px;height:50px}#o,#p,p,b,i,q{position:fixed;top:0}#o{background:rgba(0,0,0,0.8);width:100%;height:100%;display:none;left:0}#p{display:none;z-index:1;border:10px solid #fff;top:50%;left:50%}p{padding:5px;background:rgba(50,50,50,0.7);color:#ccc;z-index:2;top:50%;left:50%}b,i,q{padding:10px;color:#f00;background:#ccc;z-index:2}b{right:0;font-weight:900}i{top:50%;left:0}q{top:50%;right:0}</style><script src=http://ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js></script><script>$(function(){$('img').click(function(){a=$(this);$('body').append('<b>X</b><i>&lt;</i><q>&gt;</q><div id=o>');z($(this).attr('data-l'),$(this).attr('alt'))});$('body').on('click','b',function(){$('b,i,q,p,#o,#p').fadeOut(function(){$(this).remove();})}).on('click','q,i',function(){f=$(this).is('q')?a.next('img'):a.prev();$('#p,p').fadeOut(function(){$(this).remove();a=f.size()==0?a:f;z(a.attr('data-l'),a.attr('alt'))})})});function z(l,m){if($('#p').size()<1){$('body').append('<img id=p src='+l+'><p>'+m);i=new Image();i.onload=function(){w=i.width;h=i.height;$('#p').css({maxWidth:$('body').innerWidth()-20,maxHeight:$('body').innerHeight()-20,marginLeft:0-w/2,marginTop:0-h/2});$('p').css({marginLeft:0-$('p').width()/2,marginTop:w/2-30});$('#o,#p').fadeIn()};i.src=l}}</script><img class=t src=http://lorempixel.com/60/60 title="My family" alt="My family" data-l=http://lorempixel.com/250/250><img class=t src=http://lorempixel.com/60/60 title="Other animals" alt="Other animals" data-l=http://lorempixel.com/300/300><img class=t src=http://lorempixel.com/60/60 title="Aren't they cute?" alt="Aren't they cute?" data-l=http://lorempixel.com/350/350><img class=t src=http://lorempixel.com/60/60 title="The bestest picture on the whole internet!!!" alt="The bestest picture on the whole internet!!!" data-l=http://lorempixel.com/400/400>


I've fixed a few issues that weren't apparent in the jsFiddle testing - the only real issue I'm aware of now is that in Chrome the > button gets quotes put around it because I'm using the q tag. Not sure if the default styles in Firefox, Opera and IE do the same.

I think I've met all the requirements - again let me know if I've missed anything.

Edit:

I've removed all the ul and li stuff because it wasn't necessary, saving about 70 characters.

Here's the code (of the previous version with ul and lis) with some line-breaks and indentation to aid readability, and a Google CDN jQuery link:

<?while($x=fgets(STDIN))$i[]=chop($x)?> <!doctype html> <title></title> <style> ul{list-style-type:none} li{float:left;margin:10px;padding:0} li img{height:50px} #o,#p,p,b,i,q{position:fixed;top:0} #o{background:rgba(0,0,0,0.8);width:100%;height:100%;display:none;left:0} #p{display:none;z-index:1;border:10px solid #fff;top:50%;left:50%} p{padding:5px;background:rgba(50,50,50,0.7);color:#ccc;z-index:2;top:50%;left:50%} b,i,q{padding:10px;color:#f00;background:#ccc;z-index:2} b{right:0;font-weight:900} i{top:50%;left:0} q{top:50%;right:0} </style> <script src=http://ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js></script> <script>$(function()
{
$('img').click(function() { a=$(this).parent();
$('body').append('<b>X</b><i>&lt;</i><q>&gt;</q><div id=o>'); z($(this).attr('data-l'),$(this).attr('alt')) });$('body').on('click','b',function()
{
$('b,i,q,p,#o,#p').fadeOut(function(){$(this).remove();})
}).on('click','q,i',function()
{
f=$(this).is('q')?a.next():a.prev();$('#p,p').fadeOut(function()
{
$(this).remove(); a=f.size()==0?a:f; b=a.find('img'); z(b.attr('data-l'),b.attr('alt')) }) }) }) function z(l,m) { if($('#p').size()<1)
{
$('body').append('<img id=p src='+l+'><p>'+m); i=new Image(); i.onload=function() { w=i.width; h=i.height;$('#p').css({maxWidth:$('body').innerWidth()-20,maxHeight:$('body').innerHeight()-20,marginLeft:0-w/2,marginTop:0-h/2});
$('p').css({marginLeft:0-$('p').width()/2,marginTop:w/2-30});
$('#o,#p').fadeIn(); } i.src=l; } } </script> <ul> <?for(;$l<$i[0]*2;$l+=2)
{
$a=$i[2+$l];$b=$i[3+$l];
echo"<li><img src=".str_replace("*",substr($a,0,strrpos($a,".")),$i[1])." title='".$b."' alt='".$b."' data-l=".$a.">";
}?>
</ul>

• @PeterTaylor Oops. Swapped the images round. – Gareth Sep 6 '12 at 7:23
• @PeterTaylor I've fixed a number of bugs and layout issues and compressed the JS correctly now (I think there was a missing ; or two in the previous version). – Gareth Sep 6 '12 at 11:32
• It's not quite correct. It seems to work fine for small images, but for larger ones it isn't scaling them to fit the viewport. Also the changes have made the HTML invalid - the W3C validator complains about having multiple elements with id=t. Also your example demonstrates that the description needs escaping - look what happens to the description with an apostrophe. – Peter Taylor Sep 6 '12 at 19:35
• @PeterTaylor I've fixed the multiple id issue at a cost of 3 characters, and the quote escaping at a cost of 26 characters. I thought using max-height and max-width would handle the scaling requirement for me. I won't have time to have a look at that problem for a couple of days now, unfortunately. – Gareth Sep 6 '12 at 21:55