(function(e){
    function i(b,a){
        var c=document.createElementNS("http://www.w3.org/2000/svg",b||"svg");
        a&&e.each(a,function(a,b){
            c.setAttributeNS(null,a,b)
        });
        return e(c)
    }
    e.fn.activity=function(b){
        this.each(function(){
            var a=e(this),c=a.data("activity");
            c&&(clearInterval(c.data("interval")),c.remove(),a.removeData("activity"));
            if(b!==false){
                b=e.extend({
                    color:a.css("color")
                },e.fn.activity.defaults,b);
                var c=k(a,b).css("position","absolute").prependTo(b.outside?"body":a),d=a.outerHeight()-c.height(),f=a.outerWidth()- c.width(),d=b.valign=="top"?b.padding:b.valign=="bottom"?d-b.padding:Math.floor(d/2),f=b.align=="left"?b.padding:b.align=="right"?f-b.padding:Math.floor(f/2),g=a.offset();
                b.outside?c.css({
                    top:g.top+"px",
                    left:g.left+"px"
                }):(d-=c.offset().top-g.top,f-=c.offset().left-g.left);
                c.css({
                    marginTop:d+"px",
                    marginLeft:f+"px"
                });
                h(c,b.segments,Math.round(10/b.speed)/10);
                a.data("activity",c)
            }
        });
        return this
    };

    e.fn.activity.defaults={
        segments:12,
        space:3,
        length:7,
        width:4,
        speed:1.2,
        align:"center",
        valign:"center",
        padding:4
    };
    e.fn.activity.getOpacity=function(b,a){
        var c=b.steps||b.segments-1,d=b.opacity!==void 0?b.opacity:1/c;
        return 1-Math.min(a,c)*(1-d)/c
    };

    var k=function(){
        return e("<div>").addClass("busy")
    },h=function(){};

    if(document.createElementNS&&document.createElementNS("http://www.w3.org/2000/svg","svg").createSVGRect)if(k=function(b,a){
        for(var c=a.width*2+a.space,d=c+a.length+Math.ceil(a.width/2)+1,f=i().width(d*2).height(d*2),g=i("g",{
            "stroke-width":a.width,
            "stroke-linecap":"round",
            stroke:a.color
        }).appendTo(i("g", {
            transform:"translate("+d+","+d+")"
        }).appendTo(f)),j=0;j<a.segments;j++)g.append(i("line",{
            x1:0,
            y1:c,
            x2:0,
            y2:c+a.length,
            transform:"rotate("+360/a.segments*j+", 0, 0)",
            opacity:e.fn.activity.getOpacity(a,j)
        }));
        return e("<div>").append(f).width(2*d).height(2*d)
    },document.createElement("div").style.WebkitAnimationName!==void 0)var l={},h=function(b,a,c){
        if(!l[a]){
            for(var d="spin"+a,f="@-webkit-keyframes "+d+" {",e=0;e<a;e++){
                var j=Math.round(1E5/a*e)/1E3,i=Math.round(1E5/a*(e+1)-1)/1E3,h="% { -webkit-transform:rotate("+ Math.round(360/a*e)+"deg); }\n";
                f+=j+h+i+h
            }
            f+="100% { -webkit-transform:rotate(100deg); }\n}";
            document.styleSheets[0].insertRule(f);
            l[a]=d
        }
        b.css("-webkit-animation",l[a]+" "+c+"s linear infinite")
    };else h=function(b,a,c){
        var d=0,e=b.find("g g").get(0);
        b.data("interval",setInterval(function(){
            e.setAttributeNS(null,"transform","rotate("+ ++d%a*(360/a)+")")
        },c*1E3/a))
    };
    else{
        var m=e("<shape>").css("behavior","url(#default#VML)");
        e("body").append(m);
        if(m.get(0).adj){
            var n=document.createStyleSheet();
            e.each(["group", "shape","stroke"],function(){
                n.addRule(this,"behavior:url(#default#VML);")
            });
            k=function(b,a){
                for(var c=a.width*2+a.space,d=(c+a.length+Math.ceil(a.width/2)+1)*2,f=-Math.ceil(d/2),f=e("<group>",{
                    coordsize:d+" "+d,
                    coordorigin:f+" "+f
                }).css({
                    top:f,
                    left:f,
                    width:d,
                    height:d
                }),g=0;g<a.segments;g++)f.append(e("<shape>",{
                    path:"m "+c+",0 l "+(c+a.length)+",0"
                }).css({
                    width:d,
                    height:d,
                    rotation:360/a.segments*g+"deg"
                }).append(e("<stroke>",{
                    color:a.color,
                    weight:a.width+"px",
                    endcap:"round",
                    opacity:e.fn.activity.getOpacity(a, g)
                })));
                return e("<group>",{
                    coordsize:d+" "+d
                }).css({
                    width:d,
                    height:d,
                    overflow:"hidden"
                }).append(f)
            };

            h=function(b,a,c){
                var d=0,e=b.get(0);
                b.data("interval",setInterval(function(){
                    e.style.rotation=++d%a*(360/a)
                },c*1E3/a))
            }
        }
        e(m).remove()
    }
})(jQuery);

(function($) {

    $.fn.extend({

        SmallerThan : function(other){
            var w = this.GetWidth();
            var h = this.GetHeight();
            return w < other.Size.Width && h < other.Size.Height;
        },

        LargerThan : function(other){
            var w = this.GetWidth();
            var h = this.GetHeight();
            return w > other.Size.Width && h > other.Size.Height;
        },

        TallerThan : function(other){
            var h = this.GetHeight();
            return h > other.Size.Height;
        },

        WiderThan : function(other){
            var w = this.GetWidth();
            return w > other.Size.Width;
        },

        hDiff: function(other) {
            return (this.GetWidth() + this.Size.Width) - other.GetWidth();
        },

        vDiff: function(other) {
            return (this.GetHeight() + this.Size.Height) - other.GetHeight();
        },

        Add : function(other) {
            this.Size.Width += other.Size.Width;
            this.Size.Height += other.Size.Height;
            return this;
        },

        Subtract : function(other) {
            this.Size.Width -= other.Size.Width;
            this.Size.Height -= other.Size.Height;
            return this;
        },

        Reduce : function(other){
            this.Size.Width = other.Size.Width - this.Size.Width;
            this.Size.Height = other.Size.Height - this.Size.Height;
            return this;
        },

        ShrinkWrap : function(){

            var w = this.Size.Width;
            var h = this.Size.Height;

            for(var i = 0; i < arguments.length; i++) {
                w += arguments[i].Size.Width;
                h += arguments[i].Size.Height;
            }

            this.css({
                "width" : w + "px",
                "height" : h + "px"
            });
            return this;
        },

        FitWithin : function(other){

            var w = other.GetWidth();
            var h = other.GetHeight();

            var mar = this.margin();
            var bor = this.border();
            var pad = this.padding();

            w -= (mar.left + mar.right + pad.left + pad.right + bor.left + bor.right);
            h -= (mar.top + mar.bottom + pad.top + pad.bottom + bor.top + bor.bottom);

            this.css({
                "width" : Math.ceil(w) + "px",
                "height" : Math.ceil(h) + "px"
            });

            return this;
        },

        FitWidth : function(other){

            var w = other.GetWidth();

            var mar = this.margin();
            var bor = this.border();
            var pad = this.padding();

            w -= (mar.left + mar.right + pad.left + pad.right + bor.left + bor.right);

            this.css({
                "width" : Math.ceil(w) + "px"
            });
            return this;
        },

        FitHeight : function(other){

            var h = other.GetHeight();

            var mar = this.margin();
            var bor = this.border();
            var pad = this.padding();

            h -= (mar.top + mar.bottom + pad.top + pad.bottom + bor.top + bor.bottom);

            this.css({
                "height" : Math.ceil(h) + "px"
            });
            return this;
        },

        SetSize : function(other){

            if(other == null)
                this.css({
                    "width" : this.Size.Width + "px",
                    "height" : this.Size.Height + "px"
                });
            else
                this.css({
                    "width" : other.Size.Width + "px",
                    "height" : other.Size.Height + "px"
                });
            return this;
        },

        SetWidth : function(w){

            if(w == null)
                this.css({
                    "width" : this.Size.Width + "px"
                });
            else
                this.css({
                    "width" : Math.round(w) + "px"
                });
            return this;
        },

        SetHeight : function(h){

            if(h == null)
                this.css({
                    "height" : this.Size.Height + "px"
                });
            else
                this.css({
                    "height" : Math.round(h) + "px"
                });
            return this;
        },

        Collapse : function(other){
            this.Size.Width = other.Size.Width + this.Size.Width;
            this.Size.Height = other.Size.Height + this.Size.Height;
            this.css({
                "width" : this.Size.Width + "px",
                "height" : this.Size.Height + "px"
            });
            return this;
        },

        Reset : function() {

            this.css({
                "margin-top": "",
                "margin-right": "",
                "margin-bottom": "",
                "margin-left": "",
                "display": "",
                "overflow": "",
                "width" : "",
                "height" : ""
            });

            return this;
        },

        Apply : function(other) {

            if(other == null)
                this.css({
                    "width" : this.Size.Width + "px",
                    "height" : this.Size.Height + "px"
                });
            else
                this.css({
                    "width" : other.Size.Width + "px",
                    "height" : other.Size.Height + "px"
                });
        },

        Resize : function(other){
            this.Size.Width = other.Size.Width;
            this.Size.Height = other.Size.Height;
        },

        MBP : function() {
            var mar = this.margin();
            var bor = this.border();
            var pad = this.padding();
            return {
                Width: Math.ceil(mar.right + mar.left + bor.right + bor.left + pad.right + pad.left),
                Height: Math.ceil(mar.top + mar.bottom + bor.top + bor.bottom + pad.top + pad.bottom),
                Aspect: function(){
                    return (this.Width / this.Height)
                }
            };
        },

        MBPW : function() {
            var mar = this.margin();
            var bor = this.border();
            var pad = this.padding();
            return {
                Width: Math.ceil(mar.right + mar.left + bor.right + bor.left + pad.right + pad.left + this.width()),
                Height: Math.ceil(mar.top + mar.bottom + bor.top + bor.bottom + pad.top + pad.bottom + this.height()),
                Aspect: function(){
                    return (this.Width / this.Height)
                }
            };
        },

        GetWidth : function() {
            var w = this.width();
            return w === 0 ? this[0].width : w;
        },

        GetHeight : function() {
            var h = this.height();
            return h === 0 ? this[0].height : h;
        }

    });


    $.fn.SmartImage = function(options, finishedCallback, perImageCallback) {

        var selector = this.selector;

        return this.each(function()
        {
            var element = $(this);

            // Return early if this element already has a plugin instance
            if (element.data('siObj') && options.cache)
                return;

            // pass options to plugin constructor
            var myplugin = new oSmartImage(element, selector, options, finishedCallback, perImageCallback);

            // Store plugin object in this element's data
            if(options.cache)
                element.data('siObj', myplugin);
        });
    };

    var oSmartImage = function($cont, selector, options, finishedCallback, perImageCallback) {

        var $main_loop = false;
        var $MainLoopTimer = false;

        var defaults = {
            frame:              ".frame",
            matte:              ".matte",
            image:              "img",
            metaAttr:           "smartimage",
            scale:              "AsIs",
            alignment:          "MiddleCenter",
            position:           "MiddleCenter",
            slideshow:          false,
            animationtype:      'fade',
            speed:              'slow',
            timeout:            5000,
            runningclass:       'smartimage',
            onBeforeTransition: false,
            playPauseSelector:  false,
            previousSelector:   false,
            nextSelector:       false,
            order:              'sequential', //sequential, random, fifo
            cache:              false,
            debug:              false,
            showLoader:         true,
            loaderIni:          {
                color: 'rgb(255,255,255)'
            },
            thumbnailNav:       false,
            thumbnailOptions:   {
                scrollerContainer:  '.scrollerContainer', 
                scroller:           '.scroller', 
                scrollerNextButton: '.scrollerNextButton',
                scrollerPrevButton: '.scrollerPrevButton',
                scrollEasing:       'easeOutCirc', 
                scrollSpeed:        600
            }
        };

        this.Pause = function () {
            clearTimeout($main_loop);
            $MainLoopTimer = false;
        }

        this.Resume = function (timeout) {
            timeout = typeof timeout == 'undefined' ? opts.timeout : timeout;
            $main_loop = setTimeout(nextImage, opts.timeout);
        }

        this.GoTo = function (index) {
            if(typeof index != 'undefined' && index < elements.length && index >= 0){
                clearTimeout($main_loop);
                $MainLoopTimer = false;
                elements[opts.last].frame.hide();
                elements[opts.current].frame.hide();
                $(opts.playPauseSelector).removeClass('playing').addClass('paused');
                opts.current = parseInt(index, 10);
                if (opts.current === 0) {
                    opts.last = (elements.length - 1);
                } else {
                    opts.last = opts.current - 1;
                }
                doTransition();
            }
        }

        var elements = [];
        var randomItems = [];

        var opts = $.extend({}, defaults, options || {}, $.metadata ? $cont.metadata() : $.meta ? $cont.data() : {});
        var meta = $.isFunction($cont.data) ? $cont.data(opts.metaAttr) : null;
        if (meta)
            opts = $.extend(opts, meta);

        opts.inTransition = false;

        if(opts.showLoader){
            $cont.activity(opts.loaderIni);
        }

        if(opts.thumbnailNav){

            var $thumbnailNav = $(opts.thumbnailNav);
            var $scrollerContainer = $thumbnailNav.children(opts.thumbnailOptions.scrollerContainer);
            var $scroller = $thumbnailNav.children(opts.thumbnailOptions.scrollerContainer).children(opts.thumbnailOptions.scroller);
            var $scrollerNextButton = $thumbnailNav.children(opts.thumbnailOptions.scrollerNextButton);
            var $scrollerPrevButton = $thumbnailNav.children(opts.thumbnailOptions.scrollerPrevButton);
            //set scroller width

            $scrollerContainer.css("width",999999);
            var totalWidth = $scroller.outerWidth(true);
            $scrollerContainer.css("width",totalWidth);

            var totalHeight = $scroller.outerHeight(true); //scroller height
            //do the scrolling
            if(totalWidth>$thumbnailNav.width() || totalHeight>$thumbnailNav.height()){ //needs scrolling

                $scrollerPrevButton.hide();
                $scrollerNextButton.show();

                $scrollerNextButton.click(function(e){ //next button
                    e.preventDefault();
                    var posX = $scroller.position().left;
                    var diffX = totalWidth + (posX - $thumbnailNav.width());
                    $scrollerPrevButton.stop().show("fast");
                    if(diffX >= $thumbnailNav.width()){
                        $scroller.stop().animate({
                            left:"-=" + $thumbnailNav.width()
                        }, opts.thumbnailOptions.scrollSpeed, opts.thumbnailOptions.scrollEasing, function(){
                            if(diffX == $thumbnailNav.width()){
                                $scrollerNextButton.stop().hide("fast");
                            }
                        });
                    } else {
                        $scrollerNextButton.stop().hide("fast");
                        $scroller.stop().animate({
                            left:$thumbnailNav.width() - totalWidth
                        }, opts.thumbnailOptions.scrollSpeed, opts.thumbnailOptions.scrollEasing);
                    }

                    clearTimeout($main_loop);
                    $MainLoopTimer = false;
                    $(opts.playPauseSelector).removeClass('playing').addClass('paused');

                });
                $scrollerPrevButton.click(function(e){ //previous button
                    e.preventDefault();
                    var posX = $scroller.position().left;
                    $scrollerNextButton.stop().show("fast");
                    if(posX + $thumbnailNav.width() <= 0){
                        $scroller.stop().animate({
                            left:"+="+$thumbnailNav.width()
                        },opts.thumbnailOptions.scrollSpeed, opts.thumbnailOptions.scrollEasing, function(){
                            if(posX + $thumbnailNav.width() == 0){
                                $scrollerPrevButton.stop().hide("fast");
                            }
                        });
                    } else {
                        $scrollerPrevButton.stop().hide("fast");
                        $scroller.stop().animate({
                            left:0
                        }, opts.thumbnailOptions.scrollSpeed, opts.thumbnailOptions.scrollEasing);
                    }

                    clearTimeout($main_loop);
                    $MainLoopTimer = false;
                    $(opts.playPauseSelector).removeClass('playing').addClass('paused');

                });
            } else {
                //no scrolling needed
                $scrollerPrevButton.add($scrollerNextButton).hide(); //hide buttons
            }

            $(".thumbs_wrap", $thumbnailNav).click(function() {
                var i = $(this).index();
                var myplugin = $cont.data('siObj');
                myplugin.GoTo(i);
            });
        }

        $(opts.playPauseSelector).click(function(){
            if($MainLoopTimer) {
                clearTimeout($main_loop);
                $MainLoopTimer = false;
                $(this).removeClass('playing').addClass('paused');
            }
            else {
                $main_loop = setTimeout(nextImage, 500);
                $(this).removeClass('paused').addClass('playing');
            }
        });

        function setNextSlide() {

            switch (opts.order) {
                case 'sequential':
                    break;
                case 'random':
                    break;
                case 'fifo':
                    break;
                default:
            }
            if (opts.current === (elements.length - 1)) {
                opts.current = 0;
                opts.last = (elements.length - 1);
            } else {
                opts.current = opts.current + 1;
                opts.last = opts.current - 1;
            }
        }

        function setPreviousSlide() {

            switch (opts.order) {
                case 'sequential':
                    if(!ele) {
                        timeout = 100;
                        $main_loop = setTimeout(nextImage, timeout);
                        return;
                    }
                    break;
                case 'random':
                case 'fifo':
                    break;
                default:
            }


            if (opts.current === 0) {
                opts.current = elements.length - 1;
                opts.last = 0;
            }
            else if(opts.current === 1) {
                opts.current = 0;
                opts.last = elements.length - 1;
            }
            else {
                opts.current = opts.current - 1;
                opts.last = opts.current - 1;
            }
        }

        $(opts.previousSelector).click(function(){
            elements[opts.current].frame.hide();
            elements[opts.last].frame.hide();
            if($MainLoopTimer) {
                clearTimeout($main_loop);
                $MainLoopTimer = false;
                $(opts.playPauseSelector).removeClass('playing').addClass('paused');
            }
            if (opts.current === 0) {
                opts.current = elements.length - 1;
                opts.last = 0;
            }
            else if(opts.current === 1) {
                opts.current = 0;
                opts.last = elements.length - 1;
            }
            else {
                opts.current = opts.current - 1;
                opts.last = opts.current - 1;
            }
            doTransition();
        });

        $(opts.nextSelector).click(function(){
            elements[opts.current].frame.hide();
            elements[opts.last].frame.hide();
            if($MainLoopTimer) {
                clearTimeout($main_loop);
                $MainLoopTimer = false;
                $(opts.playPauseSelector).removeClass('playing').addClass('paused');
            }
            if (opts.current === (elements.length - 1)) {
                opts.current = 0;
                opts.last = (elements.length - 1);
            } else {
                opts.current = opts.current + 1;
                opts.last = opts.current - 1;
            }
            doTransition();
        });

        opts.container = selector;

        finishedCallback = finishedCallback || $.noop;

        if (!$.isFunction(finishedCallback)) {
            $.error('An invalid finishedCallback function was supplied.');
        }

        perImageCallback = perImageCallback || $.noop;

        if (!$.isFunction(perImageCallback)) {
            $.error('An invalid perImageCallback function was supplied.');
        }

        onBeforeTransition = opts.onBeforeTransition || $.noop;

        if (!$.isFunction(onBeforeTransition)) {
            $.error('An invalid onBeforeTransition value was supplied.');
        }

        opts.imgCnt = 0;

        opts.pathToImg = opts.frame + " " + opts.matte + " " + opts.image;

        this.UpdateSlideShow = function() {
            $(opts.pathToImg, $cont).each(function(){
                doImage($(this));
            });
            elements[opts.current].frame.show();
        }

        opts.scaleFunc = $.fn.SmartImage.scale[opts.scale];

        if ( ! $.isFunction(opts.scaleFunc)) {
            $.error('Unknown scale function: ' + opts.scale + '. Unable to resize images.');
        }

        opts.alignFunc = $.fn.SmartImage.alignment[opts.alignment];

        if ( ! $.isFunction(opts.alignFunc)) {
            $.error('Unknown alignment function: ' + opts.alignment + '. Unable to resize images.');
        }

        opts.effectFunc = $.fn.SmartImage.effect[opts.animationtype];

        if ( ! $.isFunction(opts.effectFunc)) {
            $.error('Unknown effect function: ' + opts.animationtype + '. Unable to transition images.');
        }

        opts.positionFunc = $.fn.SmartImage.position[opts.position];

        if ( ! $.isFunction(opts.positionFunc)) {
            $.error('Unknown position function: ' + opts.alignment + '. Unable to resize images.');
        }

        $cont.css('position', 'relative').addClass(opts.runningclass);

        var $images = $(opts.pathToImg, $cont);

        var imgCnt = $images.length;

        var blank = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==';

        function imgLoaded( event ) {
            if ( event.target.src !== blank ){
                setTimeout( function(){
                    doImage($(event.target));
                }, 0 );
                $(event.target).unbind( 'load error', imgLoaded );
            }
        }

        $images.bind( 'load error', imgLoaded ).each( function(index) {
            $(this).data('ordinal', index);
            if (this.complete || typeof this.complete == "undefined"){
                var src = this.src;
                this.src = blank;
                this.src = src;
            }
        });

        var doTransition = function() {
            if(elements[opts.last] && elements[opts.current]){
                var lastEle = elements[opts.last].frame;
                var currentEle = elements[opts.current].frame;
                onBeforeTransition.call(elements[opts.current], lastEle, currentEle, opts);
                opts.inTransition = true;
                opts.effectFunc.call(elements, lastEle, currentEle, opts);
                doThumbnailUpdate();
                opts.inTransition = false;
            }
        };

        var doThumbnailUpdate = function() {
            if(opts.thumbnailNav) {
                var $thumbNav = $(opts.thumbnailNav);
                var $scrollerNextButton = $thumbNav.children(opts.thumbnailOptions.scrollerNextButton);
                var $scrollerPrevButton = $thumbNav.children(opts.thumbnailOptions.scrollerPrevButton);
                $(".thumbs_wrap:not([data-item='" + (opts.current + 1) + "'])", $thumbNav).removeClass('current-thumbnail');
                var $thumb = $(".thumbs_wrap[data-item='" + (opts.current + 1) + "']", $thumbNav);
                $thumb.addClass('current-thumbnail');
                var midPoint = ($thumbNav.width() / 2) - ($thumb.width() / 2);
                var $scroller = $thumbnailNav.children(opts.thumbnailOptions.scrollerContainer).children(opts.thumbnailOptions.scroller);
                if($thumb.position().left < midPoint ){
                    $scroller.stop().animate({
                        left: '0'
                    }, 
                    opts.thumbnailOptions.scrollSpeed, 
                    opts.thumbnailOptions.scrollEasing,
                    function(){
                        $scrollerNextButton.show();
                        $scrollerPrevButton.hide();
                    });
                }
                else if($thumb.position().left > ($scroller.width() - midPoint)) {
                    $scrollerNextButton.hide();
                    $scrollerPrevButton.show();
                }
                else {
                    $scroller.stop().animate({
                        left: '-' + ($thumb.position().left - midPoint) + "px"
                    }, 
                    opts.thumbnailOptions.scrollSpeed, 
                    opts.thumbnailOptions.scrollEasing,
                    function(){
                        $scrollerNextButton.show();
                        $scrollerPrevButton.show();
                    }); 
                }
            }
        }

        var findNextElement = function(id, increasing) {
            var step = increasing ? 1 : -1;
            for(var i = id + step; i >= 0 && i <= imgCnt; i += step)
                if( elements[id] )
                    return elements[id];
            return false;
        }

        var nextImage = function(){
            var lastEle, currentEle, timeout, ele;
            if(elements.length === 0) {
                timeout = 100;
            }
            else if(elements.length === 1) {
                ele = elements[0];
                switch (opts.order) {
                    case 'sequential':
                        if(ele) {
                            lastEle = ele.frame;
                            currentEle = ele.frame;
                        }
                        else {
                            timeout = 100;
                            $main_loop = setTimeout(nextImage, timeout);
                            return;
                        }
                        break;
                    case 'random':
                        ele = findNextElement(0, true);
                        lastEle = ele.frame;
                        currentEle = ele.frame;
                        break;
                    case 'fifo':
                        lastEle = ele.frame;
                        currentEle = ele.frame;
                        break;
                    default:
                }
                onBeforeTransition.call(elements[0], lastEle, currentEle, opts);
                currentEle.show();
            }
            else {
                $MainLoopTimer = true;
                timeout = opts.timeout;

                doTransition();

                if ((opts.current + 1) < elements.length) {
                    opts.current = opts.current + 1;
                    opts.last = opts.current - 1;
                } else {
                    opts.current = 0;
                    opts.last = elements.length - 1;
                }

                ele = elements[opts.current];

                switch (opts.order) {
                    case 'sequential':
                        if(!ele) {
                            timeout = 100;
                            $main_loop = setTimeout(nextImage, timeout);
                            return;
                        }
                        break;
                    case 'random':
                    case 'fifo':
                        break;
                    default:
                }
            }
            $main_loop = setTimeout(nextImage, timeout);
        }

        if(opts.slideshow) {

            if(opts.order == 'random'){
                for(var i = 0; i < imgCnt; i++)
                    randomItems.push(i);
                randomItems = shuffle(randomItems);
            }
            opts.current = 0;
            opts.last = 0;
            setTimeout(nextImage, 100);
        }
        else {

        }

        function shuffle(v){
            for(var j, x, i = v.length; i; j = parseInt(Math.random() * i), x = v[--i], v[i] = v[j], v[j] = x);
            return v;
        };

        function doImage($theImage){

            var item = {};

            item.container = $theImage.parents(opts.container);
            item.container.toString = function(){
                return this.Size.Width + " x " + this.Size.Height
            };

            item.frame = $theImage.parents(opts.frame).Reset();

            item.frame.toString = function(){
                return this.Size.Width + " x " + this.Size.Height
            };

            item.matte = $theImage.parents(opts.matte).Reset();
            item.matte.toString = function(){
                return this.Size.Width + " x " + this.Size.Height
            };

            item.image = $theImage.Reset();
            item.image.toString = function(){
                return this.Size.Width + " x " + this.Size.Height
            };

            item.boundingbox = {};
            item.boundingbox.toString = function(){
                return this.Size.Width + " x " + this.Size.Height
            };

            item.container.Size = Size(item.container);

            item.frame.Size = mbp(item.frame);

            var bWidth = item.frame.Size.Width;
            var bHeight = item.frame.Size.Height;

            item.matte.Size = mbp(item.matte);

            bWidth += item.matte.Size.Width;
            bHeight += item.matte.Size.Height;

            item.image.Size = mbp(item.image);

            item.naturalImg = {};
            item.naturalImg.toString = function(){
                return this.Size.Width + " x " + this.Size.Height
            };

            item.naturalImg.Size = {
                Width: parseInt(item.image[0].width) ,
                Height: parseInt(item.image[0].height),
                Aspect: function(){
                    return (this.Width / this.Height)
                }
            };

            bWidth += item.image.Size.Width;
            bHeight += item.image.Size.Height;

            item.boundingbox.Size = {
                Width: parseInt(item.container.width() - bWidth) ,
                Height: parseInt(item.container.height() - bHeight),
                Aspect: function(){
                    return (this.Width / this.Height)
                }
            };

            debug(opts, item);

            opts.scaleFunc.call(item, opts);
            opts.alignFunc.call(item, opts);
            opts.positionFunc.call(item, opts);

            opts.imgCnt++;

            item.frame.css('z-index', String(opts.imgCnt)).css('position', 'absolute');

            perImageCallback.call(item, opts);

            if(opts.slideshow) {
                item.frame.hide();
                switch (opts.order) {
                    case 'sequential':
                        var idx = parseInt(item.image.data('ordinal'), 10);
                        item.frame.css('z-index', String(idx + 1)).css('position', 'absolute');
                        elements[idx] = item;
                        break;
                    case 'random':
                        var idx = randomItems.pop()
                        item.frame.css('z-index', String(idx + 1)).css('position', 'absolute');
                        elements[idx] = item;
                        break;
                    case 'fifo':
                        item.frame.css('z-index', String(opts.imgCnt)).css('position', 'absolute');
                        elements.push(item);
                        break;
                    default:
                        $.error('Unknown order: ' + opts.order);
                }
            }

            if(opts.imgCnt >= imgCnt){
                $cont.activity(false);
                finishedCallback.call($cont, opts);
            }
        }

    };

    function removeFilter(element) {
        if(element.style.removeAttribute){
            element.style.removeAttribute('filter');
        }
    }

    var mbp = function(ele) {
        var mar = ele.margin();
        var bor = ele.border();
        var pad = ele.padding();
        return {
            Width: Math.ceil(mar.right + mar.left + bor.right + bor.left + pad.right + pad.left),
            Height: Math.ceil(mar.top + mar.bottom + bor.top + bor.bottom + pad.top + pad.bottom),
            Aspect: function(){
                return (this.Width / this.Height)
            }
        };
    };

    var Size = function(ele) {
        return {
            Width: parseInt((ele.is("img") ? ele[0].width : ele.width()) , 10),
            Height: parseInt((ele.is("img") ? ele[0].height : ele.height()) , 10),
            Aspect: function(){
                return (this.Width / this.Height)
            }
        };
    };

    function debug(opts, s) {
        opts.debug && log(s);
    };

    function log() {

        var out = "";

        for(var i = 0; i < arguments.length; i++)
            for(var arg in arguments[i])
                if($.isFunction(arguments[i][arg])) {
                    var zxzxzx = arguments[i][arg];
                    out +=   arg + ": " +  zxzxzx.name + " \n";
                }
                else {
                    out +=   arg + ": " + arguments[i][arg] + " \n";
                }

        window.console && console.log && console.log(out);
    };

    jQuery.fn.SmartImage.effect = {};

    jQuery.fn.SmartImage.alignment = {};

    jQuery.fn.SmartImage.scale = {};

    jQuery.fn.SmartImage.position = {};

    jQuery.fn.SmartImage.effect.fade = function(outEle, inEle, opts){
        outEle.fadeOut(opts.speed);
        inEle.fadeIn(opts.speed, function() {
            removeFilter($(this)[0]);
            opts.inTransition = false;
        });
    }

    jQuery.fn.SmartImage.effect.slide = function(outEle, inEle, opts){
        outEle.slideUp(opts.speed);
        inEle.slideDown(opts.speed, function() {
            opts.inTransition = false;
        });
    }

    jQuery.fn.SmartImage.position.TopLeft = function(opts) {

        this.frame.css({
            "margin-left": "0",
            "margin-top": "0"
        });

    };

    jQuery.fn.SmartImage.position.TopCenter = function(opts) {

        // frame is not as wide as the container
        if( this.frame.hDiff(this.container) < 0) {
            this.frame.css({
                "margin-left": Math.round((this.container.innerWidth() - this.frame.outerWidth()) / 2) + "px"
            });
        }
        // frame is not as tall as the container
        if( this.frame.vDiff(this.container) < 0) {
            this.frame.css({
                "margin-top": "0"
            });
        }

    };

    jQuery.fn.SmartImage.position.TopRight = function(opts) {

        this.frame.css({
            "margin-left": Math.round((this.container.innerWidth() - this.frame.outerWidth())) + "px",
            "margin-top": "0"
        });

    };

    jQuery.fn.SmartImage.position.MiddleLeft = function(opts) {

        this.frame.css({
            "margin-left": "0",
            "margin-top": Math.round((this.container.innerHeight() - this.frame.outerHeight() ) / 2) + "px"
        });
    };

    jQuery.fn.SmartImage.position.MiddleCenter = function(opts) {

        // frame is not as wide as the container
        if( this.frame.hDiff(this.container) < 0) {
            this.frame.css({
                "margin-left": Math.round((this.container.innerWidth() - this.frame.outerWidth()) / 2) + "px"
            });
        }
        // frame is not as tall as the container
        if( this.frame.vDiff(this.container) < 0) {
            this.frame.css({
                "margin-top": Math.round((this.container.innerHeight() - this.frame.outerHeight() ) / 2) + "px"
            });
        }

    };

    jQuery.fn.SmartImage.position.MiddleRight = function(opts) {

        this.frame.css({
            "margin-left": Math.round((this.container.innerWidth() - this.frame.outerWidth())) + "px",
            "margin-top": Math.round((this.container.innerHeight() - this.frame.outerHeight() ) / 2) + "px"
        });

    };

    jQuery.fn.SmartImage.position.BottomLeft = function(opts) {

        this.frame.css({
            "margin-left": "0",
            "margin-top": Math.round((this.container.innerHeight() - this.frame.outerHeight() )) + "px"
        });
    };

    jQuery.fn.SmartImage.position.BottomCenter = function(opts) {

        this.frame.css({
            "margin-left": Math.round((this.container.innerWidth() - this.frame.outerWidth()) / 2) + "px",
            "margin-top": Math.round((this.container.innerHeight() - this.frame.outerHeight() )) + "px"
        });
    };

    jQuery.fn.SmartImage.position.BottomRight = function(opts) {

        this.frame.css({
            "margin-left": Math.round((this.container.innerWidth() - this.frame.outerWidth())) + "px",
            "margin-top": Math.round((this.container.innerHeight() - this.frame.outerHeight() )) + "px"
        });

    };

    jQuery.fn.SmartImage.alignment.TopLeft = function(opts) {

        this.image.css({
            "margin-left": "0",
            "margin-top": "0"
        });

    };

    jQuery.fn.SmartImage.alignment.TopCenter = function(opts) {
        if(this.image.WiderThan(this.boundingbox)) {
            this.image.css({
                "margin-left": Math.round((this.boundingbox.Size.Width - this.image.width()) / 2) + "px",
                "margin-top": "0"
            });

        }
        else{
            this.image.css({
                "margin-top": "0"
            });
        }

    };

    jQuery.fn.SmartImage.alignment.TopRight = function(opts) {
        if(this.image.WiderThan(this.boundingbox)) {
            this.image.css({
                "margin-left": Math.round((this.boundingbox.Size.Width - this.image.width())) + "px",
                "margin-top": "0"
            });

        }
        else{
            this.image.css({
                "margin-top": "0"
            });
        }

    };

    jQuery.fn.SmartImage.alignment.MiddleLeft = function(opts) {

        if(this.image.TallerThan(this.boundingbox)) {
            this.image.css({
                "margin-top": Math.round((this.boundingbox.Size.Height - this.image.height() ) / 2) + "px"
            });
        }

    };

    jQuery.fn.SmartImage.alignment.MiddleCenter = function(opts) {

        if(this.image.LargerThan(this.boundingbox)){

            this.image.css({
                "margin-left": Math.round((this.boundingbox.Size.Width - this.image.width()) / 2) + "px",
                "margin-top": Math.round((this.boundingbox.Size.Height - this.image.height() ) / 2) + "px"
            });

        }
        else if(this.image.WiderThan(this.boundingbox)) {
            this.image.css({
                "margin-left": Math.round((this.boundingbox.Size.Width - this.image.width()) / 2) + "px"
            });

        }
        else if(this.image.TallerThan(this.boundingbox)) {
            this.image.css({
                "margin-top": Math.round((this.boundingbox.Size.Height - this.image.height() ) / 2) + "px"
            });
        }

    };

    jQuery.fn.SmartImage.alignment.MiddleRight = function(opts) {


        if(this.image.WiderThan(this.boundingbox)) {
            this.image.css({
                "margin-left": Math.round((this.boundingbox.Size.Width - this.image.width())) + "px"
            });

        }


        if(this.image.TallerThan(this.boundingbox)) {
            this.image.css({
                "margin-top": Math.round((this.boundingbox.Size.Height - this.image.height() ) / 2) + "px"
            });
        }

    };

    jQuery.fn.SmartImage.alignment.BottomLeft = function(opts) {
        if(this.image.TallerThan(this.boundingbox)) {
            this.image.css({
                "margin-left": "0",
                "margin-top": Math.round((this.boundingbox.Size.Height - this.image.height())) + "px"
            });
        }
        else{
            this.image.css({
                "margin-left": "0"
            });
        }

    };

    jQuery.fn.SmartImage.alignment.BottomCenter = function(opts) {
        if(this.image.TallerThan(this.boundingbox)) {
            this.image.css({
                "margin-top": Math.round((this.boundingbox.Size.Height - this.image.height())) + "px"
            });
        }
        if(this.image.WiderThan(this.boundingbox)) {
            this.image.css({
                "margin-left": Math.round((this.boundingbox.Size.Width - this.image.width()) / 2) + "px"
            });
        }
    };

    jQuery.fn.SmartImage.alignment.BottomRight = function(opts) {
        if(this.image.TallerThan(this.boundingbox)) {
            this.image.css({
                "margin-top": Math.round((this.boundingbox.Size.Height - this.image.height())) + "px"
            });
        }
        if(this.image.WiderThan(this.boundingbox)) {
            this.image.css({
                "margin-left": Math.round((this.boundingbox.Size.Width - this.image.width())) + "px"
            });
        }
    };

    jQuery.fn.SmartImage.scale.AsIs = function(opts) {

        this.toString = function() {
            return "If set to \"AsIs\" the image is displayed at its native size. The frame and matte layers either obsure larger images or shrinkwrap smaller.";
        };

        if( this.image.SmallerThan(this.boundingbox) ){

            // shrink-to-fit

            this.frame.css({
                "display": "inline-block"
            });

            this.matte.css({
                "display": "inline-block"
            });

            this.image.css({
                "display": "inline-block"
            });

            // IE 7- quirks mode. turn on layout
            if (!$.support.boxModel) {

                this.frame.css({
                    "zoom": "1",
                    "*display": "inline"
                });

                this.matte.css({
                    "zoom": "1",
                    "*display": "inline"
                });

                this.image.css({
                    "zoom": "1",
                    "*display": "inline"
                });
            }

        }
        else if(this.image.LargerThan(this.boundingbox)){

            this.frame.css({
                "overflow": "hidden",
                "display": "block"
            }).FitWithin(this.container);


            this.matte.css({
                "overflow": "hidden",
                "display": "block"
            }).SetSize(this.boundingbox);


        }
        else if(this.image.WiderThan(this.boundingbox)) {

            this.frame.css({
                "overflow": "hidden",
                "display": "inline-block"
            }).FitWidth(this.container);

            this.matte.css({
                "overflow": "hidden",
                "display": "block"
            }).SetWidth(this.boundingbox.Size.Width);

            if (!$.support.boxModel) {

                this.frame.css({
                    "zoom": "1",
                    "*display": "inline"
                });

                this.matte.css({
                    "zoom": "1",
                    "*display": "inline"
                });

                this.image.css({
                    "zoom": "1",
                    "*display": "inline"
                });
            }

        }
        else if(this.image.TallerThan(this.boundingbox)) {
            this.frame.css({
                "overflow": "hidden",
                "display": "inline-block"
            }).FitHeight(this.container).SetWidth(this.image.width());

            this.matte.css({
                "overflow": "hidden",
                "display": "block"
            }).SetHeight(this.boundingbox.Size.Height);

            if (!$.support.boxModel) {

                this.frame.css({
                    "zoom": "1",
                    "*display": "inline"
                });

                this.matte.css({
                    "zoom": "1",
                    "*display": "inline"
                });

                this.image.css({
                    "zoom": "1",
                    "*display": "inline"
                });
            }
        }
    };

    jQuery.fn.SmartImage.scale.Stretch = function(opts) {
        this.toString = function() {
            return "If set to \"Stretch\" the image is set to the dimensions of the bounding box, likely resulting in a distorted image.";
        };

        this.image.Size.Width = this.boundingbox.Size.Width;
        this.image.Size.Height = this.boundingbox.Size.Height;

        this.image.Apply();

        this.matte.css({
            "overflow": "hidden",
            "display": "inline-block"
        });

        this.frame.css({
            "overflow": "hidden",
            "display": "inline-block"
        });

        // IE 7- quirks mode. turn on layout
        if (!$.support.boxModel) {

            this.frame.css({
                "zoom": "1",
                "*display": "inline"
            });

            this.matte.css({
                "zoom": "1",
                "*display": "inline"
            });

            this.image.css({
                "zoom": "1",
                "*display": "inline"
            });
        }
    };

    jQuery.fn.SmartImage.scale.Proportional = function(opts) {

        this.toString = function(){
            return "If set to \"Proportional\" the image will proportionally downscale or upscale to fit the dimensions of the bounding box.";
        };

        var width = this.image.GetWidth();
        var height = this.image.GetHeight();

        var ratio = Math.min((this.boundingbox.Size.Width / width), (this.boundingbox.Size.Height / height));
        width = Math.round(ratio * width);
        height = Math.round(ratio * height);

        this.image.Size.Width = width;
        this.image.Size.Height = height;

        this.image.Apply();

        this.matte.css({
            "overflow": "hidden",
            "display": "inline-block"
        });

        this.frame.css({
            "overflow": "hidden",
            "display": "inline-block"
        });

        // IE 7- quirks mode. turn on layout
        if (!$.support.boxModel) {

            this.frame.css({
                "zoom": "1",
                "*display": "inline"
            });

            this.matte.css({
                "zoom": "1",
                "*display": "inline"
            });

            this.image.css({
                "zoom": "1",
                "*display": "inline"
            });
        }
    };

    jQuery.fn.SmartImage.scale.CropToFit = function(opts) {

        var width = this.image.GetWidth();
        var height = this.image.GetHeight();

        this.toString = function(){
            return "If set to \"CropToFit\" the image will proportionally downscale or upscale to completely fill the bounding box, with the excess area cropped out.";
        };

        var ratio = Math.max((this.boundingbox.Size.Width / width), (this.boundingbox.Size.Height / height));

        width = Math.round(ratio * width);
        height = Math.round(ratio * height);

        this.image.Size.Width = width;
        this.image.Size.Height = height;

        this.image.Apply();

        this.matte.css({
            "overflow": "hidden",
            "display": "inline-block"
        }).SetHeight(this.boundingbox.Size.Height).SetWidth(this.boundingbox.Size.Width);

        this.frame.css({
            "overflow": "hidden",
            "display": "inline-block"
        }).FitHeight(this.container).FitWidth(this.container);
        ;

        // IE 7- quirks mode. turn on layout
        if (!$.support.boxModel) {

            this.frame.css({
                "zoom": "1",
                "*display": "inline"
            });

            this.matte.css({
                "zoom": "1",
                "*display": "inline"
            });

            this.image.css({
                "zoom": "1",
                "*display": "inline"
            });
        }
    };

    jQuery.fn.SmartImage.scale.DownscaleOnly = function(opts) {

        this.toString = function(){
            return "If set to \"DownscaleOnly\" the image will proportionally downscale if larger than the bounding box, but will not be upscaled if smaller.";
        };

        if( this.image.SmallerThan(this.boundingbox) ){

            // shrink-to-fit

            this.frame.css({
                "display": "inline-block"
            });

            this.matte.css({
                "display": "inline-block"
            });

            this.image.css({
                "display": "inline-block"
            });

        }
        else {

            var width = this.image.GetWidth();
            var height = this.image.GetHeight();

            var ratio = Math.min((this.boundingbox.Size.Width / width), (this.boundingbox.Size.Height / height));
            width = Math.round(ratio * width);
            height = Math.round(ratio * this.image.height);

            this.image.Size.Width = width;
            this.image.Size.Height = height;

            this.image.Apply();

            this.matte.css({
                "overflow": "hidden",
                "display": "inline-block"
            });

            this.frame.css({
                "overflow": "hidden",
                "display": "inline-block"
            });

        }

        // IE 7- quirks mode. turn on layout
        if (!$.support.boxModel) {

            this.frame.css({
                "zoom": "1",
                "*display": "inline"
            });

            this.matte.css({
                "zoom": "1",
                "*display": "inline"
            });

            this.image.css({
                "zoom": "1",
                "*display": "inline"
            });
        }
    };

})(jQuery);
