/**
 * jQuery Gallery Plugin
 *   http://code.google.com/p/jquery-gallery-plugin/
 *
 * Copyright (c) 2009 Yusuke Horie
 *
 * Released under the MIT License:
 * http://www.opensource.org/licenses/mit-license.php
 *
 * Since  : 0.1.0 - 08/02/2009
 * Version: 0.3.0 - 08/25/2009
 */ 
(function(jQuery) {

  /** private properties **/
 
  var _inc = 0;

  /** public methods **/

  jQuery.fn.gallery = function (options) {
    var options = jQuery.extend({}, jQuery.fn.gallery.defaults, options);
    
    return this.each(function(i, e) {
    	 
      var
        $this = jQuery(e),
        id = options.prefix + _inc,
        i = 0,
        n = 0,
        limit = 5,
        step = 1,
        duration = Math.ceil(options.interval*0.3),
        timeId = null;

      var height;
      if (!options.height) {
        if (!parseInt($this.css('height'), 10)) {
          height = '450px';
        } else {
          height = $this.height();
        }
      } else {
        height = options.height;
      }

      var
        width = (!options.width) ? $this.width(): options.width,
        paddingTop = parseInt($this.css('padding-top'), 10),
        paddingBottom = parseInt($this.css('padding-bottom'), 10),
        pheight = parseInt(height, 10),
        contentHeight = pheight - options.thumbHeight + paddingTop,
        o = $this.offset(),
        barWidth = jQuery(window).width() - o.left;

      // thumbnail bar
      var barTop = (options.barPosition == 'top') ? paddingTop: contentHeight;

      if (options.toggleBar) {
        if (options.barPosition == 'top') {
          $this.hover(
            function() {
              jQuery('#thumbnails_' + id).animate({top: paddingTop}, {queue: false, duration: 300});
            },
            function() {
              jQuery('#thumbnails_' + id).animate({top: barTop}, {queue: false, duration: 300});
            });
          barTop = (options.thumbHeight + paddingTop) * (-1);
        } else {
          var outerHeight = pheight + paddingTop + paddingBottom;
          $this.hover(
            function() {
              jQuery('#thumbnails_' + id).animate({top: contentHeight}, {queue: false, duration: 300});
            },
            function() {
              jQuery('#thumbnails_' + id).animate({top: outerHeight}, {queue: false, duration: 300});
            });
          barTop = outerHeight;
        }
      }

      $this
        .css({
          width: width,
          height: height,
          zIndex: options.zIndex
        })
        .prepend('<div id="' + id + '"></div>')
        .find('ul')
          .attr('id', 'thumbnails_' + id)
          .addClass(options.barClass)
          .css({
            top: barTop,
            height: options.thumbHeight + 'px',
            width: barWidth + 'px',
            zIndex: options.zIndex + 1
          })
          .find('li')
            .css({
              width: options.thumbWidth + 'px',
              height: options.thumbHeight + 'px'
            })
            .each(function (index) {
              jQuery.data(this, 'index', index);
            })
            .click(function (event) {
              event.preventDefault();
              if (options.slideshow) clearInterval(timeId);

              if (jQuery.isFunction(options.onSelect))
                options.onSelect.apply(this, [event]);

              var $e = this;
              bar.find('li').each(function (index, e) {
                if (e == $e) {
                  step = index;
                  return false;
                }
              });

              i = jQuery.data(this, 'index');

              // pre load
              for (var j=i; j<i+limit; j++) {
                var o = pictures.eq(j);
                if (o.length > 0 && typeof o.data('loaded') == 'undefined') {
                  preLoad(o.attr('href'));
                  o.data('loaded', true);
                }
              }

              display();
              if (options.slideshow)
                timeId = setInterval(display, options.interval);
            });

      if (options.showOverlay) {
        var
          itop = pheight*(1-options.ratio) + paddingTop,
          ileft = $this.css('padding-left'),
          iheight = (pheight*options.ratio) + paddingTop;

        // screen
        jQuery('<div />')
          .addClass(options.screenClass)
          .css({
            opacity: 0.5,
            top: itop,
            left: 0,
            height: iheight,
            width: $this.outerWidth(),
            zIndex: options.zIndex + 1
          })
          .insertAfter('#' + id);

        jQuery('<div />')
          .addClass(options.infoClass)
          .html('<div id="gtitle_' + id + '" class="' + options.titleClass + '" style="display:none;"></div>' +
            '<div id="gdesc_' + id + '" class="' + options.descClass + '" style="display:none;"></div>')
          .css({
            top: itop,
            left: 0,
            height: iheight,
            zIndex: options.zIndex + 1
          })
          .insertAfter('#' + id); 
      }

      // content container
      var c = jQuery('#' + id).css({
        position: 'relative',
        width: width,
        height: height,
        overflow: 'hidden'
      }).addClass(options.contentClass);

      var
        bar = $this.find('ul').show(),
        thumbnails = bar.find('img'),
        pictures = $this.find('a').bind('click.gallery', function (e) { e.preventDefault(); }),
        gtitle = jQuery('#gtitle_' + id),
        gdesc = jQuery('#gdesc_' + id),
        len =  thumbnails.length,
        w = $this.find('li:first').outerWidth(true);

      var display = function () {
        var t = thumbnails.eq(i);
        var pict = pictures.eq(i);
        var p = pict.attr('href');
        var pid = id + '_' + i;

        // pre load
        var next = pictures.eq(i+limit);
        if (next.length > 0 && typeof next.data('loaded') == 'undefined') {
          preLoad(next.attr('href'));
        }

        // delete previous picture
        c.find('img').animate({opacity: 0.0}, {
          queue: false,
          duration: duration,
          easing: 'linear',
          complete: function() { jQuery(this).remove(); }
        });

        // append new picture
        jQuery('<img />')
          .attr('src', p)
          .attr('id', pid)
          .css({
            position: 'absolute',
            top: 0,
            left: 0,
            opacity: 0.0
          })
          .bind('click.gallery', function (event) {
            options.onClick.apply(this, [event, pict]);
          })
          .appendTo('#' + id)
          .animate({opacity: 1.0}, {
            queue: false,
            duration: duration,
            easing: 'linear'
          })
          .load(function () {
            pict.data('loaded', true);
          });

        var title = t.attr('title');
        var id_of_desc = pict.attr('rel');
        var desc = (id_of_desc && jQuery('#' + id_of_desc).length > 0)
          ? jQuery('#' + id_of_desc).html(): pict.attr('title');

        if (n != 0) {
          // title
          if (typeof title != 'undefined')
            gtitle.fadeOut(duration*0.3, function () {
              jQuery(this).html(title).show();
            });

          // description
          if (typeof desc != 'undefined')
            gdesc.fadeOut(duration*0.3, function () {
              jQuery(this).html(desc).show();
            });

          // scrolle
          bar.animate({left: w*(-1)*step}, {
            queue: false,
            duration: 300,
            easing: options.easing,
            complete: function () {
              var $t = jQuery(this).css({left: 0});
              var f = $t.find('li').slice(0, step);
              var indexes = f.map(function () {
                return jQuery.data(this, 'index');
              });
              var cln = f.clone(true).each(function (j) {
                jQuery.data(this, 'index', indexes[j]);
              }).hide().appendTo(this).fadeIn(duration);
              f.remove();
              step = 1;
            }
          });
        } else {
          // title & description
          if (typeof title != 'undefined') gtitle.html(title).show();
          if (typeof desc != 'undefined') gdesc.html(desc).show();
        }

        options.onChange.apply($this.get(), [i, pict.get()]);

        if (i < (len-1)) {
          i++;
        } else {
          i = 0;
        }
        n++;
      };

      // pre load
      for (var j=0; j<limit; j++) {
        var o = pictures.eq(j);
        if (o.length > 0) {
          preLoad(o.attr('href'));
          o.data('loaded', true);
        }
      }

      display();
      if (options.slideshow)
        timeId = setInterval(display, options.interval);

      _inc++;
    });
  };

  jQuery.fn.gallery.defaults = {
    width: null,
    height: null,
    thumbWidth: 55,
    thumbHeight: 55,
    zIndex: 1,
    interval: 4500,
    prefix: 'gallery_',
    easing: 'linear',
    ratio: 0.35,
    slideshow: true,
    toggleBar: true,
    showOverlay: true,
    barPosition: null,
    barClass: 'galleryBar',
    contentClass: 'galleryContent',
    infoClass: 'galleryInfo',
    screenClass: 'galleryScreen',
    titleClass: 'galleryTitle',
    descClass: 'galleryDesc',
    onClick: function () { return; },
    onSelect: function () { return; },
    onChange: function () { return; }
  };

  /** private methods **/

  var preLoad = function (url) {
    jQuery('<img />').attr('src', url);
  };

})(jQuery);

/**
* author Remy Sharp
* url http://remysharp.com/tag/marquee
*/

(function ($) {
    $.fn.marquee = function (klass) {
        var newMarquee = [],
            last = this.length;

        // works out the left or right hand reset position, based on scroll
        // behavior, current direction and new direction
        function getReset(newDir, marqueeRedux, marqueeState) {
            var behavior = marqueeState.behavior, width = marqueeState.width, dir = marqueeState.dir;
            var r = 0;
            if (behavior == 'alternate') {
                r = newDir == 1 ? marqueeRedux[marqueeState.widthAxis] - (width*2) : width;
            } else if (behavior == 'slide') {
                if (newDir == -1) {
                    r = dir == -1 ? marqueeRedux[marqueeState.widthAxis] : width;
                } else {
                    r = dir == -1 ? marqueeRedux[marqueeState.widthAxis] - (width*2) : 0;
                }
            } else {
                r = newDir == -1 ? marqueeRedux[marqueeState.widthAxis] : 0;
            }
            return r;
        }

        // single "thread" animation
        function animateMarquee() {
            var i = newMarquee.length,
                marqueeRedux = null,
                $marqueeRedux = null,
                marqueeState = {},
                newMarqueeList = [],
                hitedge = false;
                
            while (i--) {
                marqueeRedux = newMarquee[i];
                $marqueeRedux = $(marqueeRedux);
                marqueeState = $marqueeRedux.data('marqueeState');
                
                if ($marqueeRedux.data('paused') !== true) {
                    // TODO read scrollamount, dir, behavior, loops and last from data
                    marqueeRedux[marqueeState.axis] += (marqueeState.scrollamount * marqueeState.dir);

                    // only true if it's hit the end
                    hitedge = marqueeState.dir == -1 ? marqueeRedux[marqueeState.axis] <= getReset(marqueeState.dir * -1, marqueeRedux, marqueeState) : marqueeRedux[marqueeState.axis] >= getReset(marqueeState.dir * -1, marqueeRedux, marqueeState);
                    
                    if ((marqueeState.behavior == 'scroll' && marqueeState.last == marqueeRedux[marqueeState.axis]) || (marqueeState.behavior == 'alternate' && hitedge && marqueeState.last != -1) || (marqueeState.behavior == 'slide' && hitedge && marqueeState.last != -1)) {                        
                        if (marqueeState.behavior == 'alternate') {
                            marqueeState.dir *= -1; // flip
                        }
                        marqueeState.last = -1;

                        $marqueeRedux.trigger('stop');

                        marqueeState.loops--;
                        if (marqueeState.loops === 0) {
                            if (marqueeState.behavior != 'slide') {
                                marqueeRedux[marqueeState.axis] = getReset(marqueeState.dir, marqueeRedux, marqueeState);
                            } else {
                                // corrects the position
                                marqueeRedux[marqueeState.axis] = getReset(marqueeState.dir * -1, marqueeRedux, marqueeState);
                            }

                            $marqueeRedux.trigger('end');
                        } else {
                            // keep this marquee going
                            newMarqueeList.push(marqueeRedux);
                            $marqueeRedux.trigger('start');
                            marqueeRedux[marqueeState.axis] = getReset(marqueeState.dir, marqueeRedux, marqueeState);
                        }
                    } else {
                        newMarqueeList.push(marqueeRedux);
                    }
                    marqueeState.last = marqueeRedux[marqueeState.axis];

                    // store updated state only if we ran an animation
                    $marqueeRedux.data('marqueeState', marqueeState);
                } else {
                    // even though it's paused, keep it in the list
                    newMarqueeList.push(marqueeRedux);                    
                }
            }

            newMarquee = newMarqueeList;
            
            if (newMarquee.length) {
                setTimeout(animateMarquee, 25);
            }            
        }
        
        // TODO consider whether using .html() in the wrapping process could lead to loosing predefined events...
        this.each(function (i) {
            var $marquee = $(this),
                width = $marquee.attr('width') || $marquee.width(),
                height = $marquee.attr('height') || $marquee.height(),
                $marqueeRedux = $marquee.after('<div ' + (klass ? 'class="' + klass + '" ' : '') + 'style="display: block-inline; width: ' + width + 'px; height: ' + height + 'px; overflow: hidden;"><div style="float: left; white-space: nowrap;">' + $marquee.html() + '</div></div>').next(),
                marqueeRedux = $marqueeRedux.get(0),
                hitedge = 0,
                direction = ($marquee.attr('direction') || 'left').toLowerCase(),
                marqueeState = {
                    dir : /down|right/.test(direction) ? -1 : 1,
                    axis : /left|right/.test(direction) ? 'scrollLeft' : 'scrollTop',
                    widthAxis : /left|right/.test(direction) ? 'scrollWidth' : 'scrollHeight',
                    last : -1,
                    loops : $marquee.attr('loop') || -1,
                    scrollamount : $marquee.attr('scrollamount') || this.scrollAmount || 2,
                    behavior : ($marquee.attr('behavior') || 'scroll').toLowerCase(),
                    width : /left|right/.test(direction) ? width : height
                };
            
            // corrects a bug in Firefox - the default loops for slide is -1
            if ($marquee.attr('loop') == -1 && marqueeState.behavior == 'slide') {
                marqueeState.loops = 1;
            }

            $marquee.remove();
            
            // add padding
            if (/left|right/.test(direction)) {
                $marqueeRedux.find('> div').css('padding', '0 ' + width + 'px');
            } else {
                $marqueeRedux.find('> div').css('padding', height + 'px 0');
            }
            
            // events
            $marqueeRedux.bind('stop', function () {
                $marqueeRedux.data('paused', true);
            }).bind('pause', function () {
                $marqueeRedux.data('paused', true);
            }).bind('start', function () {
                $marqueeRedux.data('paused', false);
            }).bind('unpause', function () {
                $marqueeRedux.data('paused', false);
            }).data('marqueeState', marqueeState); // finally: store the state
            
            // todo - rerender event allowing us to do an ajax hit and redraw the marquee

            newMarquee.push(marqueeRedux);

            marqueeRedux[marqueeState.axis] = getReset(marqueeState.dir, marqueeRedux, marqueeState);
            $marqueeRedux.trigger('start');
            
            // on the very last marquee, trigger the animation
            if (i+1 == last) {
                animateMarquee();
            }
        });            

        return $(newMarquee);
    };
}(jQuery));

