var ff = {
		
	updateScrollPositionTimeout: null,
	updatePositionFromScrollTimeout: null,
	ignoreNextHashChange: false,
		
    onDocumentReady: function() {
        this.init();
    },
    
    init: function() {
    	this.initPage();
    	// Only for pages with menu
    	if ($('nav.menu').length > 0) {
    		this.initMenu();
    		this.initScroll();
    	}
    	if((navigator.userAgent.match(/iPad/i) || navigator.userAgent.match(/iPhone/i))) {
    	    this.initIpad();
    	}
    },
    
    isIpad: false,
    isIpadOffsetError: false,
    isIpadPositionFixedBroken: false,
    isIpadTouchSinceOrientationChange: true,
    
    initIpad: function() {
    	var that=this;
    	
        this.isIpad = true;
        if (navigator.userAgent.match(/; CPU.*OS (?:3_2|4_.)/i)) {
            this.isIpadOffsetError = true;
            this.isIpadPositionFixedBroken = true;
        }
        
        // Even in iOS 5.0/5.0.1, position:fixed is not 100% working (only first click / touchstart on menu items)
        this.isIpadPositionFixedBroken = true;
    	
        
        var ipadScrollTouchTracking = false;
        var ipadScrollTouchStartY = 0;
        var ipadScrollTouchLastY = 0;
        var ipadScrollTouchLastTime = 0;
        var ipadScrollTouchLastVelocity = 0;
        var ipadScrollWindowStartY = 0;
        var ipadScrollTouchStartTallArticleId = null;
        
        $('nav.menu li a, #ff-nav-logo-link, .internal-link').bind('touchstart', function(event) {
        	that.ipadScrollTouchTracking = false;
        	event.stopPropagation();
        	that.menuItemClick(event);
        });
        
        $(window).bind('touchstart', function(event) {
        	that.isIpadTouchSinceOrientationChange = true;
        	
        	that.ipadScrollTouchTracking = true;
        	
        	// stop scroll animation
        	$('html,body').stop();
        	
        	ipadScrollTouchStartTallArticleId = null;
        	var ipadScrollTouchStartArticle = that.findClosestArticle(0);
        	if (ipadScrollTouchStartArticle) {
        		// For articles longer than viewport height, allow for scroll without snapping
        		if (ipadScrollTouchStartArticle.height() > window.innerHeight) {
        			ipadScrollTouchStartTallArticleId = ipadScrollTouchStartArticle.attr('id');
        		}
        	}
        	ipadScrollTouchLastVelocity = 0;
        	ipadScrollTouchStartY = event.originalEvent.touches[0].clientY;
        	ipadScrollTouchLastY = ipadScrollTouchStartY;
        	ipadScrollTouchLastTime = new Date().getTime();
        	ipadScrollWindowStartY = $(window).scrollTop();
        	
        });

        
        $(window).bind('touchmove', function(event) {
        	
        	if (!that.ipadScrollTouchTracking) return;
        	
        	event.preventDefault();
        	var now = new Date().getTime();
        	var newY = event.originalEvent.touches[0].clientY;
        	ipadScrollTouchLastVelocity = (newY - ipadScrollTouchLastY) / (now - ipadScrollTouchLastTime);
        	ipadScrollTouchLastY = newY;
        	ipadScrollTouchLastTime = now; 
        	var scrollDestination = Math.round(ipadScrollWindowStartY + (ipadScrollTouchStartY - ipadScrollTouchLastY)*1.1);
        	
        	$(window).scrollTop( scrollDestination );
        	
        	return false;
        });
        $(window).bind('touchend touchcancel', function(event) {
        	
        	if (!that.ipadScrollTouchTracking) return;
        	
        	event.preventDefault();
        	
        	// Look for page flick user interaction: if the end position differs enough from start position, flick to that
        	var pageFlickOffset = 0;
        	if (Math.abs(ipadScrollTouchLastVelocity) > 1.0) {
        		// flick to next!
        		pageFlickOffset = (ipadScrollTouchLastVelocity > 0) ? -1 : 1;
        	}
        	else {
        		if (ipadScrollTouchStartTallArticleId != null) {
            		var currentArticle = that.findClosestArticle(0);
            		if (currentArticle != null && currentArticle.attr('id') == ipadScrollTouchStartTallArticleId) {
            			// Only snap if article's top or bottom is visible
            			if (currentArticle.offset().top > $(window).scrollTop()) {
            				// Snap to top (i.e. default scroll
            			}
            			else {
            				var nextArticle = that.findClosestArticle(1);
            				if (nextArticle != null && nextArticle.offset().top < ($(window).scrollTop() + window.innerHeight)) {
            					// Next article's top is visible, snap to bottom
            					that.scrollToOffset(nextArticle.offset().top - window.innerHeight);
            					return;
            				}
            				else {
            					// Do not snap (stay in the middle of the article)
            					return;
            				}
            			}
            					
            		}
        		}
        	}
        	
        	that.updatePositionFromScroll(pageFlickOffset); // Immediately try to figure out which page is currently shown 
        	that.updateScrollPosition();     // And then immediately afterwards scroll to snap to that page.
        	
        	return false;
        	
        });
        
        $(window).bind('orientationchange', $.proxy(this.ipadUpdateOrientation, this));
        if (this.isIpadPositionFixedBroken) $(window).bind('scroll', $.proxy(this.ipadUpdateScroll, this));
        this.onWindowResize(null);
        this.ipadUpdateOrientation();
        this.ipadUpdateScroll();
    },
    
    ipadUpdateOrientation: function() {
    	this.isIpadTouchSinceOrientationChange = false;
    	
    	if (this.isIpadOffsetError) {
    		// These versions do not always properly trigger window.resize after orientation change
    		this.onWindowResize(null);
    		this.galleryOnWindowResize();
    	}
    	/*
        var height = 1130;
        if (window.orientation == -90 || window.orientation == 90) {
            height = 620;
        }
        $('.loop .article-container').css('min-height', height);
        //this.queueUpdateScrollPosition();
        this.galleryOnWindowResize();
        */
    },
    
    ipadUpdateScrollTimeout: null, 
    ipadUpdateScroll: function() {
    	if (this.isIpadPositionFixedBroken) {
    		//$('header, aside.main').css({ top: $(window).scrollTop(), position: 'absolute'});
    		if (this.ipadUpdateScrollTimeout) {
    			clearTimeout(this.ipadUpdateScrollTimeout);
    			this.ipadUpdateScrollTimeout = 0; 
    		}
    		$('header, aside.main').stop(true);
    		$('header, aside.main').css({position: 'absolute', opacity: 0, top: $(window).scrollTop(), });
    		this.ipadUpdateScrollTimeout = setTimeout(function(){
    			$('header, aside.main').animate({opacity:1});
    		}, 200);
    		
    	}
    },
    
    initPage: function() {
    	var that = this;
        // Re-map all article IDs
        $('div.article-container').each(function(index, element) {
        	$(element).attr('id', 'js-'+element.id);
        });
        // Fixup initial deeplink
        if (location.hash.length > 0 && location.hash.substr(0,1) == '#') {
        	var originalHash = location.hash.substr(1);
            setTimeout(function(){that.updateMenu(originalHash)}, 250);
            this.scrollToVirtualHash('#'+originalHash);
        }
    },
    
    initMenu: function() {
    	// Close submenus
        $('nav.menu ul ul').hide();
        // Bind clicks (but see initIpad too)
        $('nav.menu li a').bind('click', $.proxy(this.menuItemClick, this));
        $('#ff-nav-logo-link').bind('click', $.proxy(this.menuItemClick, this));
        $('.internal-link').bind('click', $.proxy(this.menuItemClick, this));
    },

    initScroll: function() {
    	$(window).bind('resize', $.proxy(this.onWindowResize, this));
    	$(window).bind('hashchange', $.proxy(this.onWindowHashchange, this));
    	$(window).scroll($.proxy(this.onWindowScroll, this));
    	$(document).keydown($.proxy(this.onWindowKeypress, this));
    },
    
    updateScrollPosition: function() {
    	// Re-scroll window so we snap to a page. This happens on layout change (window resize) or back/forward
    	this.scrollToVirtualHash(location.hash);
    },

    updateMenu: function(activeArticleId) {
    	
		var menuEquivalent = this.getMenuEquivalentForArticleId(activeArticleId);
		if (menuEquivalent) {
			activeArticleId = menuEquivalent;    			
		}
    	
		activeArticleId = '#'+activeArticleId;
    	
    	$('nav.menu li a').each(function(index, element){
    		var isSelected = false;
    		var $element = $(element);
    		if ($element.attr('href') == activeArticleId) {
    			$element.addClass('selected');
    			isSelected = true;
    		}
    		else {
    			$element.removeClass('selected');
    		}
    		
    		// collapse/expand menu?
    		var parentLI = $element.closest('li');
    		var submenu = parentLI.children('ul');
    		if (submenu.length > 0) {
    			 // A menuitem with a submenu
    			if (!isSelected) {
    				// Maybe a child of this is selected
    				submenu.children('li').children('a').each(function(index, element) {
    					if ($(element).attr('href') == activeArticleId) {
    						isSelected = true;
    						return false; // break $.each
    					}
    				});
    			}
    			
    			// Set menu to correct state
    			if (submenu.is(':hidden') != !isSelected) {
    				// Menu state needs changing
    				if (isSelected) {
    					parentLI.addClass('open');
    					submenu.slideDown(400);
    				}
    				else {
    					parentLI.removeClass('open');
    					submenu.slideUp(400);
    				}
    			}
    		}
    	});
    	
    	
    },
    
    getMenuEquivalentForArticleId: function(articleId) {
    	var $article = $('#js-'+articleId);
    	return $article.attr('data-menu-equivalent');
    },
    
    findClosestArticle: function(offset) {
    	var closestArticle = null;
    	var windowScrollTop = $(window).scrollTop() + window.innerHeight/2;
    	$('div.article-container').each(function(index, element) {
    		var $element = $(element);
    		if (closestArticle == null || ($element.offset().top > closestArticle.offset().top && $element.offset().top < windowScrollTop)) {
    			closestArticle = $element;
    		}
    	});
    	
		var offsetClosestArticle = undefined;
    	if (offset) {
    		switch (offset) {
    			case 1:
    				offsetClosestArticle = closestArticle.next('div.article-container');
    				break;
    			case -1:
    				offsetClosestArticle = closestArticle.prev('div.article-container');
    				break;
    		}
    	}
    	
    	if (typeof offsetClosestArticle == 'undefined') {
    		return closestArticle;
    	}
    	return offsetClosestArticle;
    },
    
    updatePositionFromScroll: function(offset) {
    	// User has scrolled, update menu and hash in case we're now on a different article.
    	// Find currently visible article
    	var closestArticle = this.findClosestArticle(offset);
    	
    	if (closestArticle) {
    		var articleId = closestArticle.attr('id').replace(/^js-/, '');
    		this.ignoreNextHashChange = true; // Prevent the following updateDeeplink from scrolling to article top (as that would interfere with the user's manual scroll) 
    		this.updateMenu(articleId);
    		this.updateDeeplink('#'+articleId);
    	}
    	
    },
    
    queueUpdateScrollPosition: function() {
    	if (this.updateScrollPositionTimeout) {
    		clearTimeout(this.updateScrollPositionTimeout);
    		this.updateScrollPositionTimeout = null;
    	}
    	this.updateScrollPositionTimeout = setTimeout($.proxy(this.updateScrollPosition, this), 250);
    },

    queueUpdatePositionFromScroll: function() {
    	if (this.updatePositionFromScrollTimeout) {
    		clearTimeout(this.updatePositionFromScrollTimeout);
    		this.updatePositionFromScrollTimeout = null;
    	}
    	this.updatePositionFromScrollTimeout = setTimeout($.proxy(this.updatePositionFromScroll, this), 250);
    },
    
    getCurrentArticleFromHash: function() {
    	var loc = location.hash.replace(/#/,'');
    	if (loc) {
    		return $('#js-'+loc).first();
    	}
    	return $('div.article-container').first();
    },
    
    onWindowResize: function(e) {
    	if (this.isIpad) {
    		$('.loop .article-container').css('min-height', window.innerHeight);    		
    	}
    	this.queueUpdateScrollPosition();
    },
    onWindowHashchange: function(e) {
    	if (this.ignoreNextHashChange) {
    		this.ignoreNextHashChange = false;
    		return;
    	}
    	this.queueUpdateScrollPosition();
    },
    onWindowScroll: function(e) {
    	if (this.isIpad && !this.isIpadTouchSinceOrientationChange) return; // on iPad, don't act on scrolls caused by orientation changes. a touchstart anywhere unlocks this handler again
    	this.queueUpdatePositionFromScroll();
    },
    onWindowKeypress: function(e) {
    	e = e || window.event;
    	var currentArticle = this.getCurrentArticleFromHash();
    	if (currentArticle) {
	    	var scrollBy = null;
	    	if (e.keyCode == 33) {      // pgup
	    		scrollBy = currentArticle.prev;
	    	}
	    	else if (e.keyCode == 34) { // pgdn
	    		scrollBy = currentArticle.next;
	    	}
	    	else if (e.keyCode == 32) { // space
	    		scrollBy = currentArticle.next;
	    	}
	    	if (scrollBy != null) {
	    		var nextArticle = scrollBy.apply(currentArticle, ['div.article-container']);
	    		if (nextArticle.length > 0) {
	    			var virtualTarget = '#'+nextArticle.attr('id').replace(/js-/, '');
	    			this.ignoreNextHashChange = true;
	    			this.updateDeeplink(virtualTarget)
	    			this.scrollToVirtualHash(virtualTarget)
	    			e.preventDefault();
	    			e.stopPropagation();
	    			return false;
	    		}
	    	}
    	}
    },
    
    updateDeeplink: function(newFragment) {
    	location.href = location.href.replace(/(#.*)?$/, newFragment);
    },
    
    scrollToOffset: function(targetScrollOffset) {
		$('html,body').stop();
		$('html,body').animate({scrollTop: targetScrollOffset}, {duration: 500, queue:false, complete:function() {
		    if (ff.isIpad) {
		        ff.ipadUpdateScroll();
		    }
		}});
    },
    
    scrollToRealHash: function(realHash) {
    	if (!realHash) return;
		var realDestinationElement = $('#'+realHash);
		if (realDestinationElement.length < 1) return;
		var targetScrollOffset = realDestinationElement.offset().top;
		if (this.isIpadOffsetError) {
		    targetScrollOffset -= $(window).scrollTop();
		}
		this.scrollToOffset(targetScrollOffset);
    },
    
    scrollToVirtualHash: function(virtualHash) {
		this.scrollToRealHash('js-'+virtualHash.substr(1));
    },
    
    menuItemClick: function(e) {
    	var clickedAnchor = $(e.currentTarget);
    	
    	// Link handling and scrolling
    	var destination = clickedAnchor.attr('href'); // '#fragment'
    	if (destination.charAt(0) == '#') {
    		this.updateMenu(destination.substr(1));
    		this.updateDeeplink(destination);
    		this.scrollToVirtualHash(destination);
    		return false; // Don't follow a href
    	}
    },
    
    galleryImageData: [],
    
    initGallery : function(data) {
        var imageContainer = $('.gallery .image-container');
        var captionContainer = $('.gallery .image-caption-container');
        var thumbContainer = $('.gallery .gallery-scrolling-content');
        for (var i = 0; i < data.length; i++) {
            var d = data[i];
            this.galleryImageData.push({largeUrl:d['largeUrl'], largeWidth:d['largeWidth'], largeHeight:d['largeHeight']});
            //imageContainer.append($('<img style="display:none;" />').attr('src', d['largeUrl']).attr('width', d['largeWidth']).attr('height', d['largeHeight']));
            captionContainer.append($('<div class="caption" />').append($('<span class="index">').append((i + 1) + ' / ' + data.length)).append(d['caption']));
            thumbContainer.append($('<div class="thumbnail-container"/>').append($('<img/>').attr('src', d['thumbUrl']).attr('width', 75).attr('height', 75)).append($('<div class="thumbnail-mask" />')));
        }
        var that = this;
        $('.gallery').each(function(index, element) {
            var gallery = $(element);
            var imageContainer = gallery.children('.image-container');
            imageContainer.children().each(function(index, element) {
                var image = $(element);
                image.hide();
                image.data('originalWidth', image.width());
                image.data('originalHeight', image.height());
            });
            var imageCaptionContainer = gallery.children('.image-caption-container');
            imageCaptionContainer.children().hide();
            var galleryContainer = gallery.children('.gallery-container');
            gallery.find('.gallery-scrolling-container').each(function(index, element) {
                var container = $(element);
                gallery.data('scrollIndex', 0);
                var content = container.children('.gallery-scrolling-content');
                var numItems = content.children().length;
                gallery.data('numItems', numItems);
                content.children().each(function(index, element) {
                    var item = $(element);
                    item.click(function() {
                        that.gallerySelectImageByIndex(gallery, index);
                    });
                });
                if (numItems > 0) {
                    that.gallerySelectImageByIndex(gallery, 0);
                }
            });
            gallery.find('.scroll-back-button').click(function() {
                that.galleryScrollBack(gallery);
            });
            gallery.find('.scroll-forward-button').click(function() {
                that.galleryScrollForward(gallery);
            });
            gallery.find('.image-prev-button').click(function() {
                that.galleryPrevImage(gallery);
            });
            gallery.find('.image-next-button').click(function() {
                that.galleryNextImage(gallery);
            })
            $(window).resize($.proxy(that.galleryOnWindowResize, that));
            that.galleryOnWindowResize();
        });
    },
    
    gallerySelectImageByIndex : function(gallery, index) {
        var prevSelectedIndex = gallery.data('selectedIndex');
        var numItems = gallery.data('numItems');
        if (index != prevSelectedIndex) {
            gallery.data('selectedIndex', index);
            var prevThumb = gallery.find('.gallery-scrolling-content > .selected').removeClass('selected');
            var prevCaption = gallery.find('.image-caption-container > .selected').removeClass('selected').fadeOut(400);
            var prevImage = gallery.find('.image-container > .selected').removeClass('selected').fadeOut(400);
            var thumb = gallery.find('.gallery-scrolling-content').children().eq(index).addClass('selected');
            var caption = gallery.find('.image-caption-container').children().eq(index).fadeIn(400).addClass('selected');
            //var image = gallery.find('.image-container').children().eq(index).fadeIn(400).addClass('selected');
            
            var imageContainer = gallery.find('.image-container');
            var currentImage = imageContainer.children();
            var d = this.galleryImageData[index];
            var newImage = $('<img style="display:none;" />').attr('src', d['largeUrl']).data('originalWidth', d['largeWidth']).data('originalHeight', d['largeHeight']);
            imageContainer.append(newImage);
            this.galleryOnWindowResize();
            currentImage.fadeOut(500, function() {
                currentImage.remove();
            })
            newImage.fadeIn(500);
            
            // get current image
            // clone and change url
            // fade in
            // delete old
            // call galleryOnWindowResize to adjust aspect
            
            if (index <= 0) {
                gallery.find('.image-prev-button').addClass('disabled');
            } else {
                gallery.find('.image-prev-button').removeClass('disabled');
            }
            if (index >= numItems - 1) {
                gallery.find('.image-next-button').addClass('disabled');
            } else {
                gallery.find('.image-next-button').removeClass('disabled');
            }
        }
    },
    
    galleryNextImage : function(gallery) {
        var prevSelectedIndex = gallery.data('selectedIndex');
        var numItems = gallery.data('numItems');
        if (prevSelectedIndex < numItems - 1) {
            this.gallerySelectImageByIndex(gallery, prevSelectedIndex + 1);
            this.galleryMakeSureSelectedIsVisible(gallery, prevSelectedIndex, prevSelectedIndex + 1);
        }
    },
    
    galleryPrevImage : function(gallery) {
        var prevSelectedIndex = gallery.data('selectedIndex');
        if (prevSelectedIndex > 0) {
            this.gallerySelectImageByIndex(gallery, prevSelectedIndex - 1);
            this.galleryMakeSureSelectedIsVisible(gallery, prevSelectedIndex, prevSelectedIndex - 1);
        }
    },
    
    galleryMakeSureSelectedIsVisible : function(gallery, prevIndex, newIndex) {
        // if prevIndex was visible, make sure newIndex is visible
        var numItems = gallery.data('numItems');
        var that = this;
        gallery.find('.gallery-scrolling-container').each(function(index, element) {
            var container = $(element);
            var content = container.children('.gallery-scrolling-content');
            var scrollIndex = gallery.data('scrollIndex');
            var visibleItems = gallery.data('currentVisibleItems');
            var increment = gallery.data('currentIncrement');
            if (scrollIndex <= prevIndex && prevIndex < scrollIndex + visibleItems) {
                if (newIndex < scrollIndex) {
                    scrollIndex--;
                    gallery.data('scrollIndex', scrollIndex);
                    content.animate({'left': -scrollIndex * increment}, 400);
                    that.galleryUpdateScrollButtonVisibility(gallery);
                } else if (newIndex >= scrollIndex + visibleItems) {
                    scrollIndex++;
                    gallery.data('scrollIndex', scrollIndex);
                    content.animate({'left': -scrollIndex * increment}, 400);
                    that.galleryUpdateScrollButtonVisibility(gallery);
                }
            }
        });
    },
    
    galleryOnWindowResize : function() {
        var that = this;
        $('.gallery').each(function(index, element) {
            var gallery = $(element);
            gallery.find('.image-container').each(function(index, element) {
                var imageContainer = $(element);
                var containerWidth = imageContainer.width();
                var containerHeight = imageContainer.height();
                imageContainer.children().each(function(index, element) {
                    var image = $(element);
                    var width = image.data('originalWidth');
                    var height = image.data('originalHeight');
                    var aspect = width / height;
                    var widthFactor = containerWidth / width;
                    var heightFactor = containerHeight / height;
                    if (widthFactor > heightFactor) {
                        image.height(containerHeight);
                        image.width(width * heightFactor);
                        image.css('top', 0).css('left', (containerWidth - width * heightFactor) / 2);
                    } else {
                        image.width(containerWidth);
                        image.height(height * widthFactor);
                        image.css('left', 0).css('top', (containerHeight - height * widthFactor) / 2);
                    }
                });
            });
            gallery.find('.gallery-scrolling-container').each(function(index, element) {
                var container = $(element);
                var scrollIndex = gallery.data('scrollIndex');
                var prevNumItems = gallery.data('numItems');
                var selectedIndex = gallery.data('selectedIndex');
                var selectedWasVisible = (scrollIndex <= selectedIndex && selectedIndex < scrollIndex + prevNumItems);
                var content = container.children('.gallery-scrolling-content');
                var containerWidth = container.width();
                if (containerWidth != gallery.data('currentWidth')) {
                    gallery.data('currentWidth', containerWidth);
                    var minSpacing = 15;
                    var sidePadding = 15;
                    var elementWidth = 75;
                    var numItems = gallery.data('numItems');
                    var visibleItems = Math.floor((containerWidth - 2 * sidePadding + minSpacing) / (elementWidth + minSpacing));
                    var increment = (containerWidth - 2 * sidePadding - visibleItems * elementWidth) / (visibleItems - 1) + elementWidth;
                    gallery.data('currentIncrement', increment);
                    var currentSpacing = increment - elementWidth;
                    var totalWidth = sidePadding + 2 - currentSpacing + increment * numItems;
                    content.css('width', totalWidth);
                    content.children().each(function(index, element) {
                        var pos = sidePadding + index * increment;
                        $(element).css('left', pos);
                    });
                    if (visibleItems != gallery.data('currentVisibleItems')) {
                        gallery.data('currentVisibleItems', visibleItems);
                        if (selectedWasVisible && selectedIndex >= scrollIndex + visibleItems) {
                            scrollIndex = selectedIndex - visibleItems + 1;
                        } else if (scrollIndex > numItems - visibleItems) {
                            scrollIndex = Math.max(0, numItems - visibleItems);
                        }
                        gallery.data('scrollIndex', scrollIndex);
                    }
                    content.css('left', -scrollIndex * increment);
                }
            });
            that.galleryUpdateScrollButtonVisibility(gallery);
        });
    },
    
    galleryScrollBack : function(gallery) {
        var imageContainer = gallery.children('.image-container');
        var imageCaptionContainer = gallery.children('.image-caption-container');
        gallery.find('.gallery-container > .gallery-scrolling-container').each(function(index, element) {
            var container = $(element);
            var prevScrollIndex = gallery.data('scrollIndex');
            var increment = gallery.data('currentIncrement');
            var numItems = gallery.data('numItems');
            var visibleItems = gallery.data('currentVisibleItems');
            var content = container.children('.gallery-scrolling-content');
            if (prevScrollIndex > 0) {
                var scrollIndex = Math.max(0, prevScrollIndex - Math.ceil(visibleItems / 2));
                gallery.data('scrollIndex', scrollIndex);
                content.animate({'left': -scrollIndex * increment}, 400);
            }
        });
        this.galleryUpdateScrollButtonVisibility(gallery);
    },
    
    galleryScrollForward : function(gallery) {
        var imageContainer = gallery.children('.image-container');
        var imageCaptionContainer = gallery.children('.image-caption-container');
        gallery.find('.gallery-container > .gallery-scrolling-container').each(function(index, element) {
            var container = $(element);
            var prevScrollIndex = gallery.data('scrollIndex');
            var increment = gallery.data('currentIncrement');
            var numItems = gallery.data('numItems');
            var visibleItems = gallery.data('currentVisibleItems');
            var content = container.children('.gallery-scrolling-content');
            var maxScrollIndex = numItems - visibleItems;
            if (prevScrollIndex < maxScrollIndex) {
                var scrollIndex = Math.max(0, Math.min(maxScrollIndex, prevScrollIndex + Math.ceil(visibleItems / 2)));
                gallery.data('scrollIndex', scrollIndex);
                content.animate({'left': -scrollIndex * increment}, 400);
            }
        });
        this.galleryUpdateScrollButtonVisibility(gallery);
    },
    
    galleryUpdateScrollButtonVisibility : function(gallery) {
        var backButton = gallery.find('.scroll-back-button');
        var forwardButton = gallery.find('.scroll-forward-button');
        gallery.find('.gallery-container > .gallery-scrolling-container').each(function(index, element) {
            var container = $(element);
            var scrollIndex = gallery.data('scrollIndex');
            var numItems = gallery.data('numItems');
            var visibleItems = gallery.data('currentVisibleItems');
            var content = container.children('.gallery-scrolling-content');
            var maxScrollIndex = numItems - visibleItems;
            if (scrollIndex <= 0) {
                backButton.addClass('disabled');
            } else {
                backButton.removeClass('disabled');
            }
            if (scrollIndex >= maxScrollIndex) {
                forwardButton.addClass('disabled');
            } else {
                forwardButton.removeClass('disabled');
            }
        });
    }

    
};

jQuery(document).ready(jQuery.proxy(ff.onDocumentReady, ff));
