/**
 * californiabicyclemuseum.org site JavaScript.
 *
 * @package    CaliforniaBicycleMuseum
 * @subpackage UI
 * @copyright  Copyright 2009 Spenlen Media, Inc. (http://spenlen.com)
 * @license    This source code file is licensed for the exclusive internal use of
 *             California Bicycle Museum and may not be used for any other purpose.
 * @version    $Id$
 */

CBM.Gallery = {};


CBM.Gallery.Timeline = Class.create();
CBM.Gallery.Timeline.prototype = {

  /**** Instance Variables ****/

    /**
     * Name of the timeline managed by this object.
     * @var String
     */
    timelineName : null,

    /**
     * Total pixel width of the entire timeline.
     * @var int
     */
    timelineWidth : 0,

    /**
     * Width of the visible timeline window.
     * @var int
     */
    timelineVisibleWidth : 0,

    /**
     * Minimum left offset.
     * @var int
     */
    minLeft : 0,

    /**
     * Maximum left offset.
     * @var int
     */
    maxLeft : 15,

    /**
     * Current left offset of the timeline.
     * @var int
     */
    currentLeft : 15,

    /**
     */
    moreLeftLink : null,

    /**
     */
    moreRightLink : null,




  /**** Initialization ****/

    /**
     * Establishes the effective width of the timeline and installs the various
     * event handlers responsible for maintaining the interactive navigation
     * elements.
     *
     * @var String  timelineName
     */
    initialize : function (timelineName)
    {
        if (! $(timelineName + 'Timeline')) {
            alert('Cannot find timeline element: ' + timelineName);
            return;
        }
        this.timelineName = timelineName;

        var timelineLinks = $(timelineName + 'Links');
        var lastLink = timelineLinks.childElements().last();
        if (! lastLink) {
            return;
        }
        var offset = Position.positionedOffset(lastLink);
        this.timelineWidth = offset[0] + lastLink.getWidth() + this.maxLeft;
        timelineLinks.setStyle({width: this.timelineWidth + 'px'});

        this.recalculateExtents();

        var currentLink = timelineLinks.getElementsBySelector('LI.current');
        if (currentLink.length > 0) {
            offset = Position.positionedOffset(currentLink.first());
            this.currentLeft -= offset[0] - 100;
            if (this.currentLeft > this.maxLeft) {
                this.currentLeft = this.maxLeft;
            } else if (this.currentLeft < this.minLeft) {
                this.currentLeft = this.minLeft;
            }
            timelineLinks.setStyle({left: this.currentLeft + 'px'});
        }

        this.moreLeftLink  = $(timelineName + 'MoreLeft');
        this.moreRightLink = $(timelineName + 'MoreRight');
        if (! Prototype.Browser.IE) {
            /* IE can't deal properly with setting transparency on a PNG image
             * that is already transparent.
             */
            this.moreLeftLink.setStyle({opacity: 0});
            this.moreRightLink.setStyle({opacity: 0});
        }
        this.hideOrShowMoreLinks();

        Event.observe(this.moreLeftLink,  'click',  this.moreLeftOnClick.bind(this));
        Event.observe(this.moreRightLink, 'click',  this.moreRightOnClick.bind(this));
        Event.observe(window,             'resize', this.windowOnResize.bind(this));
    },



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

    /**
     * onclick handler for the more left link. Shifts the visible timeline
     * to the right one page at a time.
     *
     * @param Event event
     */
    moreLeftOnClick : function (event)
    {
        if (this.currentLeft >= this.maxLeft) {
            return;
        }
        this.currentLeft += Math.floor(this.timelineVisibleWidth * 0.8);
        this.animate();
    },

    /**
     * onclick handler for the more right link. Shifts the visible timeline
     * to the left one page at a time.
     *
     * @param Event event
     */
    moreRightOnClick : function (event)
    {
        if (this.currentLeft <= this.minLeft) {
            return;
        }
        this.currentLeft -= Math.floor(this.timelineVisibleWidth * 0.8);
        this.animate();
    },

    /**
     * onresizehandler for the window. Recalculates timeline extents and hides
     * or shows the more left/right links as needed.
     *
     * @param Event event
     */
    windowOnResize : function (event)
    {
        this.recalculateExtents();
        this.hideOrShowMoreLinks();
    },



  /**** Animation ****/

    /**
     * Recalculates the minimum and maximum visible extents of the timeline.
     * Keeps the timeline anchored at the right edge of the window if necessary.
     */
    recalculateExtents : function ()
    {
        this.timelineVisibleWidth = $(this.timelineName + 'Timeline').getWidth();

        var newMinLeft = -(this.timelineWidth - this.timelineVisibleWidth);
        if (newMinLeft > 15) {
            newMinLeft = 15;
        }
        if (this.currentLeft <= this.minLeft) {
            var delta = this.minLeft - newMinLeft;
            if (delta < 0) {
                this.currentLeft -= delta;
                if (this.currentLeft > this.maxLeft) {
                    this.currentLeft = this.maxLeft;
                }
                $(this.timelineName + 'Links').setStyle({left: this.currentLeft + 'px'});
            }
        }
        this.minLeft = newMinLeft;
    },

    /**
     * Animates the timeline to the new location. Enforces the maximum extents
     * of the timeline view.
     */
    animate : function ()
    {
        if (this.currentLeft > this.maxLeft) {
            this.currentLeft = this.maxLeft;
        } else if (this.currentLeft < this.minLeft) {
            this.currentLeft = this.minLeft;
        }
        new Effect.Morph(this.timelineName + 'Links',
            {style: 'left: ' + this.currentLeft + 'px', duration: 0.5});
        this.hideOrShowMoreLinks();
    },

    /**
     * Hides or shows the more left/right links as needed.
     */
    hideOrShowMoreLinks : function ()
    {
        if (this.currentLeft >= this.maxLeft) {
            if (this.moreLeftLink.visible()) {
                if (Prototype.Browser.IE) {
                    this.moreLeftLink.hide();
                } else {
                    new Effect.Opacity(this.moreLeftLink,
                        {to: 0, duration: 0.5,
                         afterFinish: function () { this.moreLeftLink.hide(); }.bind(this)}
                        );
                }
            }
        } else {
            if (! this.moreLeftLink.visible()) {
                this.moreLeftLink.show();
                if (! Prototype.Browser.IE) {
                    new Effect.Opacity(this.moreLeftLink, {to: 1, duration: 0.5});
                }
            }
        }
        if (this.currentLeft <= this.minLeft) {
            if (this.moreRightLink.visible()) {
                if (Prototype.Browser.IE) {
                    this.moreRightLink.hide();
                } else {
                    new Effect.Opacity(this.moreRightLink,
                        {to: 0, duration: 0.5,
                         afterFinish: function () { this.moreRightLink.hide(); }.bind(this)}
                        );
                }
            }
        } else {
            if (! this.moreRightLink.visible()) {
                this.moreRightLink.show();
                if (! Prototype.Browser.IE) {
                    new Effect.Opacity(this.moreRightLink, {to: 1, duration: 0.5});
                }
            }
        }
    }

};
Event.onDOMReady(function () { new CBM.Gallery.Timeline('galleryEra'); });
Event.onDOMReady(function () { new CBM.Gallery.Timeline('galleryExhibit'); });



CBM.Gallery.Exhibit = Class.create();
CBM.Gallery.Exhibit.prototype = {

  /**** Initialization ****/

    /**
     * Installs the event handlers that manage the exhibit detail panes.
     */
    initialize : function (timelineName)
    {
        Event.observe('galleryEraName',         'click', this.eraDescriptionOnClick.bind(this));
        Event.observe('galleryExhibitName',     'click', this.exhibitDescriptionOnClick.bind(this));
        Event.observe('galleryExhibitDonation', 'click', this.donationStoryOnClick.bind(this));

        var photoContainer = $('galleryPhotos');
        if (photoContainer) {
            photoContainer.setStyle({opacity: 0});
            var photos = photoContainer.childElements();
            for (var i = 0; i < photos.length; i++) {
                if (i == 0) {
                    photoContainer.setStyle({width:  photos[i].getWidth()  + 'px',
                                             height: photos[i].getHeight() + 'px'});
                } else {
                    photos[i].setStyle({opacity: 0});
                }
            }
            new Effect.Opacity(photoContainer, {from: 0, to: 1, duration: 0.6, delay: 0.2});
        }
        
        var thumbnailContainer = $('galleryThumbnails');
        if (thumbnailContainer) {
            var thumbs = thumbnailContainer.childElements();
            if (thumbs.length < 2) {
                thumbnailContainer.hide();
            } else {
                thumbnailContainer.setStyle({opacity: 0});
                var newWidth = (450 / thumbs.length) - 5;
                if (newWidth > 220) {
                    newWidth = 220;
                }
                for (var i = 0; i < thumbs.length; i++) {
                    thumbs[i].setStyle({width: 0});
                    new Effect.Morph(thumbs[i], {style: 'width: ' + newWidth + 'px', duration: 0.6, delay: 0.2});
                    Event.observe(thumbs[i], 'click', this.thumbnailOnClick.bind(this, thumbs[i]));
                }
                new Effect.Opacity(thumbnailContainer, {from: 0, to: 1, duration: 0.8, delay: 0.2});
            }
        }
    },



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

    /**
     * onclick handler for the era description title. Opens the era
     * description panel and closes the others.
     *
     * @param Event event
     */
    eraDescriptionOnClick : function (event)
    {
        if ($('galleryEraDescription').visible()) {
            return;
        }
        this.preserveDescriptionHeight();
        CBM.Effects.showElement('galleryEraDescription', {afterFinish: this.restoreDescriptionHeight});
        if ($('galleryExhibitDescription').visible()) {
            CBM.Effects.hideElement('galleryExhibitDescription');
        }
        if ($('galleryExhibitDonationStory').visible()) {
            CBM.Effects.hideElement('galleryExhibitDonationStory');
        }
    },

    /**
     * onclick handler for the exhibit description title. Opens the exhibit
     * description panel and closes the others.
     *
     * @param Event event
     */
    exhibitDescriptionOnClick : function (event)
    {
        if ($('galleryExhibitDescription').visible()) {
            return;
        }
        this.preserveDescriptionHeight();
        if ($('galleryEraDescription').visible()) {
            CBM.Effects.hideElement('galleryEraDescription');
        }
        CBM.Effects.showElement('galleryExhibitDescription', {afterFinish: this.restoreDescriptionHeight});
        if ($('galleryExhibitDonationStory').visible()) {
            CBM.Effects.hideElement('galleryExhibitDonationStory');
        }
    },

    /**
     * onclick handler for the donation story title. Opens the donation story
     * panel and closes the others.
     *
     * @param Event event
     */
    donationStoryOnClick : function (event)
    {
        if ($('galleryExhibitDonationStory').visible()) {
            return;
        }
        this.preserveDescriptionHeight();
        if ($('galleryEraDescription').visible()) {
            CBM.Effects.hideElement('galleryEraDescription');
        }
        if ($('galleryExhibitDescription').visible()) {
            CBM.Effects.hideElement('galleryExhibitDescription');
        }
        CBM.Effects.showElement('galleryExhibitDonationStory', {afterFinish: this.restoreDescriptionHeight});
    },
    
    /**
     * onclick handler for the image thumbnails. Animates the appearance of
     * the new exhibit image.
     *
     * @param Element thumbnail
     * @param Event   event
     */
    thumbnailOnClick : function (thumbnail, event)
    {
        var photo = $('galleryPhoto-' + thumbnail.id.slice(17));
        if (! photo) {
            return;
        }
        if (photo.getStyle('opacity') > 0) {
            return;
        }

        effects = [
            new Effect.Opacity(photo, {sync: true, from: 0, to: 1}),
            new Effect.Morph('galleryPhotos',
                {sync: true, style: 'width: ' + photo.getWidth() + 'px; height: ' + photo.getHeight() + 'px'})
        ]
        photo.siblings().each(function (other) {
            if (other.getStyle('opacity') > 0) {
                effects[effects.length] = new Effect.Opacity(other, {sync: true, from: 1, to: 0});
            }
        });
        
        new Effect.Parallel(effects, {duration: 0.7});
    },

    /**
     * Sets a fixed height for the description block for the duration of the
     * animations. Used to mask artifacts caused by changing heights and margins.
     */
    preserveDescriptionHeight : function ()
    {
        $('galleryDescription').setStyle({height: $('galleryDescription').getHeight() + 'px'});
    },
    
    /* Restores the variable height of the description block after the
     * animations have completed.
     */
    restoreDescriptionHeight : function ()
    {
        $('galleryDescription').setStyle({height: 'auto'});
    }

};
Event.onDOMReady(function () { new CBM.Gallery.Exhibit(); });
