/*
    ezImageGallery v1.0 - JavaScript Image Gallery Controller
      - Copyright © 2008 Robert J. Secord, B.Sc.
      - Script Author
            - Robert J. Secord, B.Sc. <Robert.Secord@edgeip.com>
      - Dependancies:
            - Mootools v1.2 or later.  <www.mootools.net>
      - Tested Browsers:
            - Windows: IE 6+, Firefox 2+, Opera 9+, Netscape 8+
      - Todo:
*/

// -----------------------------------------------------------------------------------------------------------------
// ezImageGalleryThemes Definition
var ezImageGalleryThemes =
{
    // Common Theme
    Common:
    {
        ImageElement:
        {
            Size:         {'width': '100px', 'height': '100px'},
            ImageUrl:     [],
            ImageCaption: []
        },
        ImageGallery:
        {
            CssClass:
            {
                Container:    'ezImageGallery-Container',
                ImageBlock:   'ezImageGallery-ImageBlock',
                ImageElement: 'ezImageGallery-Image',
                ImageCaption: 'ezImageGallery-Caption',
                ClearingDiv:  'ezImageGallery-Clear'
            }
        },
        Navigation:
        {
            CssClass:
            {
                Container: 'ezImageGallery-Navigation',
                Buttons:   'ezImageGallery-Navigation-Button',
                Current:   'ezImageGallery-Navigation-Button-Current',
                First:     'ezImageGallery-Navigation-Button-First',
                Previous:  'ezImageGallery-Navigation-Button-Previous',
                Next:      'ezImageGallery-Navigation-Button-Next',
                Last:      'ezImageGallery-Navigation-Button-Last',
                Page:      'ezImageGallery-Navigation-Button-Page'
            },
            Text:
            {
                First:    '&lt;&lt;',   // <<
                Previous: '&lt;',       //  <
                Next:     '&gt;',       //  >
                Last:     '&gt;&gt;',   // >>
                Page:     'Page'
            }
        },
        Preloader:
        {
            ProgressLabel: 'Loading Images: ',
            OverlayOpacity: 0.3,
            CssClass:
            {
                Overlay:       'ezImageGallery-Preload-Overlay',
                Container:     'ezImageGallery-Preload-Progress',
                ProgressBar:   'ezImageGallery-Preload-InnerBar',
                ProgressLabel: 'ezImageGallery-Preload-InnerText'
            }
        }
    }
};

// -----------------------------------------------------------------------------------------------------------------
// ezImageGallery Class Definition
var ezImageGallery = new Class(
{
    Implements: [new Options, new Events],

    // -------------------------------------------------------------------------------------------------------------
    // Constructor Options (Set Externally through Object Constructor in JSON Format)
    options:
    {
        // Optional:
        Theme:    ezImageGalleryThemes.Common,                    // The Script Theme to use
        Cookies:  {Duration: 14, Name: 'ezImageGalleryCookie'},   // Cookie Name & Expiration Time in Days
        InitPage: {CurrentPage: 1, PerPage: 9},                   // Page Navigation Control

        // Events:
        onImageClick: $empty, // Called on Image Click

        // Developer Use:
        DebugMode: false    // Alerts on All Client-Side Developer Errors and Warnings
    },

    // -------------------------------------------------------------------------------------------------------------
    // Class Constructor
    initialize: function( options )
    {
        this.setOptions( options );

        // Merge Common Theme with Custom Theme
        this.options.Theme = $merge( ezImageGalleryThemes.Common, this.options.Theme );

        // Attach Window Events for DOM Ready / Page Loaded
        window.addEvents(
        {
            'load':     this.OnLoad.bind(this),
            'domready': this.OnDomReady.bind(this)
        });
    },

    // -------------------------------------------------------------------------------------------------------------
    // Hooked Events
    // ~~~~~~~~~~~~~

    // -------------------------------------------------------------------------------------------------------------
    // Event: DOM Ready
    OnDomReady: function()
    {
    },

    // -------------------------------------------------------------------------------------------------------------
    // Event: Page Loaded
    OnLoad: function()
    {
        // ImageGallery Cookies (Contained in a Hash-Object)
        this.oCookies = new Hash.Cookie(this.options.Cookies.Name, {duration: this.options.Cookies.Duration});

        // Insert Required Structural CSS
        this.InsertCSS();

        // Initialize Header Images
        this.InitializeImageGallery();

        // Preload all Header Images
        this.CreatePreloader();
        this.PreloadImages.delay( 1, this );
    },

    // -------------------------------------------------------------------------------------------------------------
    // Initialization Routines
    // ~~~~~~~~~~~~~~~~~~~~~~~

    // -------------------------------------------------------------------------------------------------------------
    // Internal Routine: Initializes Header Image Controls
    InitializeImageGallery: function()
    {
        // Ensure Valid Image List
        if( !this.options.Theme.ImageElement.ImageUrl.length )
            return this.ErrorAlert('Image Gallery Initialization Failed - No Image URLs Supplied for Selected Theme!');

        try
        {
            // Get Image Gallery Element on Page
            this.oGallery = document.getElement('.' + this.options.Theme.ImageGallery.CssClass.Container);
            if( !this.oGallery ) return; // No Image Gallery to Initialize

            // Ensure Image Gallery Cookie is Set
            if( this.oCookies.get('Preloaded') != 'True' )
                this.oCookies.set('Preloaded', 'False')
                             .set('CurrentPage', this.options.InitPage.CurrentPage)
                             .set('PerPage', this.options.InitPage.PerPage);

            // Get Current Page Settings
            this.iImageCount = this.options.Theme.ImageElement.ImageUrl.length;
            this.iCurrentPage = this.oCookies.get('CurrentPage').toInt();
            this.iPerPage = this.oCookies.get('PerPage').toInt();
            this.iMaxPage = (this.iImageCount / this.iPerPage).toInt().limit(1, this.iImageCount);

            // Ensure Valid Max Page
            if( this.iImageCount % this.iPerPage ) this.iMaxPage++;

            // Get Image Block Width
            this.iImageBlockWidth = this.options.Theme.ImageElement.Size.width.toInt();
            if( !window.ie6 ) this.iImageBlockWidth += 2; // Add 2 for Borders

            // Update Image Gallery Display
            this.UpdateGallery();
        }
        catch( szError )
        {
            return this.ErrorAlert('Image Gallery Initialization Failed:\n' + szError);
        }
    },

    // -------------------------------------------------------------------------------------------------------------
    // Internal Routine:
    UpdateGallery: function()
    {
        // Ensure Valid Current Page
        this.iCurrentPage = this.iCurrentPage.limit(1, this.iMaxPage);

        // Ensure Valid Start/Last Images of Current Page
        this.iCurrentImage = ((this.iCurrentPage - 1) * this.iPerPage);
        this.iLastImage = (this.iCurrentImage + this.iPerPage).limit(1, this.iImageCount);

        // Empty Image Gallery Element
        this.oGallery.empty();

        // Build Next/Previous Buttons (if needed)
        if( this.iMaxPage > 1 )
        {
            // Ensure Valid Start/End Paging Navigation
            var iPagingStart = (this.iCurrentPage - 4).limit(1, this.iMaxPage);
            var iPagingEnd = (this.iCurrentPage + 4).limit(1, this.iMaxPage);

            // Build Next/Previous Buttons Container
            var oButtonContainer = new Element('div', {'class': this.options.Theme.Navigation.CssClass.Container}).inject( this.oGallery );

            // Build First-Page Button (if required)
            if( this.iCurrentPage > 2 )
                oButtonContainer.adopt(new Element('div',
                {
                    'class': this.options.Theme.Navigation.CssClass.Buttons + ' ' + this.options.Theme.Navigation.CssClass.First,
                    'title': this.options.Theme.Navigation.Text.First,
                    'events': {'click': this.ChangePage.pass( 'first', this )}
                }).set('html', this.options.Theme.Navigation.Text.First));

            // Build Previous-Page Button (if required)
            if( this.iCurrentPage > 1 )
                oButtonContainer.adopt(new Element('div',
                {
                    'class': this.options.Theme.Navigation.CssClass.Buttons + ' ' + this.options.Theme.Navigation.CssClass.Previous,
                    'title': this.options.Theme.Navigation.Text.Previous,
                    'events': {'click': this.ChangePage.pass( 'previous', this )}
                }).set('html', this.options.Theme.Navigation.Text.Previous));

            // Display Paging Markers
            for( var iPage = iPagingStart; iPage <= iPagingEnd; iPage++ )
            {
                // Check for Current Page Marker
                var szClass = this.options.Theme.Navigation.CssClass.Buttons + ' ' + this.options.Theme.Navigation.CssClass.Page;
                if( this.iCurrentPage == iPage ) szClass += ' ' + this.options.Theme.Navigation.CssClass.Current;

                // Insert Page Marker
                oButtonContainer.adopt(new Element('div',
                {
                    'class': szClass,
                    'title': this.options.Theme.Navigation.Text.Page + ' ' + iPage,
                    'events': {'click': this.ChangePage.pass( iPage, this )}
                }).set('html', '' + iPage));
            }

            // Build Next-Page Button (if required)
            if( this.iCurrentPage < this.iMaxPage )
                oButtonContainer.adopt(new Element('div',
                {
                    'class': this.options.Theme.Navigation.CssClass.Buttons + ' ' + this.options.Theme.Navigation.CssClass.Next,
                    'title': this.options.Theme.Navigation.Text.Next,
                    'events': {'click': this.ChangePage.pass( 'next', this )}
                }).set('html', this.options.Theme.Navigation.Text.Next));

            // Build Last-Page Button (if required)
            if( this.iCurrentPage < this.iMaxPage - 1 )
                oButtonContainer.adopt(new Element('div',
                {
                    'class': this.options.Theme.Navigation.CssClass.Buttons + ' ' + this.options.Theme.Navigation.CssClass.Last,
                    'title': this.options.Theme.Navigation.Text.Last,
                    'events': {'click': this.ChangePage.pass( 'last', this )}
                }).set('html', this.options.Theme.Navigation.Text.Last));

            // Add a Clearing Div
            this.oGallery.adopt(new Element('div', {'class': this.options.Theme.ImageGallery.CssClass.ClearingDiv}));
        } // if( this.iMaxPage > 1 )


        // Loop through Images within Current Page
        for( var iImageIdx = this.iCurrentImage; iImageIdx < this.iLastImage; iImageIdx++ )
        {
            // Create Image Block Element
            var oImageBlock = new Element('div',
            {
                'id': this.GetImageID( iImageIdx + 1 ),
                'class': this.options.Theme.ImageGallery.CssClass.ImageBlock,
                'styles': {'width': this.iImageBlockWidth},
                'events':
                {
                    'click': function( oEvent )
                    {
                        var szTargetID = '', oTarget = oEvent.target;
                        while( !oTarget.hasClass(this.options.Theme.ImageGallery.CssClass.ImageBlock) ) oTarget = oTarget.getParent();
                        szTargetID = oTarget.get('id');
                        this.fireEvent('imageClick', [szTargetID, oEvent, this], 1);
                    }.bindWithEvent(this)
                }
            }).inject( this.oGallery );

            // Create Image Element
            var oImage = new Element('div',
            {
                'class': this.options.Theme.ImageGallery.CssClass.ImageElement,
                'styles': $merge(this.options.Theme.ImageElement.Size,
                {
                    'background': 'transparent url(' + this.options.Theme.ImageElement.ImageUrl[iImageIdx] + ') no-repeat top left'
                })
            }).inject( oImageBlock );

            // Create Image Caption Element
            var oImageCaption = new Element('div',
            {
                'class': this.options.Theme.ImageGallery.CssClass.ImageCaption
            }).set('text', this.options.Theme.ImageElement.ImageCaption[iImageIdx]).inject( oImageBlock );
        } // for()

        // Add a Clearing Div
        this.oGallery.adopt(new Element('div', {'class': this.options.Theme.ImageGallery.CssClass.ClearingDiv}));
    },

    // -------------------------------------------------------------------------------------------------------------
    // Internal Routine:
    ChangePage: function( xPageHint )  // PageHint: (mixed) Page direction or index
    {
        // Get Current Page Settings
        this.iCurrentPage = this.oCookies.get('CurrentPage').toInt();
        switch( xPageHint )
        {
            case 'first':    this.iCurrentPage = 1;                                 break;
            case 'previous': (--this.iCurrentPage).limit(1, this.iMaxPage);         break;
            case 'next':     (++this.iCurrentPage).limit(1, this.iMaxPage);         break;
            case 'last':     this.iCurrentPage = this.iMaxPage;                     break;
            default:         this.iCurrentPage = xPageHint.limit(1, this.iMaxPage); break;
        }
        this.oCookies.set('CurrentPage', this.iCurrentPage);

        // Update Gallery for New Page
        this.UpdateGallery();
    },

    // -------------------------------------------------------------------------------------------------------------
    // Preloader Routines
    // ~~~~~~~~~~~~~~~~~~

    // -------------------------------------------------------------------------------------------------------------
    // Internal Routine: Creates Preloader Display
    CreatePreloader: function()
    {
        if( this.oCookies.get('Preloaded') == 'True' ) return;

        // Create Preload Overlay
        this.oPreloadOverlay = new Element('div',
        {
            'class': this.options.Theme.Preloader.CssClass.Overlay,
            'styles':
            {
                'width':    this.oGallery.getWidth(),
                'height':   this.oGallery.getHeight(),
                'opacity':  this.options.Theme.Preloader.OverlayOpacity
            }
        }).inject(this.oGallery, 'top');

        // Create Image Preloader Progress Bar Container
        this.oPreloadProgress = new Element('div', {'class': this.options.Theme.Preloader.CssClass.Container}).inject(this.oPreloadOverlay, 'after');

        // Create Image Preloader Progress Bar Elements
        this.oPreloadProgressLabel = new Element('div', {'class': this.options.Theme.Preloader.CssClass.ProgressLabel});
        this.oPreloadProgressBar = new Element('div', {'class': this.options.Theme.Preloader.CssClass.ProgressBar});
        this.oPreloadProgress.adopt([this.oPreloadProgressBar, this.oPreloadProgressLabel]);
    },

    // -------------------------------------------------------------------------------------------------------------
    // Internal Routine: Preloads All Header Images
    PreloadImages: function()
    {
        if( this.oCookies.get('Preloaded') == 'True' ) return;

        new Asset.images(this.options.Theme.ImageElement.ImageUrl,
        {
            onProgress: function( iImageIndex )
            {
                var iWidth = ((iImageIndex + 1) * (this.oPreloadProgress.getStyle('width').toInt() / this.options.Theme.ImageElement.ImageUrl.length));
                this.oPreloadProgressBar.setStyle('width', iWidth);
                this.oPreloadProgressLabel.set('html', this.options.Theme.Preloader.ProgressLabel + (iImageIndex + 1) + '/' + this.options.Theme.ImageElement.ImageUrl.length);
            }.bind(this),
            onComplete: function()
            {
                this.oCookies.set('Preloaded', 'True');
                (function(){ this.oPreloadProgress.dispose(); this.oPreloadOverlay.dispose(); }).delay( 500, this );
            }.bind(this)
        });
    },

    // -------------------------------------------------------------------------------------------------------------
    // Insert Required Elements
    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    // -------------------------------------------------------------------------------------------------------------
    // Insert Display Styles
    InsertCSS: function()
    {
        if( $(this.ClassName + 'RequiredCSS') ) return;

        // Get Windows CSS
        var crlf = !this.options.DebugMode ? '' : '\n';
        var tab  = !this.options.DebugMode ? '' : '    ';
        var szCSS = ''
        + crlf + '.' + this.options.Theme.ImageGallery.CssClass.Container
        + crlf + '{'
        + crlf + tab + 'position: relative;'
        + crlf + tab + 'padding: 5px;'
        + crlf + tab + 'border: 1px solid #DDD;'
        + crlf + '}'
        + crlf + '.' + this.options.Theme.ImageGallery.CssClass.ImageBlock
        + crlf + '{'
        + crlf + tab + 'float: left;'
        + crlf + tab + 'margin: 0 2px 10px 2px;'
        + crlf + tab + 'border: 1px solid #EFEFEF;'
        + crlf + tab + 'cursor: pointer;'
        + crlf + '}'
        + crlf + '.' + this.options.Theme.ImageGallery.CssClass.ImageElement
        + crlf + '{'
        + crlf + tab + 'border: 1px solid #DDD;'
        + crlf + '}'
        + crlf + '.' + this.options.Theme.ImageGallery.CssClass.ImageCaption
        + crlf + '{'
        + crlf + tab + 'color: #AAA;'
        + crlf + tab + 'font-size: 0.9em;'
        + crlf + tab + 'padding: 1px 3px;'
        + crlf + '}'
        + crlf + '.' + this.options.Theme.ImageGallery.CssClass.ClearingDiv
        + crlf + '{'
        + crlf + tab + 'clear: both;'
        + crlf + '}'
        + crlf + '.' + this.options.Theme.Navigation.CssClass.Container
        + crlf + '{'
        + crlf + tab + 'float: right;'
        + crlf + tab + 'margin: 0 2px 5px 0;'
        + crlf + tab + 'border: 1px solid #DDD;'
        + crlf + '}'
        + crlf + '.' + this.options.Theme.Navigation.CssClass.Buttons
        + crlf + '{'
        + crlf + tab + 'float: left;'
        + crlf + tab + 'margin: 2px;'
        + crlf + tab + 'padding: 0 5px;'
        + crlf + tab + 'color: #AAA;'
        + crlf + tab + 'font-size: 0.9em;'
        + crlf + tab + 'border: 1px solid #EEE;'
        + crlf + tab + 'cursor: pointer;'
        + crlf + '}'
        + crlf + '.' + this.options.Theme.Navigation.CssClass.Buttons + ':hover'
        + crlf + '{'
        + crlf + tab + 'background: #EEE;'
        + crlf + '}'
        + crlf + '.' + this.options.Theme.Navigation.CssClass.Current
        + crlf + '{'
        + crlf + tab + 'font-weight: bold;'
        + crlf + tab + 'background: #EEE;'
        + crlf + '}'
        + crlf + '.' + this.options.Theme.Navigation.CssClass.First
        + crlf + '{'
        + crlf + '}'
        + crlf + '.' + this.options.Theme.Navigation.CssClass.Previous
        + crlf + '{'
        + crlf + '}'
        + crlf + '.' + this.options.Theme.Navigation.CssClass.Next
        + crlf + '{'
        + crlf + '}'
        + crlf + '.' + this.options.Theme.Navigation.CssClass.Last
        + crlf + '{'
        + crlf + '}'
        + crlf + '.' + this.options.Theme.Preloader.CssClass.Overlay
        + crlf + '{'
        + crlf + tab + 'position: absolute;'
        + crlf + tab + 'top: 0; left: 0;'
        + crlf + tab + 'z-index: 3343;'
        + crlf + tab + 'background: #000;'
        + crlf + '}'
        + crlf + '.' + this.options.Theme.Preloader.CssClass.Container
        + crlf + '{'
        + crlf + tab + 'overflow: hidden;'
        + crlf + tab + 'position: absolute;'
        + crlf + tab + 'top: 20px;'
        + crlf + tab + 'right: 20px;'
        + crlf + tab + 'z-index: 3344;'
        + crlf + tab + 'width: 120px;'
        + crlf + tab + 'height: 15px;'
        + crlf + tab + 'background: #FFF;'
        + crlf + tab + 'border: 1px solid #333;'
        + crlf + '}'
        + crlf + '.' + this.options.Theme.Preloader.CssClass.ProgressBar
        + crlf + '{'
        + crlf + tab + 'position: absolute;'
        + crlf + tab + 'top: 0; left: 0;'
        + crlf + tab + 'width: 0;'
        + crlf + tab + 'height: 15px;'
        + crlf + tab + 'background: #696;'
        + crlf + '}'
        + crlf + '.' + this.options.Theme.Preloader.CssClass.ProgressLabel
        + crlf + '{'
        + crlf + tab + 'position: absolute;'
        + crlf + tab + 'top: 0; left: 0;'
        + crlf + tab + 'width: 116px;'
        + crlf + tab + 'height: 11px;'
        + crlf + tab + 'margin: 2px;'
        + crlf + tab + 'color: #EEE;'
        + crlf + tab + 'text-align: center;'
        + crlf + tab + 'font: bold 9px/9px Verdana;'
        + crlf + tab + 'background: #333;'
        + crlf + '}';

        try
        {
            // Create Style Element
            var oCSS = new Element('style', {'type': 'text/css', 'media': 'all', 'id': this.ClassName + 'RequiredCSS'}).inject( document.head, 'top' );

            // Append CSS Text to Style Element
            if( Browser.Engine.trident ) document.styleSheets[document.styleSheets.length-1].cssText = szCSS;
            else oCSS.appendText( szCSS );

            // Insert Succesful
            return true;
        }
        catch( szError )
        {
            // Insert Failed
            return this.ErrorAlert('InsertCSS() Failed. Reason: ' + szError);
        }
    },

    // -------------------------------------------------------------------------------------------------------------
    // Common Routines
    // ~~~~~~~~~~~~~~~

    // -------------------------------------------------------------------------------------------------------------
    // Internal Routine:
    GetImageID: function( iID )
    {
        var szID = '' + iID, szImageID = 'P999';
        for( var i = 0; i < 5 - szID.length; i++ ) szImageID += '0';
        szImageID += szID;
        return szImageID;
    },

    // -------------------------------------------------------------------------------------------------------------
    // Internal Routine: Debug-Mode Error Alerts
    ErrorAlert: function( szErrorMsg )
    {
        // Debug Alert Message
        if( this.options.DebugMode )
        {
            szErrorMsg = '[' + this.ClassName + ' ' + this.ClassVersion + '] -- DEV-ERROR --\n\n' + szErrorMsg;
            if( window.console ) console.log( szErrorMsg );
            else alert( szErrorMsg );
        }
        return false;
    },

    // -------------------------------------------------------------------------------------------------------------
    // Class Name, Version, Author
    ClassName:    'ezImageGallery',
    ClassVersion: '1.0',
    ClassAuthor:  'Robert J. Secord, B.Sc.'
});
