Gallery 2 + Lightbox


As promised, here’s an explanation of how I created the cool^H^H^H^Hcheesy effects in the new gallery. In case you haven’t seen, head over to the updates page, wait for it to finish loading, then click on one of the images.

Cool, huh?

The effect was done in conjuction with a JavaScript file(s) known as Lightbox, which is a Creative Commons licensed toolkit to create nice transition effects. Integration required two main tasks:

  1. Modifying Gallery 2 to support the new transitions
  2. Modifying Lightbox to fit vertical images to the screen size

There was also some work done in adding keystrokes to Lightbox (in addition to the basic X, N, and P, I added F (full view) and Z (zoom/unzoom)). Finally, I made some changes to Lightbox to make it Gallery specific, so that I don’t need to pass hideously complicated HTML as part of the title aspect.

Let’s begin.

The first major change I had to make was in the theme, to support the ResizedId field. This is because I pass the link to the resized picture to Lightbox, which is what it displays. The theme on my page is (basically) classic, with some changes to wrap my content around it.

Before the line return array(null, 'theme.tpl');, add the following:

/* Add resizedId to child values */
if ($theme['children'] ) {
  foreach($theme['children'] as $key => $value ){
    if ($value['id'] ) {
      list($ret,$resizedIds) = GalleryCoreApi::fetchResizesByItemIds(array($value['id']));
      if ($ret) {
        return array($ret->wrap(__FILE__, __LINE__), null);
        }
    }
    if ($resizedIds ) {
      /* Find the best resized option
Use width/height max=650; */
      $lboxMaxEdge = 650;
      $resizedEdge = NULL;
      $resizedId = NULL;
      foreach($resizedIds[$value['id']] as $resize ){
        $width = $resize->getWidth();
        $height = $resize->getHeight();
        $edge = ( $width > $height )? $width : $height;
        if ($edge < = $lboxMaxEdge ) {
          if (!isset($resizedEdge) ) {
            $resizedId = $resize->getId();
            $resizedEdge = $edge;
          } else if ($edge > $resizedEdge ) {
            $resizedId = $resize->getId();
            $resizedEdge = $edge;
          }
        }
      }
      $theme['children'][$key]['resizedId'] = $resizedId;
    }
  }
}

This will allow us to reference the resized image. Now, moving on to the main theme, we have to edit theme.tpl to include the lightbox javascripts / css files.

Add the following to theme.tpl

<link rel="stylesheet" type="text/css" href="/path/to/lightbox.css" />
<script type="text/javascript" src="/path/to/prototype.js"></script>
<script type="text/javascript" src="/path/to/lightbox.js"></script>
<script type="text/javascript" src="/path/to/scriptaculous.js?load=effects"></script>

And finally, let’s use the lightbox! On or about line 80 in album.tpl, the code contains the following:

<div>
  {if isset($theme.params.$frameType) && isset($child.thumbnail)}
    {g->container type="imageframe.ImageFrame" frame=$theme.params.$frameType width=$child.thumbnail.width height=$child.thumbnail.height}
    <a href="{g->url arg1="view=core.ShowItem" arg2="itemId=`$child.id`"}">
    {g->image id="%ID%" item=$child image=$child.thumbnail class="%CLASS% giThumbnail"}
    </a>
    {/g->container}
  {elseif isset($child.thumbnail)}
    <a href="{g->url arg1="view=core.ShowItem" arg2="itemId=`$child.id`"}">
    {g->image item=$child image=$child.thumbnail class="giThumbnail"}
    </a>
  {else}
    <a href="{g->url arg1="view=core.ShowItem" arg2="itemId=`$child.id`"}"
    class="giMissingThumbnail">
    {g->text text="no thumbnail"}
    </a>
  {/if}
</div>

In other words, if we want a frame type and there’s a thumbnail, insert the frame and the thumbnail. If not, if there’s a thumbnail, insert that. If not, insert the words “no thumbnail.”

We’re going to modify that to have a link to lightbox, however, we have to also add a check to make sure that there’s no children (aka, that we’re not lightboxing an album thumbnail (unless you want to). The following code will do that nicely:

{if $child.canContainChildren}
  <a href="{g->url arg1="view=core.ShowItem" arg2="itemId=`$child.id`"}" > 
  {else}
    <a href="{g->url arg1="view=core.ShowItem" arg2="itemId=`$child.id`"}"
    imgLink="{g->url arg1="view=core.DownloadItem" arg2="itemId=`$child.resizedId`"}"
    title="{$child.title|markup}"
    pageLink="{g->url arg1="view=core.ShowItem" arg2="itemId=`$child.id`"}"
    rel="lightbox[photos]" >
  {/if}
</a>

This adds the following to each image:

  • The actual link remains the same. That way, browsers not supporting the JavaScript Lightbox will work just fine.
  • A new element, ImgLink, which Lightbox will look for to use as the resized image
  • A nice title, for mouseover events (and lightbox shows it as well
  • the rel="lightbox[photos]" which tells lightbox that this image is to be lightboxed. Additionally, adding it as part of the “array” (photos) lets us use the next and previous features of lightbox

That’s it for the changes to Gallery (whew).

Let’s move on to Lightbox.

I based my Lightbox on the script at JS Lightbox, version 2.02. However, as mentioned, I added zoom (and unzoom!) and some Gallery specific navigation. Some of the were based on code that I found on the Internet, but I don’t have specific cites for all of them. If you see anything here that looks familiar, please email me.

Let’s start with lightbox.css. First, I modified the paths of all my images, but that’s not really relevant to most people. Since I plan to have the overlaid navigation (right hand side for next, center for main, and left for previous), I needed to switch to the border box model. To do that, I added -moz-box-sizing: border-box; to the define for #prevLink, #nextLink{. I also switched their width from 49% to 20%. I added a new type as well, for the middle navigation:

#mainLink{
width: 60%;
-moz-box-sizing: border-box;
height: 100%;
background: transparent url(/path/to/blank.gif
display: block;
} 

#mainLink { left: 0; float:left;}
#mainLink:hover, #mainLink:visited:hover { background: url(/path/to/blank.gif);}

The final version of my lightbox.css is available directly from my website.

I added two images, for zooming and unzooming respectively, which can be gotten from expand.gif and contract.gif respectively.

The bulk of the changes, however, were in lightbox.js. First, I added three new variables to the begining of the file:

var fileBottomNavZoomImage = "/path/to/expand.gif";
var fileBottomNavUnzoomImage = "/path/to/contract.gif";
var zoomStatus;

Then, the main code was modified to take the source image from imgLink instead of href (after all, let’s leave the main link untouched).

Line 181: replace anchor.getAttribute('href') with anchor.getAttribute('imgLink'). This same change should also be made on lines 338 and 343.

To add the click-through, add the following code to the section that creates all the elements:

var objMainLink = document.createElement("a");
objMainLink.setAttribute('id','mainLink');
objMainLink.setAttribute('href','#');
objHoverNav.appendChild(objMainLink);

Zoom is inserted a little further down:

var objBottomNavZoomLink = document.createElement("a");
objBottomNavZoomLink.setAttribute('id','bottomNavZoomLink');
objBottomNavZoomLink.setAttribute('href','#');
objBottomNavZoomLink.onclick = function() {  myLightbox.changeImage(activeImage,'TRUE'); return false; }
objBottomNav.appendChild(objBottomNavZoomLink);

var objBottomNavZoomImage = document.createElement("img");
objBottomNavZoomImage.setAttribute('id', 'bottomNavZoomImage');
objBottomNavZoomImage.setAttribute('src', fileBottomNavZoomImage);
objBottomNavZoomLink.appendChild(objBottomNavZoomImage);

Since we want to use a neater method for passing data, replace line 331, which used to say imageArray.push(new Array(imageLink.getAttribute('href'), imageLink.getAttribute('title'))); with imageArray.push(new Array(imageLink.getAttribute('imgLink'), imageLink.getAttribute('title'), imageLink.getAttribute('pageLink')));

The same basic change is made on line 339, where the imageArray.push line becomes: imageArray.push(new Array(anchor.getAttribute('imgLink'), anchor.getAttribute('title'), anchor.getAttribute('pageLink')));

The sizing is modified slightly by replacing line 349’s var lightboxTop = arrayPageScroll[1] + (arrayPageSize[3] / 15); with var lightboxTop = arrayPageScroll[1] + 4;

The changeImage function has changed drastically, as it now support zooming. The first two changes simple modify the prototype from changeImage: function(imageNum) to changeImage: function(imageNum,zoom) and add Element.hide('mainLink'); to all of the hides. Then, replace the code between imgPreloader = new Image(); and imgPreloader.src = imageArray[activeImage][0] with:

imgPreloader = new Image();
// once image is preloaded, resize image container
if (zoom=="TRUE") {
  imgPreloader.onload=function(){
    Element.setSrc('lightboxImage', imageArray[activeImage][0]);
    photo.style.width = (imgPreloader.width)+ 'px';
    myLightbox.resizeImageContainer(imgPreloader.width, imgPreloader.height);
    zoomStatus = "TRUE";
  }

  document.getElementById('bottomNavZoomImage').src=fileBottomNavUnzoomImage ;
  document.getElementById('bottomNavZoomLink').onclick=function() { myLightbox.changeImage(activeImage); return false; };
} else {
  imgPreloader.onload=function(){
  Element.setSrc('lightboxImage', imageArray[activeImage][0]);
  //resize code
  var arrayPageSize = getPageSize();
  var targ = { w:arrayPageSize[2] - (borderSize * 2), h:arrayPageSize[3] - (borderSize * 10) };
  var orig = { w:imgPreloader.width, h:imgPreloader.height };
  Element.hide('bottomNavZoomLink');
  // shrink image with the same aspect
  var ratio = 1.0;
  if ((orig.w >= targ.w || orig.h >= targ.h) && orig.h && orig.w){
    ratio = ((targ.w / orig.w) < (targ.h / orig.h)) ? targ.w / orig.w : targ.h / orig.h;
    document.getElementById('bottomNavZoomImage').src=fileBottomNavZoomImage ;
    document.getElementById('bottomNavZoomLink').onclick=function() { myLightbox.changeImage(activeImage,"TRUE"); return false; };
    Element.show('bottomNavZoomLink');
    }
  var new_width  = Math.floor(orig.w * ratio);
  var new_height = Math.floor(orig.h * ratio);
  photo = document.getElementById('lightboxImage');
  photo.style.width = (new_width)+ 'px';
  myLightbox.resizeImageContainer(new_width, new_height);
  zoomStatus = "FALSE";
  }
}
imgPreloader.src = imageArray[activeImage][0];

Within the function resizeImageContainer, we need to add an Element.setHeight('mainLink', imgHeight); between the Element.setHeight calls for prevLink and nextLink.

The cleaner (but more limited) HTML display is achieved by replacing lines 434 with Element.setInnerHTML('caption', imageArray[activeImage][1] + "<br /><a href=\"" + imageArray[activeImage][2] + "\">Open Full Page</a>"); which is the custom code for us.

The clickthrough is starting updateNav with the following (right after Element.show('hoverNav')):

Element.show('mainLink');
document.getElementById('mainLink').onclick = function() { window.location.href=imageArray[activeImage][2]; return false; }

Since we don’t have a next button if we’re on the last image or a previous button if we’re on the first, we’ll stretch the mainLink to 80% in those cases. To do that, at the end of updateNav (right before this.enableKeyboardNav();), add:

// When there's only one button (first or last)
if(activeImage == 0 || activeImage == (imageArray.length - 1)) {
  document.getElementById('mainLink').style.width = "80%";
} else {
  document.getElementById('mainLink').style.width = "60%";
}

Finally, we adjust the key shortcuts to end with:

} else if(key == 'f') {
  window.location.href=imageArray[activeImage][2];
} else if(key == 'z') {
  if (zoomStatus == "TRUE") {
    myLightbox.changeImage(activeImage);
  } else {
    myLightbox.changeImage(activeImage, 'TRUE');
  }

before the close of the if / else if in function keyboardAction

And that’s it!

Congradulations on making it all the way through this long and boring post… the modified lightbox files may be found at http://mikeage.net/common/lightbox/ in case you don’t want to copy everything out!


13 responses to “Gallery 2 + Lightbox”

  1. Very interesting and thanks for the directions. Can I ask about the modification to album.tpl? In this article you quote the current text of album.tpl, and then you quote a modification you made.

    Does your modification REPLACE the text from album.tpl that you quote? Or does it go Before, or After, or Somewhere Inside?

    Thanks for advice and help!

    -Sara

  2. Sara,

    Sorry for not being so clear. Basically, what I did is that each time there’s an href in the original (url arg1="view=core.ShowItem" arg2="itemId=`$child.id`"}">), replace it with the new code below (three times).

    
    {if $child.canContainChildren}
      url arg1="view=core.ShowItem" arg2="itemId=`$child.id`"}" >
      {else}
        url arg1="view=core.ShowItem" arg2="itemId=`$child.id`"}"
        imgLink="{g->url arg1="view=core.DownloadItem" arg2="itemId=`$child.resizedId`"}"
        title="{$child.title|markup}"
        pageLink="{g->url arg1="view=core.ShowItem" arg2="itemId=`$child.id`"}"
        rel="lightbox[photos]" >
      {/if}
    
    

    To be a little more precise, you only need to do it twice, for the first two links. There isn’t much point in doing it where we create a text link labeled “No Thumbnail”!

    Feel free to comment again if this still isn’t clear.

  3. Hi Mike,

    I have tried numerous times yet unsuccessfully to implement your code, even when downloading your lightbox files; however, I have been able to implement Sara’s with little difficulty. I suspect the problem is with theme.inc or album.tpl.

    Please could you post (or email me) the full contents of your version of those two files?

    Thanks

    Andrew

  4. Andrew: I was looking at the source for your page.. notice that in your images, you have something like imgLink="/index.php?option=com_gallery2&Itemid=99999999&g2_view=core.DownloadItem&g2_itemId=" for each of your images. Notice that g2_itemId is empty; this would suggest that you’re not getting the resizedId.

    That code is in theme.inc . Note that my theme is based of classic, not siriux, unlike most lightbox tutorials. There was a difference there… something about child.Id vs. theme.Id or something, IIRC. That’s probably where your problem is… unfortunately, I’m not 100% sure what exactly you can do to fix it :-/

  5. Hi there,

    I have a little problem, I don’t know how to solve. When the lightbox opens I can navigate through “next” and “previous” – but only between the images shown on the album page (that are 3×3 images). The Lightbox-Layer says “image 1 of 9”. Is there a way to solve this?

Leave a Reply

Your email address will not be published. Required fields are marked *