﻿/* It is assumed that all cards have identical dimensions */

//card slider plugin
$.fn.cardSlider = function (options) {
    var defaults = {
        duration: 500, //animation duration in ms for zoom and unzoom
        slideDuration: 1500, //animation duration for sliding one cardlength
        readMoreLinkText: 'Read more' //text shown in the read more link when zoomed in
    };

    var opts = $.extend(defaults, options);

    var $slider = $(this);
    var isUnZooming = false;
    var isSliding = false;
    var keepSliding = false;
    var $list = null;
    var $lastLi = null;
    var $zoom = null;
    var listRightBorderPos = 0;
    var $listWrapper = null;
    var distance = 0; //distance that animation should cover
    var wrapperWidth = 0;
    var contentWidth = 0;
    var start = {};
    var coupledImg = null;
    var cardSize = { x: null, y: null };
    var zoomSize = { x: null, y: null };

    try {
        fillSlider();
        initSlider();
    }
    catch (e) {
        //just in case a page hides slider :-)
    }

    $(window.document).click(function () { try { unZoom(); } catch (e) { } });



    //fills $slider with the necessary markup
    function fillSlider() {
        var readMore = '';
        var html = '<div class="slidebtn left"></div>';
        html += '<div class="slidebtn right"></div>';

        html += '<a class="zoom" href="">';
        html += '<div class="infotext">';
        html += '<div class="spike"></div>';
        html += '<div class="top"></div>';
        html += '<div class="contents">';
        html += '<div class="text"></div>';
        html += '<div class="linktext"></div>';
        html += '</div>';
        html += '<div class="bottom"></div>';
        html += '</div>';
        html += '<div class="imgwrapper"><div class="shadow"></div><img src="" /></div>';
        html += '<div class="close"></div>';
        html += '</a>';
        html += '<div class="listwrapper">';
        html += '<ul>';
        for (i = 0; i < opts.cards.length; i++) {
            if (opts.cards[i].src == '') continue;

            html += '<li>';
            html += '<img class="small reflect rheight31" src="' + HTMLDecode(opts.cards[i].src) + '" zoom="' + HTMLDecode(opts.cards[i].zoomedSrc) + '" />';

            if (opts.cards[i].readMoreLinkText != null) {
                readMore = opts.cards[i].readMoreLinkText
            } else {
                readMore = opts.readMoreLinkText;
            }

            html += '<div class="cardtext" cardlink="' + HTMLDecode(opts.cards[i].cardLink) + '" readMore="' + escapeHTML(readMore) + '">';
            if (opts.cards[i].cardText) html += HTMLDecode(opts.cards[i].cardText);
            html += '</div>';
            html += '<img src="' + HTMLDecode(opts.cards[i].zoomedSrc) + '" class="preLoad" />'; //preload zoomed image
            html += '</li>';
        }
        html += '</ul>'
        html += '</div>'

        $slider.html(html);
    }

    //decodes an html-encoded string
    function HTMLDecode(str) {
        return str.replace(/&lt;/gi, '<').replace(/&gt;/gi, '>');
    }


    function escapeHTML(str) {
        var $temp = $('<div></div>');
        return $temp.text(str).html();
    }


    //clears computed values of non-digits, except for '-', ',', and '.', e.g. -15,89px becomes -15,89
    function replaceNonDigits(str) {
        var regexp = new RegExp(/[^\d^\-\.\,]/ig);
        return str.replace(regexp, '');
    }


    //binds all necessary events and other stuff
    function initSlider() {
        if (opts.cards.length == 0) return; //if no cards then nothing to init

        //save the dimensions of the cards so that we won't have to calculate them more than once
        $('img.small:first', $slider).load(
            function () {
                var img = $(this);
                cardSize.x = img.outerWidth();
                cardSize.y = img.outerHeight();
            }
        );
        $('img.preLoad:first', $slider).load(
            function () {
                var img = $(this);
                zoomSize.x = img.outerWidth();
                zoomSize.y = img.outerHeight();
            }
        );

        $(window).load(
            function () {
                $list = $('ul', $slider)
                var li = $('li:first', $list);
                distance = li.next().offset().left - li.offset().left;
                $lastLi = $('li:last', $list);
                $listWrapper = $('div.listwrapper', $slider);
                listRightBorderPos = $listWrapper.offset().left + $listWrapper.width();
                $zoom = $('.zoom', $slider);

                //set events for active ctrls
                $('div.listwrapper li img', $slider).mouseenter(function (ev) { try { zoomIn(ev.target); } catch (e) { } });
                $zoom.mouseleave(function (ev) { try { unZoom(); } catch (e) { } });

                initSlideBtns();
                $slider.css('visibility', 'visible');
            }
        );
    }


    function centerSlider() {
        $listWrapper.css('width', contentWidth).addClass('centered');
    }


    function initSlideBtns() {
        contentWidth = parseInt($lastLi.offset().left) + parseInt($lastLi.width());
        contentWidth = contentWidth - $('li:first', $slider).offset().left;

        //only show slide buttons if there are some parts of a card that are hidden
        if (contentWidth > $listWrapper.width()) {
            $('div.slidebtn', $slider).mouseenter(
                function (ev) {
                    keepSliding = true;
                    //use a short timeout to prevent starting slide when just passing over a slide button on the way to a card
                    setTimeout(function () { continousSlide($(ev.target).hasClass('right')); }, 200);
                }
            ).mouseleave(
                function (ev) {
                    keepSliding = false;
                }
            );
        } else {
            $('div.slidebtn', $slider).remove();
            centerSlider();
        }
    }


    function continousSlide(moveRight) {
        if (moveRight) {
            if (($lastLi.offset().left + $lastLi.outerWidth()) <= listRightBorderPos) {
                keepSliding = false;
                $('.slidebtn.right', $slider).hide();
            } else {
                $('.slidebtn.left', $slider).show();
            }
        } else {
            if (replaceNonDigits($list.css('left')) >= 0) {
                keepSliding = false;
                $('.slidebtn.left', $slider).hide();
            } else {
                $('.slidebtn.right', $slider).show();
            }
        }

        if (!keepSliding) return;

        slide(moveRight, function () { continousSlide(moveRight); });
    }


    //slides the list left or right
    function slide(moveRight, callback) {
        //abort if we're already sliding
        if (isSliding) return;

        isSliding = true; //set flag to prevent bugs from excessive clicking on the slide buttons

        var targetPos = parseInt(replaceNonDigits($list.css('left')));

        if (moveRight) {
            targetPos = targetPos - distance;
            $list.animate(
				{ 'left': targetPos + 'px' },
				opts.slideDuration,
				'linear',
				function () {
				    slideHelper(callback);
				}
			);
        }
        else {
            targetPos = targetPos + distance;
            targetPos = targetPos > 0 ? 0 : targetPos;
            $list.animate(
				{ 'left': targetPos + 'px' },
				opts.slideDuration,
				'linear',
				function () {
				    slideHelper(callback);
				}
			);
        }
    }


    //helper function for slide() above
    function slideHelper(callback) {
        isSliding = false;
        if (callback) callback();
    }


    //shows the big version of the clicked card along with some extra info
    function zoomIn(img) {
        if (isSliding) return; //don't want to start zooming while the list is moving

        img = $(img);

        //if called without an img there's nothing to do so abort
        if (img.length == 0) return;

        $zoom.stop(true, true); //clear animation queue so that we can immediately start over with the currently hovered card

        var infotext = $('div.infotext', $zoom);

        //clear queued animations
        infotext.stop(true).hide();

        //$coupledImage = img; //save currently clicked image so we know where to unzoom to later on
        var cardtext = img.closest('div.small').siblings('div.cardtext');

        //update cardtext, and other attributes so that they match the clicked card
        $('div.text', infotext).empty().append(cardtext.contents().clone());
        $('div.linktext', infotext).text(cardtext.attr('readMore'));
        $('div.close, div.shadow', $zoom).hide();
        $('img', $zoom).attr('src', img.attr('zoom'));
        var imgwrapper = $('div.imgwrapper', $zoom);

        var width = cardSize.x + 'px';

        var top =
			img.offset().top -
			$slider.offset().top -
			imgwrapper.position().top +
			'px';

        var left =
			img.offset().left -
			$slider.offset().left -
			imgwrapper.position().left +
			'px';

        $zoom.css('visibility', 'hidden').css({
            'width': width,
            'top': top,
            'left': left,
            'height': 'auto'
        }
		).attr('href', cardtext.attr('cardlink'));


        //save starting dimensions so we won't have to calculate them again when we unzoom
        start.width = width;
        start.top = top;
        start.left = left;
        start.height = $zoom.height();

        $zoom.css('visibility', 'visible');

        $zoom.animate({
            'top': '-' + (((zoomSize.y - cardSize.y) / 2) + parseInt(imgwrapper.position().top)) + 'px',
            'left': (img.offset().left - $slider.offset().left - ((zoomSize.x - cardSize.x) / 2)) - parseInt(replaceNonDigits($zoom.css('padding-left'))) + 'px',
            'width': zoomSize.x + 'px'
        }, opts.duration, null, function () {
            $('div.shadow', $zoom).show();
            showInfotext(infotext);
        });
    }


    //shows the infotext and also adjusts its position relative to viewport
    function showInfotext(infotext) {
        infotext = $(infotext);
        var spike = $('div.spike', infotext);

        infotext.css('visibility', 'hidden').removeClass('horiFlipped').css({ 'top': '', 'margin-left': '', 'margin-top': '' }).show();
        spike.css('margin-top', '');

        var win = $(window), overlap;

        //adjust horizontal position
        if ((infotext.offset().left + infotext.outerWidth()) > (win.width() + win.scrollLeft())) { //if infotext is outside right side of viewport then reverse it and show all of it
            infotext.addClass('horiFlipped');

            overlap = (infotext.offset().left + infotext.outerWidth() + $('div.spike', infotext).outerWidth()) - (win.width() + win.scrollLeft());

            if (overlap > 0) {
                infotext.css('margin-left', '-' + overlap + 'px');
            }
        }

        //adjust vertical position
        if ((infotext.offset().top + infotext.outerHeight()) > (win.height() + win.scrollTop())) { //if infotext is outside bottom of viewport then show all of it
            overlap = (infotext.offset().top + infotext.outerHeight()) - (win.height() + win.scrollTop());

            overlap += 20; //add a few pixels so that it's not aligned exactly with the viewport

            if (overlap > 0) {
                infotext.css('top', infotext.position().top - overlap + 'px');
            }
        }

        //adjust spike position
        if (spike.offset().top < $zoom.offset().top) {
            spike.css('margin-top', (infotext.height() - spike.height()) + 'px')
        }

        infotext.css('visibility', 'visible');
    }


    //unzooms the expanded card and animates it down to the position of the coupled image
    function unZoom() {
        if (isUnZooming) return;
        isUnZooming = true; //card is currently being zoomed out. Lets the script finish the animation in peace if unZoom() is called from somewhere else

        $zoom.stop(true);

        var img = $(coupledImg);
        var infotext = $('div.infotext', $zoom);

        $('.shadow, .infotext', $zoom).hide();
        $zoom.css('height', 'auto')
			.animate({
			    'top': start.top,
			    'left': start.left,
			    'width': start.width,
			    'height': start.height
			}, opts.duration, null, function () {
			    $zoom.css('visibility', 'hidden').show();
			    //clear flags
			    isUnZooming = false;
			});

        $('img', $zoom).attr('src', img.attr('zoom'));
    }
}

