(function($) {
    var ua      = navigator.userAgent.toLowerCase();
    isWebkit    = !!ua.match(/applewebkit/i);
    isIOS5      = !!ua.match(/os 5_[0-9_]+ like mac os x/i);
    var supportsTouch = false;
    try {
        document.createEvent("TouchEvent");
        supportsTouch = true;
    } catch (e) {}

    var bindHere = function(fn, me) { 
      return function() { 
        return fn.apply(me, arguments); 
      }; 
    };

    var cssWithoutUnit = function(element, attribute) {
    var measure = element.css(attribute);
      return (measure !== undefined) ? parseInt(measure.replace('px', ''), 10) : 0;
    };

    var Position = function(x, y) {
        if (x && x.hasOwnProperty('x') && x.hasOwnProperty('y')) {
            x = x.x;
            y = x.y;
        }
        this.x = x;
        this.y = y;
    };
    Position.prototype = {
        difference: function(p) {
            return new Position(p.x - this.x, p.y - this.y);
        }
    };
            
    var Slider = function(settings)
    {
        $(window).load(function() {
            Slider.loaded(); 
        });
    
        return this.each(function() {
            Slider.init($(this));
            if (!supportsTouch) {
                Slider.args.loadTimers = setInterval(Slider.update, 1000);
            }
        });
    };
    
    Slider.args = {
        loaded: false
    };
    
    Slider.init = function($scrollObj)
    {
        var self = this;

        if (isIOS5) {
            var $parent = $scrollObj.parent();
            $parent.css({overflow: 'scroll', '-webkit-overflow-scrolling': 'touch', 'padding-bottom': '10px'});
        } else {
            $scrollObj.wrap($('<div></div>').attr({'className':'slider-container'}));

            this.objs                 = {};     
            this.objs.scroll          = $scrollObj.css({overflow: 'visible', left: 0});
            this.objs.sliderContainer = $('.slider-container');

            this.setDimensions();
            this.initScrollbars();

            if (supportsTouch) {
                var touch = new TouchSlider();
                touch.init(this.objs.sliderContainer.selector + ' table', {tossing: true});
                touch.bind('slide', function(slider) {
                    var maxLeft  = self.objs.sliderTrackX.width() - self.objs.drag.width();
                    var ratio = self.objs.sliderTrackX.width() / slider.containerWidth();
                    var left = -slider.position.x * ratio;
                    self.objs.drag.css('left', left + 'px');    
                });
            } else {
                $(window).load(function() {
                  Slider.update($scrollObj);          
                });

                $(window).resize(function() {
                  Slider.update($scrollObj);
                });

                this.objs.sliderContainer.bind('mousewheel', bindHere(MouseEvents.mousewheel, this));
            }
        }
    };
    
    Slider.setDimensions = function()
    {
        if (!this.dims)
        {
            this.dims = {}; 
        }
        
        if (!this.objs)
        {
            return false;
        }                
        
        // Width of container
        this.dims.containerWidth = this.objs.sliderContainer.innerWidth();

        // Width of content
        if (this.objs.scroll.attr('tagName') != 'TABLE')
        {
            this.objs.scroll.css({overflow:"hidden"});  
        }
        
        this.dims.contentWidth = this.objs.scroll.attr('scrollWidth');

        this.objs.scroll.css({overflow:"visible"});
        
        if ($.browser.msie && $.browser.version.substr(0,1) < 8)
        {
            if (this.dims.contentWidth == this.dims.containerWidth && this.dims.containerWidth > $(window).width())
            {
                this.objs.sliderContainer.css('width', $(window).width());
            }
        }
        
        // Percent of content in view
        this.dims.percentInViewX = this.dims.containerWidth / this.dims.contentWidth;

        // Width of drag button
        this.dims.dragButtonWidth   = this.dims.containerWidth * this.dims.percentInViewX;
        this.dims.dragButtonPercent = this.dims.percentInViewX * 100;
    };

    Slider.initScrollbars = function()
    {
        this.objs.sliderTrack = this.objs.sliderContainer.find('.slider-track:first');
        if (this.dims.percentInViewX < 0.99) {
            this.addScrollbar();
            this.setScrollDimensions();
        } else {
            this.removeScrollbar();
        }
    };
    
    Slider.addScrollbar = function()
    {
        if (this.objs.sliderTrack.length <= 0)
        {
            var slider = $('<div/>', {
                'id': 'slider',
                'className': 'slider-track'
            });
            var sliderTrackX = $('<div/>', {
               'className': 'slider-track-x' 
            });
            var handle = $('<div/>', {
                'id': 'handle',
                'className': 'slider-drag'
            }).css('left', 0);

            this.objs.sliderContainer.append(slider.append(sliderTrackX.append(handle)));
            
            this.objs.sliderTrack  = slider;
            this.objs.sliderTrackX = sliderTrackX;
            this.objs.drag         = handle;

            this.objs.sliderTrackX.bind('mousedown', bindHere(MouseEvents.mousedown, this));
            
            if ($('#customize_iframe').length > 0)
            {
                var iframeTop = $('#customize_iframe').css('top').replace('px', '')*1;
                var iframeOffset = this.objs.sliderTrack.attr('offsetHeight');
                $('#customize_iframe').css('top', iframeTop+iframeOffset);
            }
        }
    };

    Slider.removeScrollbar = function()
    {
        if (this.objs.sliderTrack.length > 0)
        {
            if ($('#customize_iframe').length > 0)
            {
                var iframeTop = $('#customize_iframe').css('top').replace('px', '')*1;
                var iframeOffset = this.objs.sliderTrack.attr('offsetHeight');
                $('#customize_iframe').css('top', iframeTop-iframeOffset);              
            }
            
            this.objs.scroll.css({left: 0});
            this.objs.sliderTrack.remove();     
        }
    };

    Slider.setScrollDimensions = function()
    {
        var dragCss = {width: this.dims.dragButtonPercent+'%', position: 'relative'};           
        this.objs.drag.css(dragCss);        
    };
    
    Slider.adjustScroll = function()
    {
        var dragLeft     = this.objs.drag.css('left').replace('px','')*1;
        var adjustOffset = dragLeft+this.objs.drag.width();

        if (adjustOffset > this.objs.sliderTrackX.width())
        {               
            dragLeft = this.objs.sliderTrackX.width() - this.objs.drag.width();
            this.objs.drag.css({left: dragLeft+'px'});
        }

        var scrollWidth = this.dims.contentWidth - this.dims.containerWidth;
        var maxLeft     = this.objs.sliderTrackX.width() - this.objs.drag.width();
        
        var scrollPercent = maxLeft/dragLeft;
        var scrollLeft    = -scrollWidth/scrollPercent;

        this.objs.scroll.css({left: scrollLeft+'px'});
    };
    
    Slider.update = function()
    {
        // window.console.log('update');
        // Calculate dimensions
        if (this.objs)
        {
            this.setDimensions();   
            this.initScrollbars();
        }

        if (this.objs && this.objs.drag)
        {
            // Update scrollbar dimensions
            this.setScrollDimensions();
            this.adjustScroll();            
        }
    };
    
    Slider.loaded = function()
    {
        clearInterval(this.args.loadTimers);
        this.args.loadTimers = false;
        delete this.args.loadTimers;
        
        this.args.loaded = true;

        if (this.objs && this.objs.sliderContainer)
        {
            this.update();  
        }
    };

    var MouseEvents = {
        mousedown: function(e) {
            $drag = this.objs.drag;
            
            this.startX = e.pageX;
            this.startY = e.pageY;
            this.offset = this.objs.drag.css('left').replace('px','')*1;
            this.position   = this.objs.drag.css('left').replace('px','')*1;
            this.maxLeft  = this.objs.sliderTrackX.width() - this.objs.drag.width();

            if (e.target != this.objs.drag.get(0))
            {
                var clickOffset = this.objs.sliderTrackX.offset();
                var offsetPercent = (this.startX - clickOffset.left) / this.objs.sliderTrackX.width();
                $drag.css('left', this.maxLeft*offsetPercent + 'px');    
                Slider.adjustScroll();
                
                this.offset = this.maxLeft*offsetPercent;
            }
            
            var self = this;
            $(document).bind('mousemove', function(e) {
                return MouseEvents.mousemove.call(self, e);
            });
            $(document).bind('mouseup', function(e) {
                $(document).unbind('mousemove');
                $(document).unbind('mouseup');
            });

            return false;
        },

        mousemove: function(e) {
            var left = this.offset - (this.startX - e.pageX);

            if (left < 0)
            {
                left = 0;
            }
            else if (left > this.maxLeft)
            {
                left = this.maxLeft;
            }
            
            Slider.adjustScroll();
            this.objs.drag.css({left: left+'px'});   
        },

        mousewheel: function(e, delta, deltaX, deltaY) {
            if (this.objs && this.objs.drag)
            {
                var offsetLeft = this.objs.drag.css('left').replace('px','')*1;
                var maxLeft    = this.objs.sliderTrackX.width()-this.objs.drag.width();
                var dragLeft   = offsetLeft+deltaX;

                if (dragLeft < 0)
                {
                    dragLeft = 0;
                }
                else if (dragLeft > maxLeft)
                {
                    dragLeft = maxLeft;
                }

                Slider.adjustScroll();
                this.objs.drag.css({left: dragLeft+'px'});      
            }       
        }
    };

    var TouchSlider = function() {};
    TouchSlider.prototype.init = function(target, opts) {
        this.opts = jQuery.extend({
            hooks: {},
            animateEasing: 'swing',
            animateDuration: $.fx.speeds._default,
            tossFunction: function(x, dx, dt) {
                return x + dx * 100 / dt;
            },
            tossing: false
        }, opts);

        this.position = new Position(0, 0);
        this.startPosition = new Position(this.position);
        this.lastPosition = new Position(this.position);
        this.gesturing = false;

        var self = this;
        var $target = $(target);
      
        $target.addClass('touch-slider');
        this.element = $target;
        this.frame = $target.parent('.slider-container');
      
        // Bind callbacks passed in at initialization
        $.each(this.opts.hooks, function(name, fn) {
            if ($.isArray(fn)) {
                $.each(fn, function(fn2) {
                    self.subscribe(name, fn2);
                });
            } else {
                self.subscribe(name, fn);
            }
        });


        $target[0].addEventListener('touchstart', this, false);
        $target[0].addEventListener('webkitTransitionEnd', this, false);

        this.element.trigger('init.virb-lectric');
    };

    TouchSlider.prototype.subscribe = function(name, fn) {
        var self = this;
        var callback = function(e) {
            if (e.target == self.element[0]) { 
                fn(self, e);
            }
        };

        this.element.bind(name + '.virb-lectric', callback);
        return callback;
    };
    TouchSlider.prototype.bind = function(name, fn) {
        this.subscribe(name, fn);
    };

    TouchSlider.prototype.unsubscribe = function(name, fn) {
        if (typeof fn !== undefined && $.isFunction(fn)) {
            this.element.unbind(name + '.virb-lectric', fn);
        } else {
            this.element.unbind(name + '.virb-lectric');
        }
    };
    TouchSlider.prototype.unbind = function(name, fn) {
        this.unsubscribe(name, fn);
    };

    TouchSlider.prototype.containerWidth = function() {
        var padding = cssWithoutUnit(this.element, 'paddingRight') + cssWithoutUnit(this.element, 'paddingLeft');
        return cssWithoutUnit(this.element, 'marginRight') + padding + this.element.width();
    };

    TouchSlider.prototype.frameWidth = function() {
        var padding = cssWithoutUnit(this.frame, 'paddingRight') + cssWithoutUnit(this.frame, 'paddingLeft');
        return cssWithoutUnit(this.frame, 'marginRight') + padding + this.frame.width();
    };

    TouchSlider.prototype.limitXBounds = function(x) {
        var containerWidth = this.containerWidth();
        
        x = (x + this.containerWidth() - this.frameWidth() > 0) ? x : this.position.x;
        x = (x > 0) ? 0 : x;

        return x;
    };

    TouchSlider.prototype.handleEvent = function(event) { 
        TouchEvents[event.type].call(this, event); 
    };

    TouchSlider.prototype.update = function(opts) {
        var options = jQuery.extend({animate: true, triggerSlide: true}, opts);
        if (options.animate) { this.decayOn(); }
        this.element.css({'-webkit-transform': 'translate3d(' + this.position.x + 'px, 0, 0)'}); 

        if (options.triggerSlide) { this.element.trigger('slide.virb-lectric'); }
    };

    TouchSlider.prototype.decayOff = function() {
        this.element.css({'-webkit-transition-duration': '0s'});
        this.element.css({'-webkit-transition-property': 'none'});
    };

    TouchSlider.prototype.decayOn = function() {
        var duration = this.opts.animateDuration;
        if (typeof duration === "number") {
            duration = duration / 1000;
        } else {
            if (duration in $.fx.speeds) {
                duration = $.fx.speeds[duration];
            } else {
                duration = $.fx.speeds._default;
            }
        }

        this.element.css({'-webkit-transition-duration': duration + 's'});
        this.element.css({'-webkit-transition-property': '-webkit-transform'});
    };

    var TouchEvents = {
        click: function(e) {
            if (this.moved) { e.preventDefault(); }
            this.element[0].removeEventListener('click', this, false);
            return false;
        },

        touchstart: function(e) {
            this.currentTarget = e.currentTarget;
            this.startPosition.x = e.touches[0].pageX - this.position.x;
            this.startPosition.y = e.touches[0].pageY - this.position.y;
            this.moved = false;

            window.addEventListener('gesturestart', this, false);
            window.addEventListener('gestureend', this, false);
            window.addEventListener('touchmove', this, false);
            window.addEventListener('touchend', this, false);
            this.element[0].addEventListener('click', this, false);

            this.decayOff();

            this.element.trigger('start.virb-lectric');
        },

        touchmove: function(e) {
            if (this.gesturing) { return false; }

            if (!this.moved) {
                var deltaY = e.touches[0].pageY - this.startPosition.y;
                var deltaX = e.touches[0].pageX - this.startPosition.x;
                if (Math.abs(deltaY) < 15) {
                    e.preventDefault();
                }

                this.element.trigger('firstSlide.virb-lectric');
            }

            this.moved = true;
            this.lastPosition.x = this.position.x;
            this.lastPosition.y = this.position.y;
            this.lastMoveTime = new Date();

            this.position.x = this.limitXBounds(e.touches[0].pageX - this.startPosition.x);

            this.update({animate: false});
        },

        touchend: function(e) {
            window.removeEventListener('gesturestart', this, false);
            window.removeEventListener('gestureend', this, false);
            window.removeEventListener('touchmove', this, false);
            window.removeEventListener('touchend', this, false);

            if (this.moved) {
                var dx = this.position.x - this.lastPosition.x;
                var dt = (new Date()) - this.lastMoveTime + 1; 

                if (this.opts.tossing) {
                    var tossedX = this.limitXBounds(this.opts.tossFunction(this.position.x, dx, dt));
                    this.position.x = tossedX;
                } else {
                    this.position.x = this.position.x;
                }

                this.update();
                this.element.trigger('end.virb-lectric');
            } else {
                this.element.trigger('endNoSlide.virb-lectric');
            }

            this.currentTarget = undefined;
        },

        gesturestart: function(e) { 
            e.preventDefault();
            this.gesturing = true; 
        },

        gestureend: function(e) { 
            e.preventDefault();
            this.gesturing = false; 
        },

        webkitTransitionEnd: function(e) {
            this.element.trigger('animationEnd.virb-lectric');
        }
    };

    window.virb.$.fn.slider = Slider;
})(virb.$);
