/*
 * dannydonnelly.com main site JavaScript.
 *
 * @copyright  Copyright 2007 Spenlen Media (http://spenlen.com)
 * @version    $Id: site.js 119 2007-07-05 05:34:55Z walberty $
 */

var DD = {};


DD.MainNav = {

  /**** Constants ****/
  
    /**
     * Delay in milliseconds before the sub-navigation area is displayed when
     * the mouse hovers over the navigation link.
     * @var int
     */
    HOVER_DELAY: 1000,
    
    /**
     * Delay in milliseconds before the sub-navigation area is hidden if the
     * mouse leaves the navigation link or sub-navigation area.
     * @var int
     */
    LINGER_DELAY: 2000,
    
    /**
     * Duration in milliseconds of the slide-in animation.
     * @var int
     */
    ANIMATION_IN_DURATION: 500,
    
    /**
     * Duration in milliseconds of the slide-out animation.
     * @var int
     */
    ANIMATION_OUT_DURATION: 250,
    
    /**
     * Pixel offset from the bottom of the viewport where the bottom of the
     * sub-navigation areas should be aligned.
     * @var int
     */
    SUBNAV_BOTTOM: 45,
  


  /**** Class Variables ****/
  
  
    showTimers:  {},
    hideTimers:  {},
    animations:  {},
    isAnimating: {},



  /**** Initialization ****/
  
  
    /**
     * Attaches mouse events to the navigation links and sub-navigation areas.
     */
    init : function ()
    {
        $$('#main-nav A').each(function (link) {

            var navName = link.id.substring(9);
            
            var subNav = $('main-subnav-' + navName);
            if (subNav) {
                Event.observe(link, 'click', DD.MainNav.navLinkClick.bindAsEventListener(link, navName));
                Event.observe(link, 'mouseover', function () {
                    DD.MainNav.navLinkMouseOver(navName);
                });
                Event.observe(link, 'mouseout',  function () {
                    DD.MainNav.navLinkMouseOut(navName);
                });
                Event.observe(subNav, 'mouseover', function () {
                    DD.MainNav.subNavMouseOver(navName);
                });
                Event.observe(subNav, 'mouseout',  function () {
                    DD.MainNav.subNavMouseOut(navName);
                });
            }

        });
    },
  
  

  /**** Event Handlers ****/
  

    /**
     * 'onclick' handler for the navigation links. Shows the specified
     * sub-navigation area immediately.
     *
     * @param Event  theEvent
     * @param string navName
     */
    navLinkClick : function (theEvent, navName)
    {
        Event.stop(theEvent);    // don't propagate the click
        if ($('main-subnav-' + navName).getStyle('visibility') === 'visible') {
            if (DD.MainNav.hideTimers[navName]) {
                window.clearTimeout(DD.MainNav.hideTimers[navName]);
                DD.MainNav.hideTimers[navName] = null;                
            }
            DD.MainNav.hide(navName);
            return;
        } else if (DD.MainNav.showTimers[navName]) {
            window.clearTimeout(DD.MainNav.showTimers[navName]);
            DD.MainNav.showTimers[navName] = null;
        }
        DD.MainNav.show(navName);
    },

    /**
     * 'onmouseover' handler for the navigation links. Sets a timer to show the
     * specified sub-navigation area after HOVER_DELAY milliseconds. This timer
     * may be cancelled by {@link navLinkMouseOut()}.
     *
     * @param string navName
     */
    navLinkMouseOver : function (navName)
    {
        if ($('main-subnav-' + navName).getStyle('visibility') === 'visible') {
            if (DD.MainNav.hideTimers[navName]) {
                window.clearTimeout(DD.MainNav.hideTimers[navName]);
                DD.MainNav.hideTimers[navName] = null;                
            }
            return;
        } else if (DD.MainNav.showTimers[navName]) {
            return;
        }
        DD.MainNav.showTimers[navName] = window.setTimeout(function () { DD.MainNav.show(navName) }, DD.MainNav.HOVER_DELAY);
    },

    /**
     * 'onmouseout' handler for the navigation links. Sets a timer to hide the
     * specified sub-navigation area after LINGER_DELAY milliseconds. This
     * timer may be cancelled by {@link navLinkMouseOver()} or
     * {@link subNavMouseOver()}.
     *
     * @param string navName
     */
    navLinkMouseOut : function (navName)
    {
        if ($('main-subnav-' + navName).getStyle('visibility') === 'hidden') {
            if (DD.MainNav.showTimers[navName]) {
                window.clearTimeout(DD.MainNav.showTimers[navName]);
                DD.MainNav.showTimers[navName] = null;
            }
            return;
        } else if (DD.MainNav.hideTimers[navName]) {
            return;
        }
        DD.MainNav.hideTimers[navName] = window.setTimeout(function () { DD.MainNav.hide(navName) }, DD.MainNav.LINGER_DELAY);
    },
  
    /**
     * 'onmouseover' handler for the sub-navigation areas. Ensures the area
     * remains visible by clearing hide timers if they exit.
     *
     * @param string navName
     */
    subNavMouseOver : function (navName)
    {
        if (DD.MainNav.hideTimers[navName]) {
            window.clearTimeout(DD.MainNav.hideTimers[navName]);
            DD.MainNav.hideTimers[navName] = null;
        }
    },

    /**
     * 'onmouseout' handler for the sub-navigation areas. Sets a timer to hide
     * the area after LINGER_DELAY milliseconds. This timer may be cancelled by
     * {@link navLinkMouseOver()} or {@link subNavMouseOver()}.
     *
     * @param string navName
     */
    subNavMouseOut : function (navName)
    {
        if (DD.MainNav.hideTimers[navName]) {
            return;
        }
        DD.MainNav.hideTimers[navName] = window.setTimeout(function () { DD.MainNav.hide(navName) }, DD.MainNav.LINGER_DELAY);
    },
  
  

  /**** Animation Functions ****/
  
  
    /**
     * Clears any pending timers for the specified sub-navigation area
     *
     * @param string navName
     */
    clearTimers : function (navName)
    {
        if (DD.MainNav.showTimers[navName]) {
            window.clearTimeout(DD.MainNav.showTimers[navName]);
            DD.MainNav.showTimers[navName] = null;
        }
        if (DD.MainNav.hideTimers[navName]) {
            window.clearTimeout(DD.MainNav.hideTimers[navName]);
            DD.MainNav.hideTimers[navName] = null;
        }
    },
    
    /**
     * Animates the appearance of the specified sub-navigation area.
     *
     * @param string navName
     */
    show : function (navName)
    {
        DD.MainNav.clearTimers(navName);

        var subNav  = $('main-subnav-' + navName);

        if (DD.MainNav.isAnimating[navName]) {
            DD.MainNav.animations[navName].clearTimer();
        }
        
        var mainNav      = $('main-nav-' + navName);
        var linkPosition = Position.positionedOffset(mainNav);
        
        var newLeft      = (linkPosition[0] + mainNav.getWidth() - subNav.getWidth()) + 'px';
        var startBottom  = DD.MainNav.SUBNAV_BOTTOM - subNav.getHeight();

        var currentBottom = parseInt(subNav.getStyle('bottom').replace('px', ''), 10);
        if (currentBottom > startBottom) {
            startBottom = currentBottom;
        }

        subNav.setStyle({'z-index': 20, bottom: startBottom, left: newLeft, visibility: 'visible'});
        
        subNav.siblings().each(function (nav) {
            DD.MainNav.hide(nav.id.substring(12));
        });
        
        if (! DD.MainNav.animations[navName]) {
            DD.MainNav.animations[navName] = new Fx.Style(subNav, 'bottom', {fps: 30});
        };
        
        DD.MainNav.isAnimating[navName] = true;

        DD.MainNav.animations[navName].options.duration   = DD.MainNav.ANIMATION_IN_DURATION;
        DD.MainNav.animations[navName].options.transition = Fx.Transitions.cubicOut;
        DD.MainNav.animations[navName].options.onComplete = function () {
            DD.MainNav.isAnimating[navName] = false;
        };
        
        DD.MainNav.animations[navName].custom(startBottom, DD.MainNav.SUBNAV_BOTTOM);
    },

    /**
     * Animates the disappearance of the specified sub-navigation area.
     *
     * @param string navName
     */
    hide : function (navName)
    {
        DD.MainNav.clearTimers(navName);

        var subNav = $('main-subnav-' + navName);
        if (subNav.getStyle('visibility') === 'hidden') {
            return;
        }

        if (DD.MainNav.isAnimating[navName]) {
            DD.MainNav.animations[navName].clearTimer();
        }
        
        var startBottom = parseInt(subNav.getStyle('bottom').replace('px', ''), 10);
        var endBottom   = DD.MainNav.SUBNAV_BOTTOM - subNav.getHeight();

        if (startBottom < endBottom) {
            subNav.setStyle({visibility: 'hidden'});
            return;
        }

        DD.MainNav.isAnimating[navName] = true;

        subNav.setStyle({'z-index': 10});
        
        DD.MainNav.animations[navName].options.duration   = DD.MainNav.ANIMATION_IN_DURATION;
        DD.MainNav.animations[navName].options.transition = Fx.Transitions.cubicIn;
        DD.MainNav.animations[navName].options.onComplete = function () {
            subNav.setStyle({visibility: 'hidden'});
            DD.MainNav.isAnimating[navName] = false;
        };
        
        DD.MainNav.animations[navName].custom(startBottom, endBottom);
    }
    
};

Event.observe(window, 'load', function () { DD.MainNav.init(); });



DD.StorySlideShow = {

  /**** Constants ****/
  
    /**
     * Delay in milliseconds between the cross-fade animations.
     * @var int
     */
    ANIMATION_DELAY: 9000,
    
    /**
     * Duration in milliseconds of the cross-fade animation.
     * @var int
     */
    ANIMATION_DURATION: 3000,
    


  /**** Class Variables ****/
  
  
    animations:   [],
    currentIndex: 0,



  /**** Initialization ****/


    /**
     * Locates all of the story slide show images on the page and begins
     * the animation.
     */
    init : function ()
    {
        var images = $$('.storySlideshow .photoContainer');
        if (images.length > 0) {
          for (var i = 0; i < images.length; i++) {
            DD.StorySlideShow.animations[i] =
                new Fx.Style(images[i], 'opacity',
                             {duration: DD.StorySlideShow.ANIMATION_DURATION, fps: 30});
          }
          new PeriodicalExecuter(DD.StorySlideShow.showNext, (DD.StorySlideShow.ANIMATION_DELAY / 1000));
        }
    },



  /**** Animation Functions ****/
  
  
    /**
     * Advances the animation to the next image. If at the last image, resets
     * to the first.
     */
    showNext : function ()
    {
        DD.StorySlideShow.animations[DD.StorySlideShow.currentIndex]._start(1, 0);
        if (++DD.StorySlideShow.currentIndex >= DD.StorySlideShow.animations.length) {
          DD.StorySlideShow.currentIndex = 0;
        }
        DD.StorySlideShow.animations[DD.StorySlideShow.currentIndex]._start(0, 1);
    }
    
};
Event.observe(window, 'load', function () { DD.StorySlideShow.init(); });



DD.StoryGrid = {

  /**** Constants ****/
  
    /**
     * Delay in milliseconds between the cross-fade animations.
     * @var int
     */
    ANIMATION_DELAY: 6000,
    
    /**
     * Duration in milliseconds of the cross-fade animation.
     * @var int
     */
    ANIMATION_DURATION: 800,
    
    /**
     * Percentage of squares that should be hidden at any given time.
     * @var int
     */
    HIDDEN_PERCENTAGE: 0,
    


  /**** Class Variables ****/
  
  
    stories:        [],
    visibleStories: [],
    hiddenStories:  [],



  /**** Initialization ****/


    /**
     * Locates all of the story squares on the page and begins the animation.
     * Also adds mouseover and mouseout events to the links for the hover
     * effect.
     */
    init : function ()
    {
        DD.StoryGrid.stories = $$('.storySquare');
        if (DD.StoryGrid.stories.length > 0) {
          for (var i = 0; i < DD.StoryGrid.stories.length; i++) {
              DD.StoryGrid.stories[i].animation =
                  new Fx.Style(DD.StoryGrid.stories[i], 'opacity',
                               {duration: DD.StoryGrid.ANIMATION_DURATION, fps: 30});
              DD.StoryGrid.stories[i].animation.set(1);

              DD.StoryGrid.visibleStories[i] = i;

              Event.observe(DD.StoryGrid.stories[i], 'mouseover',
                            DD.StoryGrid.linkMouseOver.bindAsEventListener(DD.StoryGrid.stories[i]));
              Event.observe(DD.StoryGrid.stories[i], 'mouseout',
                            DD.StoryGrid.linkMouseOut.bindAsEventListener(DD.StoryGrid.stories[i]));
          }
          
          // Randomize the animation order
          DD.StoryGrid.visibleStories.sort(function (a, b) { return (Math.random() > 0.5) ? -1 : 1; })
          
          var numberHidden = Math.round(DD.StoryGrid.stories.length * DD.StoryGrid.HIDDEN_PERCENTAGE / 100);
          for (i = 0; i < numberHidden; i++) {
              var index = DD.StoryGrid.visibleStories.shift();
              DD.StoryGrid.stories[index].animation.set(0);
              DD.StoryGrid.hiddenStories.push(index); 
          }
          new PeriodicalExecuter(DD.StoryGrid.showNext, (DD.StoryGrid.ANIMATION_DELAY / 1000));
        }
    },



  /**** Animation Functions ****/
  
  
    /**
     * Advances the animation to the next image. If at the last image, resets
     * to the first.
     */
    showNext : function ()
    {
        var outIndex = DD.StoryGrid.visibleStories.shift();
        DD.StoryGrid.hiddenStories.push(outIndex);
        DD.StoryGrid.stories[outIndex].animation.clearTimer();
        DD.StoryGrid.stories[outIndex].animation._start(DD.StoryGrid.stories[outIndex].animation.now, 0);
        
        var inIndex = DD.StoryGrid.hiddenStories.shift();
        DD.StoryGrid.visibleStories.push(inIndex);
        DD.StoryGrid.stories[inIndex].animation.clearTimer();
        DD.StoryGrid.stories[inIndex].animation._start(DD.StoryGrid.stories[inIndex].animation.now, 1);
    },
    


  /**** Event Handlers ****/
  

    /**
     * 'onmouseover' handler for the story links. Fades the image out to half
     * opacity.
     */
    linkMouseOver : function ()
    {
        this.animation.clearTimer();
        this.animation._start(this.animation.now, 0.5);
    },

    /**
     * 'onmouseout' handler for the story links. Restores the image to full
     * opacity.
     */
    linkMouseOut : function ()
    {
        this.animation.clearTimer();
        this.animation._start(this.animation.now, 1);
    }

};
Event.observe(window, 'load', function () { DD.StoryGrid.init(); });


DD.SongLyrics = {

  /**** Constants ****/
  
    /**
     * Duration in milliseconds of the cross-fade animation.
     * @var int
     */
    ANIMATION_DURATION: 250,
    


  /**** Initialization ****/


    /**
     * Locates all of the story squares on the page and begins the animation.
     * Also adds mouseover and mouseout events to the links for the hover
     * effect.
     */
    init : function ()
    {
        $$('.lyricLink').each(function (link) {
            Event.observe(link, 'click', DD.SongLyrics.lyricLinkClick.bindAsEventListener(link));
        });
        var descriptionLink = $('albumDescriptionLink');
        if (descriptionLink) {
            Event.observe(descriptionLink, 'click', DD.SongLyrics.descriptionLinkClick);
        }
    },



  /**** Event Handlers ****/
  

    /**
     * 'onclick' handler for the lyric links. Fades the album description and
     * other song lyrics out, if visible, and shows new the lyric.
     */
    lyricLinkClick : function ()
    {
        var lyricName = 'lyric-' + this.id.substring(6);
        var lyric = $(lyricName);
        if (! lyric) {
            return;
        }

        var description = $('albumDescription');
        if (description.getStyle('visibility') === 'visible') {
            var descriptionAnimation =
                new Fx.Style(description, 'opacity', {duration: DD.SongLyrics.ANIMATION_DURATION, fps: 30});
            descriptionAnimation._start(1, 0);

            var descriptionLink = $('albumDescriptionLink');
            var descriptionLinkAnimation =
                new Fx.Style(descriptionLink, 'opacity', {duration: DD.SongLyrics.ANIMATION_DURATION, fps: 30});
            descriptionLinkAnimation._start(0, 1);
        }
        
        DD.SongLyrics._hideAllLyricsExcept(lyricName);
        
        if ((lyric.getStyle('opacity') == 1) && (lyric.getStyle('display') == 'block')) {
            return;
        }
        
        lyric.setStyle({visibility: 'hidden', display: 'block'});
        var lyricAnimation =
            new Fx.Style(lyric, 'opacity', {duration: DD.SongLyrics.ANIMATION_DURATION, fps: 30});
        lyricAnimation._start(0, 1);        
    },

    /**
     * 'onclick' handler for the show description link. Fades the song lyrics
     * out, if visible, and shows the album description.
     */
    descriptionLinkClick : function ()
    {
        var description = $('albumDescription');
        if (description.getStyle('visibility') === 'visible') {
            return;
        }
        
        DD.SongLyrics._hideAllLyricsExcept('');
        
        var descriptionAnimation =
            new Fx.Style(description, 'opacity', {duration: DD.SongLyrics.ANIMATION_DURATION, fps: 30});
        descriptionAnimation._start(0, 1);

        var descriptionLink = $('albumDescriptionLink');
        var descriptionLinkAnimation =
            new Fx.Style(descriptionLink, 'opacity', {duration: DD.SongLyrics.ANIMATION_DURATION, fps: 30});
        descriptionLinkAnimation._start(1, 0);
    },
    

  /**** Internal Functions ****/
  
    /**
     * Fades any visible lyrics out except for the one specified.
     *
     * @param String except
     */
    _hideAllLyricsExcept : function (except)
    {
        $$('.lyric').each(function (lyric) {
            if ((lyric.getStyle('visibility') !== 'hidden') &&
                (lyric.id !== except)) {
                var lyricAnimation =
                    new Fx.Style(lyric, 'opacity', {duration: DD.SongLyrics.ANIMATION_DURATION, fps: 30,
                        onComplete: function () {
                            lyric.setStyle({display: 'none'});
                        }
                        });
                lyricAnimation._start(1, 0);
            }
        });        
    }

};
Event.observe(window, 'load', function () { DD.SongLyrics.init(); });
