/*!
 * jquery.bannr. jQuery rotating banner plugin
 * 
 * Copyright (c) 2009 Craig Thompson
 * http://craigsworks.com
 *
 * Licensed under MIT
 * http://www.opensource.org/licenses/mit-license.php
 *
 * Launch  : April 2009
 * Version : 1.0.0-beta1
 * Released: Unreleased
 */
(function($)
{
   // Implementation
   $.fn.bannr = function(options) 
   { 
      var obj, config, command;
      
      // Set null options object if no options are provided
      if(!options) options = {};
      
      // Check for API call
      else if(options === 'api') return $(this).eq(0).data('bannr');
      
      // Iterate each matched element
      return $(this).each(function() // Return original elements as per jQuery guidelines
      {
         // Check for API commands
         if(typeof options == 'string')
         {
            command = options.toLowerCase();
            
            // Execute command 
            if(command == 'destroy') $(this).data('bannr').destroy();
            else if(command == 'scroll') $(this).data('bannr').scroll();
            else if(command == 'disable') $(this).data('bannr').disable(true);
            else if(command == 'enable') $(this).data('bannr').disable(false);
         }
         
         // No API commands
         else
         {
            // Create unique configuration object
            config = $.extend(true, {}, $.fn.bannr.defaults, options);
            
            // Instantiate the tooltip and add API data
            obj = new bannr($(this), config);
            $(this).data('bannr', obj);
         };
      });
   };
   
   // Instantiator
   function bannr(master, options)
   {
      // Declare this reference
      var self = this;
      
      // Setup class attributes
      self.options = options;
      self.elements = {
         master: master.addClass('bannr '+self.options.classes.master),
         container: null,
         loading: null,
         title: null,
         nav: null,
         tooltip: null
      };
      self.status = {
         current: 0,
         disabled : false,
         hovered: false
      };
      self.timers = {};
      
      // Define exposed API methods
      $.extend(self, self.options.api,
      {
         
         show: function(id, auto)
         {
            var returned, thumbnail, fullsize, background, left;
            
            // Call API method and if return value is false, halt
            returned = self.beforeShow.call(self, id);
            if(returned === false) return self;
            
            // Return if no ID was specified
            if(id === null) return;
            
            // Setup element references
            thumbnail = self.elements.nav.find('[bannr='+id+']:first').trigger('mouseenter', [auto]);
            fullsize = self.elements.container.find('[bannr='+id+']:first');
            
            // Determine background of currently active image and remove active class
            background = self.elements.container.find('img.'+self.options.classes.active).attr('src');
            left = fullsize.position().left;
            
            // Remove active classes and trigger mouseleave event
            self.elements.master.find('.'+self.options.classes.active)
               .removeClass(self.options.classes.active)
               .trigger('mouseleave', [auto]);
            
            // Set new background and position of container element
            self.elements.container.css({
               left: -left,
               background: 'transparent url('+background+') no-repeat '+left+'px 0px'
            });
            
            // Set current id and add active classes
            self.status.current = id;
            thumbnail.addClass(self.options.classes.active);
            fullsize.addClass(self.options.classes.active);
            
            // Fade and slide in image
            fullsize.stop(true, false)
               .css({ opacity: 0, marginLeft: -25, paddingRight: 25 })
               .animate({ opacity: 1, marginLeft: 0, paddingRight: 25 }, 900);
            
            // Fade and slide in title
            self.elements.title
               .stop(true, false)
               .animate({ opacity: 0 }, self.options.effect.length / 2, function()
               {
                  self.elements.title
                     .text( fullsize.data('title') )
                     .css({ left: -10 })
                     .animate({ opacity: 1, left: 20 }, self.options.effect.length / 2, function()
                     { 
                        // Fix antialias issue in IE
                        if($.browser.msie) self.elements.title.get(0).style.removeAttribute('filter');
                     });
               })
            
            // Call API method
            self.onShow.call(self, id);
            
            return self;
         },
         
         next: function(auto)
         {
            var items, id
            
            // Setup items and id properties
            items = self.elements.container.find('img').length;
            id = (self.status.current == items - 1) ? 0 : self.status.current + 1;
            
            // Show the item
            self.show(id, auto);
            
            return self;
         },
         
         prev: function(auto)
         {
            var items, id
            
            // Setup items and id properties
            items = self.elements.container.find('img').length;
            id = (self.status.current == 0) ? items.length - 1 : self.status.current - 1;
            
            // Show the item
            self.show(id, auto);
            
            return self;
         },
         
         scroll: function(speed)
         {
            var item, returned;
            
            // Call API method and if return value is false, halt
            returned = self.beforeScroll.call(self);
            if(returned === false) return self;
            
            // Define speed if not given
            if(typeof speed != 'number') speed = 7000;
            
            // Start the auto timer
            self.timers.scroll = setInterval(function()
            {
               // If bannr is disabled, return
               if(self.status.disabled === true || self.status.hovered === true) return;
               
               // Show the next image in the series and hide the tooltip
               self.next(true);
               self.elements.tooltip.css({ opacity: 0 });
            }
            , speed)
            
            // Call API method
            self.onScroll.call(self);
            
            return self;
         },
         
         disable: function(state)
         {
            self.status.disabled = (state) ? true : false;
            
            return self;
         },
         
         destroy: function()
         {
            var returned;
            
            // Call API method and if return value is false, halt
            returned = self.beforeDestroy.call(self);
            if(returned === false) return self;
            
            // Remove the elements
            self.elements.nav.remove();
            self.elements.title.remove();
            self.elements.loading.remove();
            
            // Put all images back into original container and state
            self.elements.container.find('img').each(function()
            {
               $(this).attr('title', $(this).data('title')).removeData('title')
                  .attr('alt', $(this).data('tooltip')).removeData('tooltip')
                  .removeClass(self.options.classes.active)
                  .appendTo(self.elements.master);
            })
            
            // Remove classes, events and container element
            self.elements.container.remove();
            self.elements.master.removeClass('bannr '+self.options.classes.master)
               .unbind('mouseover.bannr mouseout.bannr');
            
            // Stop timer if present
            clearInterval(self.timers.scroll);
            
            // Call API method and log destroy
            self.onDestroy.call(self);
            
            return self.elements.master
         }

      });
      
      // Create the tooltip
      construct.call(self);
   };
   
   // Define priamry construct function
   function construct()
   {
      var self;
      self = this;
      
      // Create container
      self.elements.master.css({ zoom: 1 })
         .wrapInner('<div class="'+self.options.classes.container+'" '+'style="position:relative; width:10000em; z-index:3;"></div>');
      self.elements.container = self.elements.master.children('div:first');
      
      // Create loading screen
      self.elements.loading = $('<div>')
         .addClass(self.options.classes.loading)
         .append(
            '<div style="width: 220px; margin:'+Math.floor(self.elements.master.outerHeight() / 2)+'px auto 0;">' +
            '<img src="'+self.options.loading.image+'" alt="'+self.options.loading.text+'"/>' +
            '<span>0%</span></div>'
         )
         .prependTo(self.elements.master);
      
      function render()
      {
         var wrapper;
         
         // Call API method
         self.beforeRender.call(self);
         
         // Create title
         self.elements.title = $('<div>')
            .addClass(self.options.classes.title)
            .prependTo(self.elements.master);
         
         // Create navigation
         self.elements.nav = $('<div>')
            .addClass(self.options.classes.nav)
            .appendTo(self.elements.master);
         
         // Create tooltip
         self.elements.tooltip = $('<div>').css({ opacity: 0 })
            .addClass(self.options.classes.tooltip)
            .appendTo(self.elements.nav);
         
         // Create navigation items
         self.elements.container.find('img:lt('+self.options.items+')').each(function(i)
         {
            var thumbnail, width, height, left;
            
            // Set image attributes
            $(this).show()
               .data('title', $(this).attr('title')).removeAttr('title')
               .data('tooltip', $(this).attr('alt')).removeAttr('alt');
            
            // Setup new thumbnail
            thumbnail = $(this).clone()
               .removeClass(self.options.classes.active)
               .attr('width', Math.floor($(this).width() / 6))
               .attr('height', Math.floor($(this).height() / 6))
            
            // Create wrapper and append it
            $('<div>').addClass(self.options.classes.thumbnail)
               .append(thumbnail)
               .prependTo(self.elements.nav)
               
            // Add the bannr id's
            .add(this).attr('bannr', i);
         });
         
         // Assign events
         assignEvents.call(self);
         
         // Set first iamge as active if none is tagged
         var active = self.elements.container.find('img.active')
         if(active.length == 0) 
            active = self.elements.container.find('img:first').addClass(self.options.classes.active);
            
         // Start initial animation with active image
         self.elements.nav.find('[bannr='+active.attr('bannr')+']:first').trigger('mouseenter', [true])
         self.show(parseInt(active.attr('bannr')), true)
         
         // Set the background to transparent initially to avoid twin overlaps
         self.elements.container.css({ background: 'transparent' });
         
         // Start scroll if enabled
         if(self.options.scroll !== false) self.scroll(self.options.scroll);
         
         // Call API method
         self.onRender.call(self);
      }
      
      // Define afterLoad method
      function afterLoad()
      {
         render(); // Create bannr 
      
         // Fade out loading screen and tooltip
         self.elements.loading.fadeOut(300);
         self.elements.tooltip.css({ opacity: 0 });
      }
      
      // Make sure images are loaded before rendering
      var images = self.elements.container.find('img[complete=false]');
      var loadedImages = 0;
      if(images.length > 0)
      {
         images.each(function()
         {
            $(this).load(function()
            { 
               loadedImages++;
               
               // Set loading precentage
               var percentage = Math.floor((loadedImages / images.length) * 100);
               self.elements.loading.find('span').text(percentage + '%');
               
               // If all images are loaded, continue
               if(loadedImages == images.length) afterLoad();
            });
         });
      }
      else afterLoad();
      

   };
   
   function assignEvents()
   {
      var self;
      self = this;
      
      // Find all navigation items
      self.elements.nav.find('.'+self.options.classes.thumbnail).each(function()
      {
         // Setup click event
         $(this).bind('click.bannr', function()
         {
            // Return if the item is already active or bannr is disabled
            if($(this).hasClass(self.options.classes.active) || self.status.disabled === true) return;
            
            // Show the image
            self.show( parseInt($(this).attr('bannr')) )
         })
         
         // Setup mouseenter event
         .bind('mouseenter.bannr', function(event, auto)
         { 
            var position, content, length;
            length = (auto === true) ? self.options.effect.length : 220;
            
            // Return if the item is already active or bannr is disabled
            if($(this).hasClass(self.options.classes.active) || self.status.disabled === true) return;
            
            // Setup tooltip details
            position = $(this).position().left ;
            content = self.elements.container.find('[bannr='+$(this).attr('bannr')+']:first').data('tooltip');
            
            // Animate tooltip if not an automatic scroll
            if(auto !== true)
            {
               self.elements.tooltip
                  .stop(true, false)
                  .text( content )
                  .animate({ opacity: 1, left: position }, 220, function()
                  { 
                     // Fix antialias issue in IE
                     if($.browser.msie) self.elements.tooltip.get(0).style.removeAttribute('filter');
                  });
            }
            else self.elements.tooltip.css({ opacity: 0 });
            
            // Animate the thumbnail
            $(this).stop(true, false).animate({ height: 50, marginTop: -15 }, length, 'swing');
         })
         
         // Setup mouseleave event
         .bind('mouseleave.bannr', function(event, auto)
         { 
            var length = (auto === true) ? self.options.effect.length : 220;
            
            // Return if the item is already active or bannr is disabled
            if($(this).hasClass(self.options.classes.active) || self.status.disabled === true) return;
            
            // Fade out the tooltip
            if(auto !== true) self.elements.tooltip.stop(true, false).animate({ opacity: 0 }, 220);
            
            // Animate the thumbnail
            $(this).stop(true, false).animate({ height: 35, marginTop: 0 }, length, 'swing') ;
         });
         
         // Setup scroll events
         self.elements.master.bind('mouseover.bannr', function(){ self.status.hovered = true });
         self.elements.master.bind('mouseout.bannr', function(){ self.status.hovered = false });
      });
   };

   // Define configuration defaults
   $.fn.bannr.defaults = {
      // Navigation
      items: 4,
      scroll: true,
      
      loading: {
         text: 'Loading...',
         image: 'images/loading.gif'
      },
      
      // Effects
      effect: {
         type: 'fade slide',
         length: 900
      },
      
      // Style
      classes: {
         master: '',
         container: 'bannr-container',
         loading: 'bannr-loading',
         title: 'bannr-title',
         nav: 'bannr-nav',
         thumbnail: 'bannr-thumbnail',
         tooltip: 'bannr-tooltip',
         active: 'bannr-active'
      },
      
      // Callbacks
      api: {
         beforeRender: function(){},
         onRender: function(){},
         beforeShow: function(){},
         onShow: function(){},
         beforeScroll: function(){},
         onScroll: function(){},
         beforeDestroy: function(){},
         onDestroy: function(){}
      }
   };
})(jQuery);