/* ** ** GalleryView - jQuery Content Gallery Plugin ** Author: Jack Anderson ** Version: 3.0b3 (March 15, 2011) ** ** Please use this development script if you intend to make changes to the ** plugin code. For production sites, it is recommended you use jquery.galleryview-3.0.min.js. ** ** See README.txt for instructions on how to markup your HTML ** ** See CHANGELOG.txt for a review of changes and LICENSE.txt for the applicable ** licensing information. ** */ //Global variable to check if window is already loaded //Used for calling GalleryView after page has loaded var window_loaded = false; $(window).load(function(){ window_loaded = true; }); (function($){ $.fn.galleryView = function(options) { var opts = $.extend($.fn.galleryView.defaults,options); var id; var iterator = 0; // INT - Currently visible panel/frame var item_count = 0; // INT - Total number of panels/frames var slide_method; // STRING - indicator to slide entire filmstrip or just the pointer ('strip','pointer') var paused = false; // BOOLEAN - flag to indicate whether automated transitions are active var pointer_speed = 0; // INT - Speed (in milliseconds) of pointer animation var animate_panels = true; // BOOLEAN - flag to indicate whether panels will animate during transitions var current = 1; // INT - index of current panel/frame var gallery_images; // OBJECT - container for images within UL passed to plugin var image_count = 0; // INT - number of images within gallery var loaded_images = 0; // INT - number of gallery images that have been loaded in the browser // Element dimensions var gallery_width; var gallery_height; var pointer_height; var pointer_width; var strip_width; var strip_height; var wrapper_width; var wrapper_height; var f_frame_width; var f_frame_height; var filmstrip_orientation; // Arrays used to scale frames and panels var frame_img_scale = {}; var panel_img_scale = {}; var img_h = {}; var img_w = {}; // Flag indicating whether to scale panel images var scale_panel_images = true; var panel_nav_displayed = false; // Define jQuery objects for reuse var j_gallery; var j_filmstrip; var j_frames; var j_frame_img_wrappers; var j_panels; var j_pointer; var j_panel_wrapper; /* ** Plugin Functions */ /* ** showItem(int,boolean,function) ** Transition from current frame to frame i (1-based index) ** skip_animation flag let's us override transition speed to show an item instantly ** (useful when loading gallery for the first time) ** If provided, run callback function after transition completes */ function showItem(i,speed,callback) { // A scrolling filmstrip will contain three copies of each frame, we want to know the relative position of the target frame var mod_i = i%item_count; var distance; var diststr; // Disable next/prev buttons until transition is complete // This prevents overlapping of animations $('.gv-nav-next, .gv-panel-nav-next, .gv-nav-prev, .gv-panel-nav-prev',j_gallery).unbind('click'); j_frames.unbind('click'); // Use timer to rebind navigation buttons when transition ends $(document).oneTime(speed,'bindNavButtons',function(){ $('.gv-nav-next, .gv-panel-nav-next',j_gallery).click(showNextItem); $('.gv-nav-prev, .gv-panel-nav-prev',j_gallery).click(showPrevItem); enableFrameClicking(); }); if(opts.show_filmstrip) { // Fade out all frames j_frames.removeClass('current').find('img').stop().animate({opacity:opts.frame_opacity},speed); // Fade in target frame j_frames.eq(i).addClass('current').find('img').stop().animate({opacity:1},speed); } //If necessary, transition between panels if(opts.show_panels) { if(animate_panels) { if(opts.panel_animation=='slide') { // Move target frame just to the right of the current frame j_panels.eq(mod_i).css({ left: getInt($('.gv-panel.current').eq(0).css('left'))+opts.panel_width+'px', zIndex: 50 }).show().animate({ left: '-='+opts.panel_width+'px' },speed,opts.easing,function(){ $(this).addClass('current'); }); // Slide current frame and target frame to the left $('.gv-panel.current').css({zIndex:49}).animate({ left: '-='+opts.panel_width+'px' },speed,opts.easing,function(){ $(this).removeClass('current').hide(); }); } else if(opts.panel_animation=='zoomOut') { // After zoom is complete, add 'current' class to now visible panel and move it to top of stack via z-index $(document).oneTime(speed,'setCurrentFrame',function(){ j_panels.eq(mod_i).addClass('current').css('zIndex',50); }); // Show target panel and place below current frame via z-index j_panels.eq(mod_i).show().css('zIndex',49); // Shrink panel container to center while moving image in opposite direction // End result is an image that remains static while borders of container shrink $('.gv-panel.current img').animate({ top: '-='+opts.panel_height/2+'px', left:'-='+opts.panel_width/2+'px' },speed,'swing',function(){ // After zoom is complete, immediately animate it back to its original position for next time $(this).animate({ top: '+='+opts.panel_height/2+'px', left: '+='+opts.panel_width/2+'px' },0); }); $('.gv-panel.current').animate({ top:'+='+opts.panel_height/2+'px', left:'+='+opts.panel_width/2+'px', height:0, width:0 },speed,'swing',function(){ $(this).removeClass('current').hide().css({ top:getPos(j_panels[mod_i]).top+'px', left:getPos(j_panels[mod_i]).left+'px', height:opts.panel_height+'px', width:opts.panel_width+'px' }); }); } else if(opts.panel_animation == 'crossfade') { // Default behavior is to fade panel into view j_panels.removeClass('current').fadeOut(speed,function(){$(this).css('filter','');}).eq(mod_i).addClass('current').fadeIn(speed,function(){$(this).css('filter','');}); } else { // Fade out panels, and then fade in target panel // Use timer due to inconsistency in fadeIn/fadeOut callback reliability j_panels.removeClass('current').stop().fadeOut(speed/2); $(document).oneTime(speed/2,'fadeInPanel',function(){ j_panels.eq(mod_i).addClass('current').stop().fadeIn(speed/2); }); } } else { // If no animation style is chosen, simply show the new panel instantly $(document).oneTime(speed,'switch_panels',function(){j_panels.hide().eq(mod_i).show();}); } } // If gallery has a filmstrip, handle animation of frames if(opts.show_filmstrip) { // Slide either pointer or filmstrip, depending on transition method if(opts.filmstrip_style == 'scroll' && slide_method=='strip') { // Stop filmstrip if it's currently in motion j_filmstrip.stop(); if(filmstrip_orientation=='horizontal') { // Determine distance between pointer (eventual destination) and target frame distance = getPos(j_frames[i]).left - (getPos(j_pointer[0]).left+(pointer_width/2)-(f_frame_width/2)); diststr = (distance>=0?'-=':'+=')+Math.abs(distance)+'px'; // Animate filmstrip and slide target frame under pointer j_filmstrip.animate({left:diststr},speed,opts.easing,function(){ var old_i = i; // After transition is complete, shift filmstrip so that a sufficient number of frames // remain on either side of the visible filmstrip if(i>item_count) { i = mod_i; iterator = i; j_filmstrip.css('left','-'+((f_frame_width+opts.frame_gap)*i)+'px'); } else if (i<=(item_count-strip_size)) { i = (mod_i)+item_count; iterator = i; j_filmstrip.css('left','-'+((f_frame_width+opts.frame_gap)*i)+'px'); } // If the target frame has changed due to filmstrip shifting, // make sure new target frame has 'current' class and correct size/opacity settings if(old_i != i) { j_frames.eq(old_i).removeClass('current').find('img').css({opacity:opts.frame_opacity}); j_frames.eq(i).addClass('current').find('img').css({opacity:1}); } }); } else { //Determine distance between pointer (eventual destination) and target frame distance = getPos(j_frames[i]).top-getPos($('.gv-strip_wrapper',j_gallery)[0]).top; diststr = (distance>=0?'-=':'+=')+Math.abs(distance)+'px'; // Animate filmstrip and slide target frame under pointer j_filmstrip.animate({ 'top':diststr },speed,opts.easing,function(){ // After transition is complete, shift filmstrip so that a sufficient number of frames // remain on either side of the visible filmstrip var old_i = i; if(i>item_count) { i = mod_i; iterator = i; j_filmstrip.css('top','-'+((f_frame_height+opts.frame_gap)*i)+'px'); } else if (i<=(item_count-strip_size)) { i = (mod_i)+item_count; iterator = i; j_filmstrip.css('top','-'+((f_frame_height+opts.frame_gap)*i)+'px'); } //If the target frame has changed due to filmstrip shifting, //Make sure new target frame has 'current' class and correct size/opacity settings if(old_i != i) { j_frames.eq(old_i).removeClass('current').find('img').css({opacity:opts.frame_opacity}); j_frames.eq(i).addClass('current').find('img').css({opacity:1}); } // If panels are not set to fade in/out, simply hide all panels and show the target panel if(!animate_panels) { j_panels.hide().eq(mod_i).show(); } }); } } else if(slide_method=='pointer') { // Stop pointer if it's currently in motion j_pointer.stop(); // Get screen position of target frame var pos = getPos(j_frames[i]); if(filmstrip_orientation=='horizontal') { // Slide the pointer over the target frame j_pointer.animate({ left:pos.left+(f_frame_width/2)-(pointer_width/2)+'px' },pointer_speed,opts.easing,function(){ if(!animate_panels) {j_panels.hide().eq(mod_i).show();} }); } else { // Slide the pointer over the target frame j_pointer.animate({ top:pos.top+(f_frame_height/2)-(pointer_height)+'px' },pointer_speed,opts.easing,function(){ if(!animate_panels) {j_panels.hide().eq(mod_i).show();} }); } } } if(callback) {$(document).oneTime(speed,'showItemCallback',callback);} current = i; }; /* ** extraWidth(jQuery element) ** Return the combined width of the border and padding to the elements left and right. ** If the border is non-numerical, assume zero (not ideal, will fix later) ** RETURNS - int */ function extraWidth(el) { if(!el) { return 0; } if(el.length==0) { return 0; } el = el.eq(0); var ew = 0; ew += getInt(el.css('paddingLeft')); ew += getInt(el.css('paddingRight')); ew += getInt(el.css('borderLeftWidth')); ew += getInt(el.css('borderRightWidth')); return ew; }; /* ** extraHeight(jQuery element) ** Return the combined height of the border and padding above and below the element ** If the border is non-numerical, assume zero (not ideal, will fix later) ** RETURN - int */ function extraHeight(el) { if(!el) { return 0; } if(el.length==0) { return 0; } el = el.eq(0); var eh = 0; eh += getInt(el.css('paddingTop')); eh += getInt(el.css('paddingBottom')); eh += getInt(el.css('borderTopWidth')); eh += getInt(el.css('borderBottomWidth')); return eh; }; /* ** showNextItem() ** Transition from current frame to next frame */ function showNextItem() { // Cancel any transition timers until we have completed this function $(document).stopTime("transition"); if(++iterator==j_frames.length) {iterator=0;} // We've already written the code to transition to an arbitrary panel/frame, so use it showItem(iterator,opts.transition_speed); // If automated transitions haven't been cancelled by an option or paused on hover, re-enable them if(!paused && opts.transition_interval > 0) { $(document).everyTime(opts.transition_interval,"transition",function(){showNextItem();}); } }; /* ** showPrevItem() ** Transition from current frame to previous frame */ function showPrevItem() { // Cancel any transition timers until we have completed this function $(document).stopTime("transition"); if(--iterator<0) {iterator = item_count-1;} // We've already written the code to transition to an arbitrary panel/frame, so use it showItem(iterator,opts.transition_speed); // If automated transitions haven't been cancelled by an option or paused on hover, re-enable them if(!paused && opts.transition_interval > 0) { $(document).everyTime(opts.transition_interval,"transition",function(){showNextItem();}); } }; /* ** getPos(jQuery element) ** Calculate position of an element relative to top/left corner of gallery ** If the gallery bounding box itself is passed to the function, calculate position of gallery relative to top/left corner of browser window ** RETURNS - JSON {left: int, top: int} */ function getPos(el) { if(!el) return {top:0,left:0}; var left = 0, top = 0; var el_id = el.id; if(el.offsetParent) { do { left += el.offsetLeft; top += el.offsetTop; } while(el = el.offsetParent); } //If we want the position of the gallery itself, return it if(el_id == id) {return {'left':left,'top':top};} //Otherwise, get position of element relative to gallery else { var gPos = getPos(j_gallery[0]); var gLeft = gPos.left; var gTop = gPos.top; return {'left':left-gLeft,'top':top-gTop}; } }; /* ** enableFrameClicking() ** Add an onclick event handler to each frame ** Exception: if a frame has an anchor tag, do not add an onlick handler */ function enableFrameClicking() { j_frames.each(function(i){ if($('a',this).length==0) { $(this).click(function(){ // Prevent transitioning to the current frame (unnecessary) if(iterator!=i) { $(document).stopTime("transition"); showItem(i,opts.transition_speed); iterator = i; if(!paused && opts.transition_interval > 0) { $(document).everyTime(opts.transition_interval,"transition",function(){showNextItem();}); } } }); } }); }; /* ** buildPanels() ** Construct gallery panels:
elements ** NOTE - These DIVs are generated automatically from the content of the UL passed to the plugin */ function buildPanels() { // If panel overlay content exists, add the necessary overlay background DIV // The overlay content and background are separate elements so the background's opacity isn't inherited by the content j_panels.each(function(i){ if($('.gv-panel-overlay',this).length>0) { $(this).append('
'); } }); // If there is no filmstrip in this gallery, add navigation buttons to the panel itself if(opts.show_panel_nav) { $('
').addClass('gv-panel-nav-next').appendTo(j_gallery).css({ position:'absolute', zIndex:'1100', top:((opts.filmstrip_position=='top'?opts.frame_gap+wrapper_height:0)+(opts.panel_height-22)/2)+'px', right:((opts.filmstrip_position=='right'?opts.frame_gap+wrapper_width:0)+10)+'px', display:'none' }).click(showNextItem); $('
').addClass('gv-panel-nav-prev').appendTo(j_gallery).css({ position:'absolute', zIndex:'1100', top:((opts.filmstrip_position=='top'?opts.frame_gap+wrapper_height:0)+(opts.panel_height-22)/2)+'px', left:((opts.filmstrip_position=='left'?opts.frame_gap+wrapper_width:0)+10)+'px', display:'none' }).click(showPrevItem); } //Set size and position of panel container j_panel_wrapper.css({ width:opts.panel_width+'px', height:opts.panel_height+'px', position:'absolute', overflow:'hidden' }); if(opts.show_filmstrip) { switch(opts.filmstrip_position) { case 'top': j_panel_wrapper.css({top:wrapper_height+opts.frame_gap+'px'}); break; case 'left': j_panel_wrapper.css({left:wrapper_width+opts.frame_gap+'px'}); break; default: break; } } // Set the height and width of each panel, and position it appropriately within the gallery j_panels.each(function(i){ $(this).css({ width:(opts.panel_width-extraWidth(j_panels))+'px', height:(opts.panel_height-extraHeight(j_panels))+'px', position:'absolute', top:0, left:0, display:'none' }); }); // Position each panel overlay within panel $('.gv-panel-overlay',j_panels).css({ position:'absolute', zIndex:'999', width:(opts.panel_width-extraWidth($('.gv-panel-overlay',j_panels)))+'px', left:0 }); $('.gv-overlay-background',j_panels).css({ position:'absolute', zIndex:'998', width:opts.panel_width+'px', left:0, opacity:opts.overlay_opacity }); if(opts.overlay_position=='top') { $('.gv-panel-overlay',j_panels).css('top',0); $('.gv-overlay-background',j_panels).css('top',0); } else { $('.gv-panel-overlay',j_panels).css('bottom',0); $('.gv-overlay-background',j_panels).css('bottom',0); } $('.gv-panel iframe',j_panels).css({ width:opts.panel_width+'px', height:opts.panel_height+'px', border:0 }); // If panel images have to be scaled to fit within frame, do so and position them accordingly if(scale_panel_images) { $('img',j_panels).each(function(i){ $(this).css({ height:panel_img_scale[i%item_count]*img_h[i%item_count], width:panel_img_scale[i%item_count]*img_w[i%item_count], position:'relative', top:(opts.panel_height-(panel_img_scale[i%item_count]*img_h[i%item_count]))/2+'px', left:(opts.panel_width-(panel_img_scale[i%item_count]*img_w[i%item_count]))/2+'px' }); }); } }; /* ** buildFilmstrip() ** Construct filmstrip from