Thursday, November 25, 2010

FREE! ASP.net/C# IMDb Scraping API


In extension to my previous IMDb Scraping API in PHP, I have converted the code to ASP.net/C# based IMDb scraper. In my de facto development style, I have kept the code pretty simple and concise. For more details on the movie information it scraps, please refer to my previous post. I don’t have an IIS server running to demonstrate the code but it is functionally similar to my PHP Scraper API so you can test it on the link below. And above all... it’s FREE!!!

Test the Scraper API: http://lab.abhinayrathore.com/imdb/

Download C# Class file: http://lab.abhinayrathore.com/imdb/imdb_asp_csharp.htm
(Add this class file to your project and rename the namespace accordingly)
To convert this C# code to VB.net use this tool:
http://www.developerfusion.com/tools/convert/csharp-to-vb/

Fork it on GitHub: https://github.com/abhinayrathore/ASP.NET-IMDb-Scraper

Download DLL file: http://lab.abhinayrathore.com/imdb/IMDb.dll
(Copy this DLL file to your project folder and add it to the project references)

How to use this class:

  1. Include the class on your ASP page.
  2. Instantiate the class: IMDb imdb = new IMDb("The Godfather", true);
    (Second parameter is an optional Boolean value for scraping extra movie information.)
  3. Access the movie information using public variables: imdb.Id, imdb.Title etc.

It’s been a while since I worked on ASP.net, so if you have any improvements or suggestions, do let me know :)

Saturday, October 30, 2010

FREE! PHP IMDb Scraper/API for new IMDb Template


IMDb is undoubtedly the leading information source for media information and is the top target of web scraping for movie lovers around the world. Unfortunately IMDb does not provide an API to access its database so web scraping is the only resort for us. PHP being one of the most commonly used and powerful web development language enables easy web scraping with the power of PCRE (Perl Compatible Regular Expressions).

For my recent project on a Movie Catalog (http://movies.abhinayrathore.com), I needed a  IMDb scraper and found one built by Tyler Hall. His version was not robust enough to scrap all kind of movie pages so I extended it and made it more robust to support different type of titles, BUT recently IMDb changed its page template and most of the old scrapers stopped working including mine. So, I modified my scraper to accommodate the new template changes and considered it as my moral responsibility to contribute back to the developer community.

This new scraper is very robust and capable enough to handle a wide variety of new template modifications. Apart from the regular information it even goes deep to scan extra media images and release dates.

Click here for a Demo

Last Updated: Feb 1, 2014

Major changes in Feb 20, 2013 version:

  1. Now we use the combined information page to scrape the data. This page doesn't change quite often and we can get complete list of individual departments.
  2. Add a few more entities; producers, musicians, cinematographers, editors etc. Removed metascore information. Removed small poster url.
  3. You can now pass a second boolean parameter to the getMovieInfo() and getMovieInfoById() functions to disable the extra information. By default it is set to true and may slow down the scraping. If you don't need all the extra info like Storyline, Release Dates, Recommendations or Media Images, just pass false as second parameter to these methods. Example $movieArray = $imdb->getMovieInfo("The Godfather", false);.
  4. Information for individuals in the list of directors, cast, writers etc. is now in an associative array with key being the IMDb id of the individual.

UPDATE:
As some of you might have noticed, Google is preventing automated script access to its search result pages. I have created 2 search functions for Google and Bing so you can use whichever one works best for you. I have converted the code to use Bing as of now and will look for other alternatives if we run into some hurdles. Keep me updated if you have any better ideas :)

Here is a list of all the attributes it scraps from the IMDb page:

  1. TITLE_ID
  2. TITLE
  3. YEAR
  4. RATING
  5. GENRES
  6. STARS
  7. DIRECTORS
  8. WRITERS
  9. CAST
  10. PRODUCERS
  11. MUSICIANS
  12. CINEMATOGRAPHERS
  13. EDITORS
  14. ALSO_KNOWN_AS
  15. RELEASE_DATE
  16. RELEASE_DATES
  17. PLOT
  18. POSTER
  19. POSTER_LARGE
  20. RUNTIME
  21. TOP_250
  22. OSCARS
  23. AWARDS
  24. NOMINATIONS
  25. STORYLINE
  26. TAGLINE
  27. MEDIA_IMAGES
  28. MPAA_RATING
  29. VOTES
  30. RECOMMENDED_TITLES
  31. VIDEOS

How to use this PHP Scraper?
Include the class file on your php page
include("imdb.php");
Instantiate the class and get the results in an array:
$imdb = new Imdb();
$movieArray = $imdb->getMovieInfo("The Godfather");

You can try this scraper on my lab page: http://lab.abhinayrathore.com/imdb/

To download the PHP Source Code directly use this link: http://lab.abhinayrathore.com/imdb/imdb_php.htm

Fork it on GitHub: https://github.com/abhinayrathore/PHP-IMDb-Scraper

Example usage: http://lab.abhinayrathore.com/imdb/usage.htm

Proxy script for downloading or displaying Media images on your website: http://lab.abhinayrathore.com/imdb/imdbImage.txt

To implement you own IMDb Web Service API to return data in XML, JSON or JSONP format, use this script along with the API: http://lab.abhinayrathore.com/imdb/imdbWebService.htm

To implement IMDb.com's search suggestions on your website, please follow this post: http://web3o.blogspot.com/2011/10/imdb-search-suggestions-with-jquery.html

If you find any part of this scraper broken or incorrect, please drop a comment here and I’ll try to fix it as soon as possible.

IMDb has a leechers policy in place for media images. You may not be able to use the URL for some of the images to display on your website. As a workaround you can use a PHP Proxy to display or download those images. I’ve written a small proxy script to grab the images: http://lab.abhinayrathore.com/imdb/imdbImage.txt. To use this script you just need to pass the image URL as a request parameter:
<img src="imdbImage.php?url=<?=$url?>" />

NOTE: For users outside of USA
IMDb will automatically redirect you to titles listed in the language used for release in your country (Read more).
To see films listed under their original titles regardless of your country region you will have to modify this script to scrap the titles from http://akas.imdb.com because http://www.imdb.com will automatically redirect you to your country specific title page.

Happy Scraping :)

Friday, May 28, 2010

Google Maps: Dynamically Drawn and Resizable Polygon Overlay

In extension to my previous post on Dynamically Movable and Resizable Circle Overlay, I created yet another nifty tool to dynamically draw a polygon overlay on Google Maps. You won’t find another robust tool like this for Polygon drawing on the internet. Users can draw polygon dynamically by adding, deleting and moving the polygon boundary nodes. User can press shift+click on the map to add a new node to polygon boundary or they can press ctrl+click on any node marker to remove it. The node markers can be dragged around to resize the polygon.

This is how it looks like in action:
Google Maps Dynamic Polygon

The JavaScript code is pretty easy to modify for your custom needs. The initialize() function is used to initialize the Google Map object when the page loads. A click event is also added to the map for adding node markers on the map. The click event takes care of the case when the user click on the previous markers while holding the shift key. When the user clicks on the map, addMarker() function is called to add another boundary node to the polygon. For every newly added marker, drag events are assigned for resizing the polygon and a click event is assigned to delete the node when ctrl key is pressed while clicking the marker pin. The fitPolygon() function is used to set map bounds and center to adjust the polygon inside it.

Put the following JavaScript code in the head section of your page:

<script src="http://maps.google.com/maps?file=api&amp;v=2&amp;key=YOUR_API_KEY&sensor=true" type="text/javascript"></script>
<script type="text/javascript">
/* Developed by: Abhinay Rathore [web3o.blogspot.com] */ 
//Global variables
var global = this;
var map;

var PolygonMarkers = []; //Array for Map Markers
var PolygonPoints = []; //Array for Polygon Node Markers
var bounds = new GLatLngBounds; //Polygon Bounds
var Polygon; //Polygon overlay object
var polygon_resizing = false; //To track Polygon Resizing

//Polygon Marker/Node icons
var redpin = new GIcon(); //Red Pushpin Icon
redpin.image = "http://maps.google.com/mapfiles/ms/icons/red-pushpin.png";
redpin.iconSize = new GSize(32, 32);
redpin.iconAnchor = new GPoint(10, 32);
var bluepin = new GIcon(); //Blue Pushpin Icon
bluepin.image = "http://maps.google.com/mapfiles/ms/icons/blue-pushpin.png";
bluepin.iconSize = new GSize(32, 32);
bluepin.iconAnchor = new GPoint(10, 32);

function initialize() { //Initialize Google Map
    if (GBrowserIsCompatible()) {
        map = new GMap2(document.getElementById("map_canvas")); //New GMap object
        map.setCenter(new GLatLng(38.90, -94.68007), 13);

        var ui = new GMapUIOptions(); //Map UI options
        ui.maptypes = { normal:true, satellite:true, hybrid:true, physical:false }
        ui.zoom = {scrollwheel:true, doubleclick:true};
        ui.controls = { largemapcontrol3d:true, maptypecontrol:true, scalecontrol:true };
        map.setUI(ui); //Set Map UI options

        //Add Shift+Click event to add Polygon markers
        GEvent.addListener(map, "click", function(overlay, point, overlaypoint) {
            var p = (overlaypoint) ? overlaypoint : point;
            //Add polygon marker if overlay is not an existing marker and shift key is pressed
            if (global.shiftKey && !checkPolygonMarkers(overlay)) { addMarker(p); }
        });
    }
}

// Adds a new Polygon boundary marker
function addMarker(point) {
    var markerOptions = { icon: bluepin, draggable: true };
    var marker = new GMarker(point, markerOptions);
    PolygonMarkers.push(marker); //Add marker to PolygonMarkers array
    map.addOverlay(marker); //Add marker on the map
    GEvent.addListener(marker,'dragstart',function(){ //Add drag start event
        marker.setImage(redpin.image);
        polygon_resizing = true;
    });
    GEvent.addListener(marker,'drag',function(){ drawPolygon(); }); //Add drag event
    GEvent.addListener(marker,'dragend',function(){   //Add drag end event
        marker.setImage(bluepin.image);
        polygon_resizing = false;
        drawPolygon();
        fitPolygon();
    });
    GEvent.addListener(marker,'click',function(point) { //Add Ctrl+Click event to remove marker
        if (global.ctrlKey) { removeMarker(point); }
    });
    drawPolygon();

    //If more then 2 nodes then automatically fit the polygon
    if(PolygonMarkers.length > 2) fitPolygon();
}

// Removes a Polygon boundary marker
function removeMarker(point) {
    if(PolygonMarkers.length == 1){ //Only one marker in the array
        map.removeOverlay(PolygonMarkers[0]);
        map.removeOverlay(PolygonMarkers[0]);
        PolygonMarkers = [];
        if(Polygon){map.removeOverlay(Polygon)};
    }
      else //More then one marker
      {
        var RemoveIndex = -1;
        var Remove;
        //Search for clicked Marker in PolygonMarkers Array
        for(var m=0; m<PolygonMarkers.length; m++)
        {
            if(PolygonMarkers[m].getPoint().equals(point))
            {
                RemoveIndex = m; Remove = PolygonMarkers[m]
                break;
            }
        }
        //Shift Array elemeents to left
        for(var n=RemoveIndex; n<PolygonMarkers.length-1; n++)
        {
            PolygonMarkers[n] = PolygonMarkers[n+1];
        }
        PolygonMarkers.length = PolygonMarkers.length-1 //Decrease Array length by 1
        map.removeOverlay(Remove); //Remove Marker
        drawPolygon(); //Redraw Polygon
      }
}

//Draw Polygon from the PolygonMarkers Array
function drawPolygon()
{
    PolygonPoints.length=0;
    for(var m=0; m<PolygonMarkers.length; m++)
    {
        PolygonPoints.push(PolygonMarkers[m].getPoint()); //Add Markers to PolygonPoints node array
    }
    //Add first marker in the end to close the Polygon
    PolygonPoints.push(PolygonMarkers[0].getPoint());
    if(Polygon){ map.removeOverlay(Polygon); } //Remove existing Polygon from Map
    var fillColor = (polygon_resizing) ? 'red' : 'blue'; //Set Polygon Fill Color
    Polygon = new GPolygon(PolygonPoints, '#FF0000', 2, 1, fillColor, 0.2); //New GPolygon object
    map.addOverlay(Polygon); //Add Polygon to the Map

    //TO DO: Function Call triggered after Polygon is drawn
}

//Fits the Map to Polygon bounds
function fitPolygon(){
    bounds = Polygon.getBounds();
    map.setCenter(bounds.getCenter(), map.getBoundsZoomLevel(bounds));
}
//check is the marker is a polygon boundary marker
function checkPolygonMarkers(marker) {
    var flag = false;
    for (var m = 0; m < PolygonMarkers.length; m++) {
        if (marker == PolygonMarkers[m])
        { flag = true; break; }
    }
    return flag;
}

//////////////////[ Key down event handler ]/////////////////////
//Event handler class to attach events
var EventUtil = {
      addHandler: function(element, type, handler){
            if (element.addEventListener){
                    element.addEventListener(type, handler, false);
            } else if (element.attachEvent){
                    element.attachEvent("on" + type, handler);
            } else {
                    element["on" + type] = handler;
            }
      }
};

// Attach Key down/up events to document
EventUtil.addHandler(document, "keydown", function(event){keyDownHandler(event)});
EventUtil.addHandler(document, "keyup", function(event){keyUpHandler(event)});

//Checks for shift and Ctrl key press
function keyDownHandler(e)
{
      if (!e) var e = window.event;
      var target = (!e.target) ? e.srcElement : e.target;
      if (e.keyCode == 16 && !global.shiftKey) { //Shift Key
            global.shiftKey = true;
      }
      if (e.keyCode == 17 && !global.ctrlKey) { //Ctrl Key
            global.ctrlKey = true;
      }
}
//Checks for shift and Ctrl key release
function keyUpHandler(e){
      if (!e) var e = window.event;
      if (e.keyCode == 16 && global.shiftKey) { //Shift Key
            global.shiftKey = false;
      }
      if (e.keyCode == 17 && global.ctrlKey) { //Ctrl Key
            global.ctrlKey = false;
      }
}
</script>

To initialize the map you can call the functions on page load event and include a div tag inside your body to hold the Map.

<body onload="initialize()" onunload="GUnload()">
<div id="map_canvas" style="width:100%; height:450px"></div>
</body>

Feel free to modify and use this code on your website. I have used API v2 for my code but you can easily modify this code for API v3. Happy Mapping!

Thursday, May 27, 2010

Google Maps: Dynamically Movable and Resizable Circle Overlay

Google provides a pretty wholesome Map API for creating customized maps with enhanced features. In a recent project of mine I had to use dynamically drawn map overlays for searching house properties within them. To implement that I looked for some algorithms to draw circles on Google Map Overlays and found a good one at http://koti.mbnet.fi/ojalesa/googlepages/circle.htm, but this program was built to change the radius using html form field. To take it a few steps further, I planned to make this circle movable and resizable by directly dragging map icons.

      This is one of the best dynamic circle overlay drawing tool you can find on internet. There are two pin markers to alter the circle, the Blue pin is used to drag the circle around on the map and the Red one can be dragged to resize the circle. You can even set minimum and maximum radius for the circle in the JavaScript code. The resize marker pin always stays at the 0 degree edge of the circle. The Circle fill color changes to red if the circle is moved or resized.

This is how it looks like in action: 

Google Map Dynamic Circle

JavaScript code: The logistic code is pretty easy to modify for your custom needs. The initialize() function is used to initialize the Google Map object when the page loads. Circle Center and Resize markers are then added at the default center location. Marker drag events are assigned to Center and Resize markers for dragging and resizing the circle. The drawCircle() function implements the algorithm for drawing the circle and finally the fitCircle() function is used to set the Map bounds to include the full circle inside it. If you want to trigger any other function after the circle is drawn, you can call your function after the fitCircle() function call at the end of drawCircle() function. Google Map uses Metric Units for distance by default, you will have to use conversion factor (1km = 0.621371192mi) to convert radius value into miles if you wish to.

Place the following JavaScript code in the head section of your page:
<script src="http://maps.google.com/maps?file=api&amp;v=2&amp;key=YOUR_API_KEY&sensor=true" type="text/javascript"></script>
<script type="text/javascript">
/* Developed by: Abhinay Rathore [web3o.blogspot.com] */
//Global variables
var map;
var bounds = new GLatLngBounds; //Circle Bounds
var map_center = new GLatLng(38.903843, -94.680096);

var Circle; //Circle object
var CirclePoints = []; //Circle drawing points
var CircleCenterMarker, CircleResizeMarker;
var circle_moving = false; //To track Circle moving
var circle_resizing = false; //To track Circle resizing
var radius = 1; //1 km
var min_radius = 0.5; //0.5km
var max_radius = 5; //5km

//Circle Marker/Node icons
var redpin = new GIcon(); //Red Pushpin Icon
redpin.image = "http://maps.google.com/mapfiles/ms/icons/red-pushpin.png";
redpin.iconSize = new GSize(32, 32);
redpin.iconAnchor = new GPoint(10, 32);
var bluepin = new GIcon(); //Blue Pushpin Icon
bluepin.image = "http://maps.google.com/mapfiles/ms/icons/blue-pushpin.png";
bluepin.iconSize = new GSize(32, 32);
bluepin.iconAnchor = new GPoint(10, 32);

function initialize() { //Initialize Google Map
    if (GBrowserIsCompatible()) {
        map = new GMap2(document.getElementById("map_canvas")); //New GMap object
        map.setCenter(map_center);

        var ui = new GMapUIOptions(); //Map UI options
        ui.maptypes = { normal:true, satellite:true, hybrid:true, physical:false }
        ui.zoom = {scrollwheel:true, doubleclick:true};
        ui.controls = { largemapcontrol3d:true, maptypecontrol:true, scalecontrol:true };
        map.setUI(ui); //Set Map UI options

        addCircleCenterMarker(map_center);
        addCircleResizeMarker(map_center);
        drawCircle(map_center, radius);
    }
}

// Adds Circle Center marker
function addCircleCenterMarker(point) {
    var markerOptions = { icon: bluepin, draggable: true };
    CircleCenterMarker = new GMarker(point, markerOptions);
    map.addOverlay(CircleCenterMarker); //Add marker on the map
    GEvent.addListener(CircleCenterMarker, 'dragstart', function() { //Add drag start event
        circle_moving = true;
    });
    GEvent.addListener(CircleCenterMarker, 'drag', function(point) { //Add drag event
        drawCircle(point, radius);
    });
    GEvent.addListener(CircleCenterMarker, 'dragend', function(point) { //Add drag end event
        circle_moving = false;
        drawCircle(point, radius);
    });
}

// Adds Circle Resize marker
function addCircleResizeMarker(point) {
    var resize_icon = new GIcon(redpin);
    resize_icon.maxHeight = 0;
    var markerOptions = { icon: resize_icon, draggable: true };
    CircleResizeMarker = new GMarker(point, markerOptions);
    map.addOverlay(CircleResizeMarker); //Add marker on the map
    GEvent.addListener(CircleResizeMarker, 'dragstart', function() { //Add drag start event
        circle_resizing = true;
    });
    GEvent.addListener(CircleResizeMarker, 'drag', function(point) { //Add drag event
        var new_point = new GLatLng(map_center.lat(), point.lng()); //to keep resize marker on horizontal line
        var new_radius = new_point.distanceFrom(map_center) / 1000; //calculate new radius
        if (new_radius < min_radius) new_radius = min_radius;
        if (new_radius > max_radius) new_radius = max_radius;
        drawCircle(map_center, new_radius);
    });
    GEvent.addListener(CircleResizeMarker, 'dragend', function(point) { //Add drag end event
        circle_resizing = false;
        var new_point = new GLatLng(map_center.lat(), point.lng()); //to keep resize marker on horizontal line
        var new_radius = new_point.distanceFrom(map_center) / 1000; //calculate new radius
        if (new_radius < min_radius) new_radius = min_radius;
        if (new_radius > max_radius) new_radius = max_radius;
        drawCircle(map_center, new_radius);
    });
}

//Draw Circle with given radius and center
function drawCircle(center, new_radius) {
    //Circle Drawing Algorithm from: http://koti.mbnet.fi/ojalesa/googlepages/circle.htm

    //Number of nodes to form the circle
    var nodes = new_radius * 40;
    if(new_radius < 1) nodes = 40;

    //calculating km/degree
    var latConv = center.distanceFrom(new GLatLng(center.lat() + 0.1, center.lng())) / 100;
    var lngConv = center.distanceFrom(new GLatLng(center.lat(), center.lng() + 0.1)) / 100;

    CirclePoints = [];
    var step = parseInt(360 / nodes) || 10;
    var counter = 0;
    for (var i = 0; i <= 360; i += step) {
        var cLat = center.lat() + (new_radius / latConv * Math.cos(i * Math.PI / 180));
        var cLng = center.lng() + (new_radius / lngConv * Math.sin(i * Math.PI / 180));
        var point = new GLatLng(cLat, cLng);
        CirclePoints.push(point);
        counter++;
    }
    CircleResizeMarker.setLatLng(CirclePoints[Math.floor(counter / 4)]); //place circle resize marker
    CirclePoints.push(CirclePoints[0]); //close the circle polygon
    if (Circle) { map.removeOverlay(Circle); } //Remove existing Circle from Map
    var fillColor = (circle_resizing || circle_moving) ? 'red' : 'blue'; //Set Circle Fill Color
    Circle = new GPolygon(CirclePoints, '#FF0000', 2, 1, fillColor, 0.2); //New GPolygon object for Circle
    map.addOverlay(Circle); //Add Circle Overlay on the Map
    radius = new_radius; //Set global radius
    map_center = center; //Set global map_center
    if (!circle_resizing && !circle_moving) { //Fit the circle if it is nor moving or resizing
        fitCircle();
        //Circle drawing complete trigger function goes here

    }
}

//Fits the Map to Circle bounds
function fitCircle() {
    bounds = Circle.getBounds();
    map.setCenter(bounds.getCenter(), map.getBoundsZoomLevel(bounds));
}
</script>

To initialize the map you can call the functions on page load event and include a div tag inside your body to hold the Map.

<body onload="initialize()" onunload="GUnload()">
<div id="map_canvas" style="width:100%; height:450px"></div>
</body>

Feel free to modify and use this code on your website. I have used API v2 for my code but you can easily modify this code for API v3. Happy Mapping!

Thursday, April 1, 2010

jQuery Image Gallery Plugin with Slideshow functionality

In my ongoing cavalcade on JavaScript/jQuery Web Image tools I decided to design a very useful and commonly used tool; a very easy to use Image Gallery/Slideshow Plugin. There are a plethora of JavaScript/jQuery Image Gallery tools you can find on internet, but for most of them you will have to pay for the source code. Though my plugin may not be that customizable but hey, it does gets the job done with minimal effort. It also features a Slideshow tool and controls to navigate through all the images in the gallery. You can tweak the CSS file to match the color and styling of your website. If you want to use it in it’s own style, it’s just a plug and play feature to add on your website. It isn’t that bad? Is it, plus FREE!!!

Why should you use this Image Gallery Plugin:

  1. Zero coding required, just plug-n-play.
  2. Works on any screen size.
  3. Works with images of any size, resolution or aspect ratio.
  4. No images used in the layout, hence faster to load.
  5. Compatible with all the modern browsers.
  6. Users can navigate between images using the Image Gallery List or the Navigation buttons.
  7. Users can set the Slideshow speed during plugin initialization.

This is how the tool looks like in action:

jQuery Image Gallery Plugin

To begin with, first you need to add links for script reference to the latest jQuery code, ImageGallery.js file (provided below) and ImageGallery.css stylesheet (provided below) in the head section of your page. To use this plugin you just need to create a div container in your HTML page and put all the images in there. You can apply styling to your container in any way you want to. To assign Image Gallery event to all the images, you just need to use the jQuery plugin on the image container as shown in example below in the page load event.

      Here in this sample HTML code I have used a pic_container class for my image div container (CSS in the head section). Then to assign the Image Gallery plugin in the $(document).ready() function I have used jQuery class selector to assign plugin to my container as $(".pic_container").ImageGallery(3000);, where the values inside ImageGallery() function is the time delay in milliseconds for the Slideshow (1000ms = 1sec).

      Including this plugin on your existing page you’ll only require the 2 links to jQuery (.js) files and the link to Image Gallery stylesheet (ImageGallery.css) file. To assign the plugin to your div tags, you will need to use the page load function ($(document).ready()) as shown below:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>jQuery Image Gallery by Abhinay Rathore</title>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script src="ImageGallery.js"></script>
<link href="ImageGallery.css" rel="stylesheet" type="text/css" />
<script type="text/javascript">
/* Developed by: Abhinay Rathore [web3o.blogspot.com] */
//////////////////[ Page Load Function ]///////////////////////
$(document).ready(function(){
      //Add ImageGallery function to all the 'pic_container' containers
      //SlideShow Time Interval can be passed as function argument (1000 = 1sec)
      $(".pic_container").ImageGallery(3000);
});
/////////////////[ End Page Load Function ]///////////////////
</script>

<style type="text/css">
.pic_container {
      border: 1px solid #9CF;
}
.pic_container img{
      border: 1px solid #9CF;
      width: auto; height: 100px;
      padding: 5px; margin: 5px;
      cursor: pointer;
}
</style>

</head>

<body>
<div class="pic_container">
<img src="BlueHills.jpg" />
<img src="Sunset.jpg" />
<img src="WaterLilies.jpg" />
<img src="Winter.jpg" />
<img src="liberty.jpg" />
<img src="goldengate1.jpg" />
<img src="city1.jpg" />
<img src="scene21.jpg" />
<img src="taj_mahal1.jpg" />
<img src="taj1.jpg" />
</div>
</body>
</html>

To include the Image Gallery plugin code you will need to create a separate ImageGallery.js file and put the following contents in there. The jQuery/JavaScript code in this file is pretty easy to understand and tweak around. The main event starts from the startGallery() function where all the controls and containers are added to the HTML page and Gallery List is populated at the bottom. displayPicture() function is used to display the Main Picture and needs to be called for any subsequent picture display calls. Slideshow is controlled by the three function defined under the Slideshow Functions section. Gallery List scrolling is handled by the separate set of functions defined under Gallery List Scroll Functions section. I have used minimal amount of well commented code to get the functionality working, so if you need extra features, you can always modify the code according to your needs.

Contents of ImageGallery.js file:
////////////////////////////////////////////////////////
// Developed by: Abhinay Rathore [web3o.blogspot.com] //
////////////////////////////////////////////////////////

//////////////////[ jQuery Plugin ]///////////////////////
jQuery.fn.ImageGallery = function(Time_Interval){
      //Assign Click event to all the images in the container...
      this.children("img").click(function(){ startGallery(this); });
      //Set SlideShow Time Interval
      if(!Time_Interval) SlideShow_Time_Interval = 3000;
      else SlideShow_Time_Interval = Time_Interval;
}
//////////////////[ END jQuery Plugin ]///////////////////////

//////////////////[ Global Variables ]///////////////////////
var Gallery = new Array();
var MaxPicWidth, MaxPicHeight, WindowWidth, WindowHeight, CurrentIndex, MaxIndex;
var GalleryListContainerWidth, GalleryListWidth, WidthDiff;
var SlideShow_Time_Interval;
var PageTitle;
/////////////////[ End Global Variables ]////////////////////

//////////////////[ Picture Gallery Functions ]///////////////////////
//This function initiates the Picture Gallery by loading all the containers and controls
function startGallery(id){
      //Scroll Page to top
      $('html,body').animate({scrollTop: 0}, 'fast');
     
      //Declare Global variables...
      WindowWidth = $(window).width();
      WindowHeight = $(window).height();
      MaxPicWidth = WindowWidth - 100;
      MaxPicHeight = WindowHeight - 170;
     
      //Create translucent background...
      var bg = document.createElement('div');
      bg.id = "Gallery_Background";
      document.body.appendChild(bg); //Attach background to the BODY
      $(bg).fadeTo('fast', 0.9);
     
      //Create Gallery Container with dimensions of the window...
      var container = document.createElement('div');
      container.id = "GalleryContainer";
      $(container).css({'width': WindowWidth, 'height': WindowHeight});
     
      //Create and attach the Gallery Close Button to the Container...
      var CloseButton = document.createElement('div');
      CloseButton.id = "GalleryCloseButton";
      CloseButton.innerHTML = "X";
      container.appendChild(CloseButton);
      $(CloseButton).click(function(){ stopGallery() }); //Attach Close event
     
      //Create and attach the container for Gallery Picture List
      var GalleryListContainer = document.createElement('div');
      GalleryListContainer.id = "GalleryListContainer";
      $(GalleryListContainer).css({'top': WindowHeight - 110, 'left': '50px',
                    'width': WindowWidth - 100 - 4, 'height': '100px'});
      container.appendChild(GalleryListContainer);
     
      //Create and attach the Gallery List to the Gallery List Container...
      var GalleryList = document.createElement('div');
      GalleryList.id = "GalleryList";
      GalleryListContainer.appendChild(GalleryList);
     
      //Create and attach the Gallery List Left Scroller to the Container...
      var GalleryListLeft = document.createElement('div');
      GalleryListLeft.id = "GalleryListLeft";
      $(GalleryListLeft).css({'top': WindowHeight - 110, 'left': '0px'});
      GalleryListLeft.appendChild(createSpanNode("&loz;"));
      container.appendChild(GalleryListLeft);
     
      //Create and attach the Gallery List Right Scroller to the Container...
      var GalleryListRight = document.createElement('div');
      GalleryListRight.id = "GalleryListRight";
      $(GalleryListRight).css({'top': WindowHeight - 110, 'right': '0px'});
      GalleryListRight.appendChild(createSpanNode("&loz;"));
      container.appendChild(GalleryListRight);
     
      //Create and attach the Gallery Controls Container to the Container...
      var ControlsContainer = document.createElement('div');
      ControlsContainer.id = "ControlsContainer";
      $(ControlsContainer).css({'top': WindowHeight - 145});
      container.appendChild(ControlsContainer);
     
      //Create and attach the Gallery Controls to the Controls Container...
      //First Image Control
      var First = document.createElement('div');
      First.id = "First";
      First.className = "button";
      First.innerHTML = "First";
      ControlsContainer.appendChild(First);
      //Previous Image Control
      var Prev = document.createElement('div');
      Prev.id = "Prev";
      Prev.className = "button";
      Prev.innerHTML = "&laquo;Prev";
      ControlsContainer.appendChild(Prev);
      //Play Pause Slideshow Control
      var Play = document.createElement('div');
      Play.id = "Play";
      Play.className = "button";
      Play.innerHTML = "Slideshow";
      $(Play).click(function(){ StartSlideShow(); });
      ControlsContainer.appendChild(Play);
      //Next Image Control
      var Next = document.createElement('div');
      Next.id = "Next";
      Next.className = "button";
      Next.innerHTML = "Next&raquo;";
      ControlsContainer.appendChild(Next);
      //Last Image Control
      var Last = document.createElement('div');
      Last.id = "Last";
      Last.className = "button";
      Last.innerHTML = "Last";
      ControlsContainer.appendChild(Last);
     
      //Create and attach the Main Pictire holder to the Container...
      var MainPicture = document.createElement('div');
      MainPicture.id = "GalleryMainPicture";
      container.appendChild(MainPicture);
     
      //Attach the Container to the BODY
      document.body.appendChild(container);
      //Disable Page Scroll
      $('html,body').css({'overflow': 'hidden'});
      //Add the Pictures to the Gallery List
      //This function returns the index of the clicked image
      var index = LoadPictures(id, GalleryList);
     
      //Display the clicked picture in the MainPicture box...
      displayPicture(index, true);
     
      PageTitle = document.title;
      document.title = "Image Gallery by Abhinay Rathore [web3o.blogspot.com]";
}
//Stops the Image Gallery when Close Button is clicked
function stopGallery(){
      Gallery = new Array(); //Clear the Gallery Array
      StopSlideShow(); //Stop Slideshow is it is running
      $("#GalleryContainer").children().remove(); //Remove all child nodes from Container
      $("#GalleryContainer").remove(); //Remove the Container
      $("#Gallery_Background").remove(); //Remove Gallery Background
      $('html,body').css({'overflow': 'auto'}); //Enable Page Scroll
      document.title = PageTitle;
}
//////////////////[ END Picture Gallery Functions ]///////////////////////

/////////////////[ Gallery List Picture collector Function ]///////////////////
//This function adds all the sibling images to the Gallery List and returns the
//index to the clicked image
function LoadPictures(id, GalleryList){
      var pics = $(id).parent().children(); //Get all the siblings of the clicked image
      var index = 0, i;
      for(i=0; i<pics.length; i++) //Iterate through all the siblings and add to the Gallery Array
      {
            var img = document.createElement('img'); //Create new image
            img.src = pics[i].src;
            img.name = i //Store Array index in the 'name' attribute of thumbnails
            Gallery[i] = pics[i].src; //Add image link to the Gallery Array
            if(pics[i] == id){ index = i; }  //Clicked Image index
            //Attach click event to the images...
            $(img).click(function(){ displayPicture(parseInt(this.name), true);});
            GalleryList.appendChild(img); //Add newly created image thumbnail to the Gallery List
      }
      MaxIndex = i - 1; //Max Index in the array
     
      //Assign click events to First and Last image control buttons
      $("#First").click(function(){ displayPicture(0, true); });
      $("#Last").click(function(){ displayPicture(MaxIndex, true); });
     
      //Calculate Gallery List width to compare with the Gallery List Container
      //If Gallery List Width is less then the Container Width then the
      //List Scroll controls are not displayed
      var GalleryListContainer = $("#GalleryListContainer");
      GalleryListContainerWidth = GalleryListContainer.width();
      var GalleryListContainerOffset = GalleryListContainer.offset();
      var lastChild = $("#GalleryList > img:nth-child(" + (i) + ")");
      var lastChildPosition  = lastChild.position();
      var lastChildWidth  = lastChild.width();
      var GalleryList = $("#GalleryList");
      var GalleryListOffset  = GalleryList.offset();
      GalleryListWidth = Math.round(GalleryListOffset.left + lastChildPosition.left + lastChildWidth - GalleryListContainerOffset.left + 5);
     
      if(GalleryListWidth > GalleryListContainerWidth){ //Attach mouse events to List Scroll Controls
            WidthDiff = GalleryListWidth - GalleryListContainerWidth;
            $("#GalleryListLeft").mouseover(function(){scrollGalleryList('L');});
            $("#GalleryListRight").mouseover(function(){scrollGalleryList('R');});
            $("#GalleryListLeft, #GalleryListRight").mouseout(function(){stopScrollGalleryList();});
      } else { //Hide List Scroll Controls
            $("#GalleryListLeft, #GalleryListRight").css({'visibility': 'hidden'});
      }
      return index;
}
/////////////////[ END Gallery List Picture collector Function ]///////////////////

//////////////////[ Display Picture Functions ]///////////////////////
//This function displays the Image at the given index in Gallery Array
function displayPicture(index, enableControls){
      CurrentIndex = index; //Set Current Index for Slideshow
     
      var MainPicture = $("#GalleryMainPicture");
      MainPicture.children().remove(); //Remove previous image
      var img = document.createElement('img'); //Create new image
      img.src = Gallery[index]; //Set new image source
      MainPicture.append(img); //Add newly created image to the MainPicture box
      var imgDim = PicDimensions(img) //Get image dimensions to fit in the MainPicture box
      $(img).css({'height': imgDim.height, 'width': imgDim.width}); //Set new image dimensions
      //Set new size and margins for the MainPicture box
      MainPicture.css({'height': imgDim.height, 'width':imgDim.width,
                              'left': LeftMargin(imgDim.width), 'top': TopMargin(imgDim.height)});
     
      //If not a Slideshow display: enable the Next/Prev image controls
      if(enableControls){ EnablePictureControls(index); }
     
      //Highlight selected picture under the Gallery List images
      highlightSelectedPic(index);
}
//This functions highlights the selected picture under the Gallery List images
function highlightSelectedPic(index){
      $("#GalleryList").children().fadeTo(0, 0.5); //Fade all the images in Gallery List
      var selectedChild = $("#GalleryList > img:nth-child(" + (index + 1) + ")");
      selectedChild.fadeTo(0, 1.0); //Highlight the selected image (at given index)
     
      //Calculate the image thumbnail position inside Gallery List to bring it in focus
      var GalleryList  = $("#GalleryList");
      var GalleryListContainer = $("#GalleryListContainer");
      var GalleryListContainerOffset = GalleryListContainer.offset();
      var GalleryListOffset  = GalleryList.offset();
      var selectedChildPosition  = selectedChild.position();
      var selectedChildWidth  = selectedChild.width();
      var leftEdge = Math.round(GalleryListOffset.left + selectedChildPosition.left);
      var rigthEdge = Math.round(GalleryListOffset.left + selectedChildPosition.left + selectedChildWidth);
     
      //Bring the selected picture into foucs in Gallery List
      if(rigthEdge > GalleryListContainerWidth){ //scroll left
            GalleryList.animate({left: '-=' + (rigthEdge - GalleryListContainerWidth - GalleryListContainerOffset.left + 5)}, 200);
      } else if(leftEdge < GalleryListContainerOffset.left){ //scroll right
            GalleryList.animate({left: '+=' + (GalleryListContainerOffset.left - leftEdge + 5)}, 200);
      }
}
//This function is used to enable the Next/Prev Image Controls
//based upon the index in Gallery Array
function EnablePictureControls(index){
      var Prev = $("#Prev"), Next = $("#Next");
      $("#Prev, #Next").css({'visibility': 'visible'});
      $("#First, #Last").css({'visibility': 'visible'});
      Prev.unbind('click'); //Unbind existing click events
      Next.unbind('click'); //Unbind existing click events
      if(Gallery.length == 1){ //If only one image, hide Next/Prev image controls
            $("#Prev, #Next").css({'visibility': 'hidden'});
            $("#First, #Last").css({'visibility': 'hidden'});
      } else if(index == 0){ //If first image displayed, hide Prev image control
            Prev.css({'visibility': 'hidden'});
            $("#First").css({'visibility': 'hidden'});
            Next.click(function(){displayPicture(index+1, true);});
      } else if(index == Gallery.length - 1){ //If last image displayed, hide Next image control
            Prev.click(function(){displayPicture(index-1, true);});
            Next.css({'visibility': 'hidden'});
            $("#Last").css({'visibility': 'hidden'});
      } else { //Show Next/Prev image controls
            Prev.click(function(){displayPicture(index-1, true);});
            Next.click(function(){displayPicture(index+1, true);});
      }
}
//////////////////[ END Display Picture Functions ]///////////////////////

//////////////////[ SlideShow Functions ]///////////////////////
var SlideShowIndex = 0;
function StartSlideShow(){ //Start SlideShow...
      //Hide Next/Prev and First/Last Image Controls
      $("#Prev, #Next").css({'visibility': 'hidden'});
      $("#First, #Last").css({'visibility': 'hidden'});
      //Change Play Button to Pause
      $("#Play").unbind('click');
      $("#Play").text('Stop Slideshow');
      $("#Play").click(function(){ StopSlideShow(); });
     
      var MainPicture = document.getElementById("GalleryMainPicture");
      //Set SlideShow index
      if(CurrentIndex == MaxIndex){ SlideShowIndex = 0; }
      else { SlideShowIndex = CurrentIndex + 1; }
      //Start SlideShow timer with time_interval specified above
      MainPicture.timer = setInterval(function() { PlaySlideShow(); }, SlideShow_Time_Interval);
}
function PlaySlideShow(){ //Switch SlideShow Image
      displayPicture(SlideShowIndex, false);
      SlideShowIndex += 1; //Step to next image
      if(SlideShowIndex > MaxIndex) SlideShowIndex = 0;
}
function StopSlideShow(){ //Stop SlideShow...
      var MainPicture = document.getElementById("GalleryMainPicture");
      clearTimeout(MainPicture.timer); //Clear SlideShow timer
     
      //Show Next/Prev and First/Last Image Controls
      $("#Prev, #Next").css({'visibility': 'visible'});
      $("#First, #Last").css({'visibility': 'visible'});
      //Enable Next/Prev Controls based upon the Current Index
      EnablePictureControls(CurrentIndex);
     
      //Change Pause Button to Play
      $("#Play").unbind('click');
      $("#Play").text('Slideshow');
      $("#Play").click(function(){ StartSlideShow(); });
}
//////////////////[ END SlideShow Functions ]///////////////////////

//////////////////[ Gallery List Scroll Functions ]///////////////////////
//Gallery List scrolling begins when the user takes the mouse over the
//Gallery List scroll controls and stops when the mouse moves out of the controls
function scrollGalleryList(dir){ //Start Scrolling the Gallery List
      var GalleryList = document.getElementById("GalleryList");
      //Start Gallery List Scroll timer
      GalleryList.timer = setInterval(function() { animateGalleryList(dir); }, 10);
}
function animateGalleryList(dir){
      var GalleryList = document.getElementById("GalleryList");
      var left = $(GalleryList).css("left"); //Get current left position
      if(left == "auto") left = parseInt(0); else left = parseInt(left.replace("px", ""));
      switch(dir) //shift the Gallery List to left/right based upon the direction
    {
            case "R": if(Math.abs(left) < WidthDiff) { $(GalleryList).css({'left': left-10}); }
                              else { stopScrollGalleryList(); }
                              break;
            case "L": if(left < 0) { $(GalleryList).css({'left': left+10}); }
                              else { stopScrollGalleryList(); }
                              break;
      }
}
function stopScrollGalleryList(){ //Stop Scrolling the Gallery List
      var GalleryList = document.getElementById("GalleryList");
      clearTimeout(GalleryList.timer); //Clear Gallery List Scroll timer
}
//////////////////[ END Gallery List Scroll Functions ]///////////////////////

//This function returns the display picture dimensions based upon the
//maximum picture hight and width according to the page size
function PicDimensions(pic){
      var d = new Dimensions();
      d.height = Math.min(MaxPicHeight, $(pic).height());
      d.width = Math.round($(pic).width() * d.height / $(pic).height());
      if(d.width > MaxPicWidth){
            d.width = MaxPicWidth;
            d.height = Math.round($(pic).height() * d.width / $(pic).width());
      }
      return d;
}
function Dimensions(){ //Dimensions class to hold width and height values
      var width; var height;
}

//This funcition creates a 'span' node with the provided text
function createSpanNode(text){
      var span = document.createElement('span');
      span.innerHTML = text;
      return span;
}

//These functions calculate the Left and Top margins for any div tag
//to center align them to the page based upon their width and height
//These functions are mainly used to center align the MainPicture box
function LeftMargin(width){
      return Math.round((WindowWidth - width)/2);
}
function TopMargin(height){
      return Math.round((WindowHeight - 150 - height)/2);
}

The CSS code for the Image Gallery plugin is pretty easy to modify. It does not use any images so it is faster to load as well but you can always add images to make it look prettier. To match the color and styling of your website you can tweak around this code on your own. The ID’s in CSS match with the ID’s in the jQuery code so it will be pretty easy to figure out the layout. Most of the dynamic styling is done in the startGallery() function where you can easily tweak them. Almost all of the elements are absolutely aligned and the positioning is decided by the dynamic jQuery code based upon the window dimensions. Create this ImageGallery.css file and link it in the head section of your page.

Contents of ImageGallery.css file:
#Gallery_Background{
      background-color: #DFDFDF;
      width: 100%; height: 100%;
      position: absolute;
      top: 0px; left: 0px;
}
#GalleryContainer{
      position: absolute;
      top: 0px; left: 0px;
}
#GalleryCloseButton{
      background-color: #fff;
      position: absolute;
      top: 5px; right: 5px;
      border: 2px solid #069; color: #069;
      font-family: "Arial Black", Gadget, sans-serif;
      font-size: large; line-height: 20px;
      width: 20px; height: 20px;
      text-align: center;
      cursor: pointer;
}
#GalleryMainPicture{
      background-color: #FFF;
      position: absolute;
      border: 3px solid #069;
      overflow: hidden;
}
#ControlsContainer{
      position: relative; text-align: center;
      margin: 0 auto;
}
#ControlsContainer .button{
      position: relative; display: inline-block;
      text-align: center; cursor: pointer;
      height: 25px; width: auto;
      background-color: #FFF;
      font-family: Verdana, Geneva, sans-serif;
      font-weight: bold; font-size: medium;
      line-height: 25px; color: #069;
      margin: 0px 5px 0px 5px; padding: 0px 5px 0px 5px;
      border: 2px solid #069;
      overflow: hidden;
}
#GalleryListContainer{
      background-color: #FFF;
      position: absolute; overflow: hidden;
      border: 2px solid #069;
}
#GalleryList{
      position: relative;
      background-color: #FFF;
      text-align: center;
      height: 100px; width: auto;
      white-space: nowrap;
      padding: 5px; margin: 0 auto;
      display: block;
}
#GalleryList img{
      margin: 0px 5px 0px 0px;
      height: 90px; width: auto;
      cursor: pointer;
}
#GalleryListLeft, #GalleryListRight{
      position: absolute;
      height: 104px; width: 50px;
      cursor: pointer; overflow: hidden;
      font-family: Verdana, Geneva, sans-serif;
      font-weight: bold; font-size: 126px;
      line-height: 90px; color: #069;
}
#GalleryListLeft span{
      margin-left: -5px;
}
#GalleryListRight span{
      margin-left: -55px;
}
#GalleryListLeft:hover, #GalleryListRight:hover{
      color: #09C;
}

Feel free to use or modify and play around with this code at your own risk and convenience. If you use this code on your website, please do provide due credits to me in the ImageGallery.js file (I guess that’s not too much to ask for).