﻿//custom wrapper for elastislide plugin
// by :Dan Wellman @ Design Haus

$(function () {

    // http://www.netcu.de/jquery-touchwipe-iphone-ipad-library touchwipe plugin
    $.fn.touchwipe = function (settings) {

        var config = {
            min_move_x: 20,
            min_move_y: 20,
            wipeLeft: function () { },
            wipeRight: function () { },
            wipeUp: function () { },
            wipeDown: function () { },
            preventDefaultEvents: true
        };

        if (settings) $.extend(config, settings);

        this.each(function () {
            var startX;
            var startY;
            var isMoving = false;

            function cancelTouch() {
                this.removeEventListener('touchmove', onTouchMove);
                startX = null;
                isMoving = false;
            }

            function onTouchMove(e) {
                if (config.preventDefaultEvents) {
                    e.preventDefault();
                }
                if (isMoving) {
                    var x = e.touches[0].pageX;
                    var y = e.touches[0].pageY;
                    var dx = startX - x;
                    var dy = startY - y;
                    if (Math.abs(dx) >= config.min_move_x) {
                        cancelTouch();
                        if (dx > 0) {
                            config.wipeLeft();
                        }
                        else {
                            config.wipeRight();
                        }
                    }
                    else if (Math.abs(dy) >= config.min_move_y) {
                        cancelTouch();
                        if (dy > 0) {
                            config.wipeDown();
                        }
                        else {
                            config.wipeUp();
                        }
                    }
                }
            }

            function onTouchStart(e) {
                if (e.touches.length == 1) {
                    startX = e.touches[0].pageX;
                    startY = e.touches[0].pageY;
                    isMoving = true;
                    this.addEventListener('touchmove', onTouchMove, false);
                }
            }
            if ('ontouchstart' in document.documentElement) {
                this.addEventListener('touchstart', onTouchStart, false);
            }
        });

        return this;
    };

    // elastislide plugin http://tympanus.net/codrops/2011/09/12/elastislide-responsive-carousel/

    $.elastislide = function (options, element) {
        this.$el = $(element);
        this._init(options);
    };

    $.elastislide.defaults = {
        speed: 450, // animation speed
        //easing: '', // animation easing effect - disabled for this implementation
        imageW: 190, // the images width
        margin: 3, // image margin right
        border: 2, // image border
        minItems: 1, // the minimum number of items to show. 
        // when we resize the window, this will make sure minItems are always shown 
        // (unless of course minItems is higher than the total number of elements)
        current: 0, // index of the current item
        // when we resize the window, the carousel will make sure this item is visible 
        onClick: function () { return false; } // click item callback
    };

    $.elastislide.prototype = {
        _init: function (options) {

            this.options = $.extend(true, {}, $.elastislide.defaults, options);

            // <ul>
            this.$slider = this.$el.find('ul');

            // <li>
            this.$items = this.$slider.children('li');

            // total number of elements / images
            this.itemsCount = this.$items.length;

            // cache the <ul>'s parent, since we will eventually need to recalculate its width on window resize
            this.$esCarousel = this.$slider.parent();

            // validate options
            this._validateOptions();

            // set sizes and initialize some vars...
            this._configure();

            // add navigation buttons
            this._addControls();

            // initialize the events
            this._initEvents();

            // show the <ul>
            this.$slider.show();

            // slide to current's position
            this._slideToCurrent(false);

        },
        _validateOptions: function () {

            if (this.options.speed < 0)
                this.options.speed = 450;
            if (this.options.margin < 0)
                this.options.margin = 4;
            if (this.options.border < 0)
                this.options.border = 1;
            if (this.options.minItems < 1 || this.options.minItems > this.itemsCount)
                this.options.minItems = 1;
            if (this.options.current > this.itemsCount - 1)
                this.options.current = 0;

        },
        _configure: function () {

            // current item's index
            this.current = this.options.current;

            // the ul's parent's (div.es-carousel) width is the "visible" width
            this.visibleWidth = this.$esCarousel.width();

            // test to see if we need to initially resize the items
            if (this.visibleWidth < this.options.minItems * (this.options.imageW + 2 * this.options.border) + (this.options.minItems - 1) * this.options.margin) {
                this._setDim((this.visibleWidth - (this.options.minItems - 1) * this.options.margin) / this.options.minItems);
                this._setCurrentValues();
                // how many items fit with the current width
                this.fitCount = this.options.minItems;
            }
            else {
                this._setDim();
                this._setCurrentValues();
            }

            // set the <ul> width
            this.$slider.css({
                width: this.sliderW
            });

        },
        _setDim: function (elW) {

            // <li> style
            this.$items.css({
                marginRight: this.options.margin,
                width: (elW) ? elW : this.options.imageW + 2 * this.options.border
            }).children('a').css({ // <a> style
                borderWidth: this.options.border
            });

        },
        _setCurrentValues: function () {

            // the total space occupied by one item
            this.itemW = this.$items.outerWidth(true);

            // total width of the slider / <ul>
            // this will eventually change on window resize
            this.sliderW = this.itemW * this.itemsCount;

            // the ul parent's (div.es-carousel) width is the "visible" width
            this.visibleWidth = this.$esCarousel.width();

            // how many items fit with the current width
            this.fitCount = Math.floor(this.visibleWidth / this.itemW);

        },
        _addControls: function () {

            this.$navNext = $('<span class="es-nav-next">&#9658;</span>');
            this.$navPrev = $('<span class="es-nav-prev">&#9668;</span>');
            $('<div class="es-nav"/>')
			    .append(this.$navPrev)
			    .append(this.$navNext)
			    .appendTo(this.$el);

        },
        _toggleControls: function (dir, status) {

            // show / hide navigation buttons
            if (dir && status) {
                if (status === 1)
                    (dir === 'right') ? this.$navNext.show() : this.$navPrev.show();
                else
                    (dir === 'right') ? this.$navNext.hide() : this.$navPrev.hide();
            }
            else if (this.current === this.itemsCount - 1 || this.fitCount >= this.itemsCount)
                this.$navNext.hide();

        },
        _initEvents: function () {

            var instance = this;

            // window resize
            $(window).on('resize.elastislide', function (event) {

                instance._reload();

                // slide to the current element
                clearTimeout(instance.resetTimeout);
                instance.resetTimeout = setTimeout(function () {
                    instance._slideToCurrent();
                }, 200);

            });

            // navigation buttons events
            this.$navNext.on('click.elastislide', function (event) {
                instance._slide('right');
            });

            this.$navPrev.on('click.elastislide', function (event) {
                instance._slide('left');
            });

            // item click event
            this.$slider.on('click.elastislide', 'li', function (event) {
                instance.options.onClick($(this));
                return false;
            });

            // touch events
            instance.$slider.touchwipe({
                wipeLeft: function () {
                    instance._slide('right');
                },
                wipeRight: function () {
                    instance._slide('left');
                }
            });

        },
        reload: function (callback) {
            this._reload();
            if (callback) callback.call();

        },
        _reload: function () {

            var instance = this;

            // set values again
            instance._setCurrentValues();

            // need to resize items
            if (instance.visibleWidth < instance.options.minItems * (instance.options.imageW + 2 * instance.options.border) + (instance.options.minItems - 1) * instance.options.margin) {
                instance._setDim((instance.visibleWidth - (instance.options.minItems - 1) * instance.options.margin) / instance.options.minItems);
                instance._setCurrentValues();
                instance.fitCount = instance.options.minItems;
            }
            else {
                instance._setDim();
                instance._setCurrentValues();
            }

            instance.$slider.css({
                width: instance.sliderW + 10 // TODO: +10px seems to solve a firefox "bug" :S
            });

        },
        _slide: function (dir, val, anim, callback) {

            // if animating return
            //if( this.$slider.is(':animated') )
            //return false;

            // current margin left
            var ml = parseFloat(this.$slider.css('margin-left'));

            // val is just passed when we want an exact value for the margin left (used in the _slideToCurrent function)
            if (val === undefined) {

                // how much to slide?
                var amount = this.fitCount * this.itemW, val;

                if (amount < 0) return false;

                // make sure not to leave a space between the last item / first item and the end / beggining of the slider available width
                if (dir === 'right' && this.sliderW - (Math.abs(ml) + amount) < this.visibleWidth) {
                    amount = this.sliderW - (Math.abs(ml) + this.visibleWidth) - this.options.margin; // decrease the margin left
                    // show / hide navigation buttons
                    this._toggleControls('right', -1);
                    this._toggleControls('left', 1);
                }
                else if (dir === 'left' && Math.abs(ml) - amount < 0) {
                    amount = Math.abs(ml);
                    // show / hide navigation buttons
                    this._toggleControls('left', -1);
                    this._toggleControls('right', 1);
                }
                else {
                    var fml; // future margin left
                    (dir === 'right')
						? fml = Math.abs(ml) + this.options.margin + Math.abs(amount)
						: fml = Math.abs(ml) - this.options.margin - Math.abs(amount);

                    // show / hide navigation buttons
                    if (fml > 0)
                        this._toggleControls('left', 1);
                    else
                        this._toggleControls('left', -1);

                    if (fml < this.sliderW - this.visibleWidth)
                        this._toggleControls('right', 1);
                    else
                        this._toggleControls('right', -1);

                }

                (dir === 'right') ? val = '-=' + amount : val = '+=' + amount

            }
            else {
                var fml = Math.abs(val); // future margin left

                if (Math.max(this.sliderW, this.visibleWidth) - fml < this.visibleWidth) {
                    val = -(Math.max(this.sliderW, this.visibleWidth) - this.visibleWidth);
                    if (val !== 0)
                        val += this.options.margin; // decrease the margin left if not on the first position

                    // show / hide navigation buttons
                    this._toggleControls('right', -1);
                    fml = Math.abs(val);
                }

                // show / hide navigation buttons
                if (fml > 0)
                    this._toggleControls('left', 1);
                else
                    this._toggleControls('left', -1);

                if (Math.max(this.sliderW, this.visibleWidth) - this.visibleWidth > fml + this.options.margin)
                    this._toggleControls('right', 1);
                else
                    this._toggleControls('right', -1);

            }

            $.fn.applyStyle = (anim === undefined) ? $.fn.animate : $.fn.css;

            var sliderCSS = { marginLeft: val };

            var instance = this;

            this.$slider.stop().applyStyle(sliderCSS, $.extend(true, [], { duration: this.options.speed, easing: this.options.easing, complete: function () {
                if (callback) callback.call();
            }
            }));

        },
        _slideToCurrent: function (anim) {

            /* how much to slide?
            var amount = this.current * this.itemW;
            if ($.browser.msie && ($.browser.version === "8.0" || $.browser.version === "9.0")) { //fix position of elastislide in IE 8/9
                amount = amount + 40
            }
            */
            this._slide('', -amount, anim);

        },
        add: function ($newelems, callback) {

            // adds new items to the carousel
            this.$items = this.$items.add($newelems);
            this.itemsCount = this.$items.length;
            this._setDim();
            this._setCurrentValues();
            this.$slider.css({
                width: this.sliderW
            });
            this._slideToCurrent();

            if (callback) callback.call($newelems);

        },
        setCurrent: function (idx, callback) {
            if (this.current != idx) { //added this to prevent the plugin showing the wrong set of images in the elastislide plugin in IE
                this.current = idx;

                var ml = Math.abs(parseFloat(this.$slider.css('margin-left'))),
				    posR = ml + this.visibleWidth,
				    fml = Math.abs(this.current * this.itemW);

                if (fml + this.itemW > posR || fml < ml) {
                    this._slideToCurrent();
                }

                if (callback) callback.call();
            }

        },
        destroy: function (callback) {

            this._destroy(callback);

        },
        _destroy: function (callback) {
            this.$el.off('.elastislide').removeData('elastislide');
            $(window).off('.elastislide');
            if (callback) callback.call();
        }
    };

    var logError = function (message) {
        if (this.console) {
            console.error(message);
        }
    };

    $.fn.elastislide = function (options) {
        if (typeof options === 'string') {
            var args = Array.prototype.slice.call(arguments, 1);

            this.each(function () {
                var instance = $.data(this, 'elastislide');
                if (!instance) {
                    logError("cannot call methods on elastislide prior to initialization; " +
					"attempted to call method '" + options + "'");
                    return;
                }
                if (!$.isFunction(instance[options]) || options.charAt(0) === "_") {
                    logError("no such method '" + options + "' for elastislide instance");
                    return;
                }
                instance[options].apply(instance, args);
            });
        }
        else {
            this.each(function () {
                var instance = $.data(this, 'elastislide');
                if (!instance) {
                    $.data(this, 'elastislide', new $.elastislide(options, this));
                }
            });
        }
        return this;
    };

    var $rgGallery = $('#ryaGallery'), // gallery container
    	$esCarousel = $rgGallery.find('div.es-carousel-wrapper'), // carousel container
    	$items = $esCarousel.find('ul > li'), // the carousel items
    	itemsCount = $items.length; // total number of items

    Gallery = (function () {

        var current = 0, // index of the current item
            anim = false,
            $navPrev = $rgGallery.find('a.rg-image-nav-prev'),
			$navNext = $rgGallery.find('a.rg-image-nav-next'),
			$imgWrapper = $rgGallery.find('div.rg-image'),

            init = function () {

                // add large image wrapper
                _addImageWrapper();

                _initCarousel();

            },

            _initCarousel = function () {

                // we are using the elastislide plugin:
                // http://tympanus.net/codrops/2011/09/12/elastislide-responsive-carousel/
                $esCarousel.show().elastislide({
                    imageW: 145,
                    minItems: 3,
                    margin: 6,
                    onClick: function ($item) {
                        if (anim) return false;

                        anim = true;

                        // on click show image
                        _showImage($item);

                        current = $item.index();

                        // set elastislide's current to current
                        $esCarousel.elastislide('setCurrent', current);
                    }
                });
            },

            _addImageWrapper = function () {

                // adds navigation events for large images

                if (itemsCount > 1) {

                    $navPrev.on('click.rgGallery', function (event) {
                        _navigate('left');
                        return false;
                    });

                    $navNext.on('click.rgGallery', function (event) {
                        _navigate('right');
                        return false;
                    });

                    // add touchwipe events on the large image wrapper
                    $imgWrapper.touchwipe({
                        wipeLeft: function () {
                            _navigate('right');
                        },
                        wipeRight: function () {
                            _navigate('left');
                        },
                        preventDefaultEvents: false
                    });

                    // add keyboard events on the large image wrapper
                    $(document).on('keyup.rgGallery', function (event) {
                        if (event.keyCode == 39) {
                            _navigate('right');
                        } else if (event.keyCode == 37) {
                            _navigate('left');
                        }
                    });
                }
            },

            _navigate = function (dir) {

                // navigate through the large images

                if (anim) return false;
                anim = true;

                if (dir === 'right') {
                    if (current + 1 >= itemsCount) {
                        current = 0;
                    } else {
                        ++current;
                    }
                } else if (dir === 'left') {
                    if (current - 1 < 0) {
                        current = itemsCount - 1;
                    } else {
                        --current;
                    }
                }

                _showImage($items.eq(current));

            },

            _showImage = function ($item) {

                // shows the large image that is associated to the $item

                var $loader = $rgGallery.find('div.rg-loading').show();

                $items.removeClass('selected');
                $item.addClass('selected');

                var $thumb = $item.find('img'),
					largesrc = $thumb.attr('src');

                $('<img/>').load(function () {
                    $rgGallery.find('div.rg-image').empty().append('<img src="' + largesrc + '"/>')

                    $loader.hide();

                    // show nav
                    $navPrev.show();
                    $navNext.show();


                    //$esCarousel.elastislide('reload');
                    $esCarousel.elastislide('setCurrent', current);

                    anim = false;
                }).attr('src', largesrc);

            };

        return {
            init: init
        };

    })();

    Gallery.init();

});
