/*    08/24/2010
		PikaChoose
	Jquery plugin for photo galleries
    Copyright (C) 2010 Jeremy Fry

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
(function($) {
    /**
     * Creates a slideshow for all matched elements.
     *
     * @example $("#pikame").PikaChoose();
     * @input needed <ul id="pikame"><li><img src="first.jpg"><span>Caption</span></li><li><img src="second.jpg"><span>Caption</span></li></ul>
     *
     * @method PikaChoose
     * @return $
     * @param o {Hash|String} A set of key/value pairs to set as configuration properties or a method name to call on a formerly created instance.
     */
    var defaults = {
        autoPlay: true,
        speed: 6000,
        text: {
            play: "",
            stop: "",
            previous: "Previous",
            next: "Next"
        },
        transition:[-1],
        showCaption: false,
        IESafe: false
    };

    $.fn.PikaChoose = function(o) {
        return this.each(function() {
            $(this).data('pikachoose', new $pc(this, o));
        });
    }

    /**
     * The PikaChoose object.
     *
     * @constructor
     * @class pikachoose
     * @param e {HTMLElement} The element to create the carousel for.
     * @param o {Object} A set of key/value pairs to set as configuration properties.
     * @cat Plugins/PikaChoose
     */
    $.PikaChoose = function(e, o) {
        this.options    = $.extend({}, defaults, o || {});
        this.list     	= null;
        this.image  	= null;
        this.anchor		= null;
        this.caption	= null;
        this.imgNav		= null;
        this.imgPlay 	= null;
        this.imgPrev	= null;
        this.imgNext 	= null;
        this.textNext	= null;
        this.textPrev	= null;
        this.previous  = null;
        this.next 		= null;
        this.aniDiv		= null;
        this.thumbs		= null;
        this.transition= null;
        this.active		= null;
        this.animating	= false;
        if (e.nodeName == 'UL' || e.nodeName == 'OL') {
            this.list = $(e);
            this.build();
            this.bindEvents();
        }else{
            return;
        }

        var y = 0;
        var x = 0;
        for(var t = 0; t<25;t++)
        {
            var a = '<div col="'+y+'" row="'+x+'"></div>';
            this.aniDiv.append(a);
            y++
            if(y == 5)
            {
                x++;
                y=0;
            }
        }

    }//end PikaChoose function(e, o)

    var $pc = $.PikaChoose;
    $pc.fn = $pc.prototype = {
        pikachoose: '4.0.3'
    };

    $pc.fn.extend = $pc.extend = $.extend;

    $pc.fn.extend({
        /**
         * Builds the gallery structure.
         *
         * @method build
         * @return undefined
         */
        build: function() {
            this.step 		= 0; //transition step count
            //create the structure for pikachoose
            this.wrap 		= $("<div class='pika-image'></div>").insertBefore(this.list);
            this.image 		= $("<img>").appendTo(this.wrap);
            this.anchor		= this.image.wrap("<a>").parent();
            this.imgNav 	= $("<div class='pika-imgnav'></div>").insertAfter(this.anchor);
            this.imgPlay 	= $("<a></a>").appendTo(this.imgNav);
            if(this.options.autoPlay){
                this.imgPlay.addClass('pause');
            }else{
                this.imgPlay.addClass('play');
            }
            this.imgPrev 	= $("<a class='previous'></a>").insertAfter(this.imgPlay);
            this.imgNext 	= $("<a class='next'></a>").insertAfter(this.imgPrev);
            this.caption 	= $("<div class='caption'></div>").insertAfter(this.imgNav);
            if(!this.options.showCaption){
                this.caption.hide();
            }
            this.aniDiv		= $("<div class='animation'></div>").insertAfter(this.caption);
            this.textNav 	= $("<div class='pika-textnav'></div>").insertAfter(this.aniDiv);
            this.textPrev 	= $("<a class='previous'>"+this.options.text.previous+"</a>").appendTo(this.textNav);
            this.textNext	= $("<a class='next'>"+this.options.text.next+"</a>").appendTo(this.textNav);
            this.list.addClass('pika-thumbs');
            this.list.children('li').wrapInner("<div class='clip' />");
            this.thumbs = this.list.find('img');
            this.active		= this.thumbs.eq(0);

            //fill in info for first image
            this.finishAnimating({
                'source':this.active.attr('ref') || this.active.attr('src'),
                'caption':this.active.parents('li:first').find('span:first').html(),
                'clickThrough':this.active.parent().attr('href') || ""
                });

            //process all the thumbnails
            this.thumbs.each(this.createThumb);
        }, //end setup
        /**
         * proccesses thumbnails
         *
         * @method createThumb
         * @return undefined
         */
        createThumb: function() {
            var self = $(this);
            $(this).hide();
            //store all the data with the image
            $.data(this,'clickThrough',$(this).parent('a').attr('href') || "#");
            if($(this).parent('a').length > 0){
                $(this).unwrap();
            }
            $.data(this,'caption',$(this).next('span').html() || "");
            $(this).next('span').remove();
            $.data(this,'source',$(this).attr('ref') || $(this).attr('src'));
            $.data(this,'order',$("ul li").index($(this).parents('li')));
            //pass data so it can enter the load scope
            var data = $.data(this);
        },//end createThumb
        /**
         * proccesses thumbnails
         *
         * @method bindEvents
         * @return undefined
         */
        bindEvents: function() {
            this.thumbs.bind('click',{
                self:this
            },this.imgClick);
            this.imgNext.bind('click',{
                self:this
            },this.nextClick);
            this.textNext.bind('click',{
                self:this
            },this.nextClick);
            this.imgPrev.bind('click',{
                self:this
            },this.prevClick);
            this.textPrev.bind('click',{
                self:this
            },this.prevClick);
            this.imgPlay.bind('click',{
                self:this
            },this.playClick);
            this.wrap.bind('mouseenter',{
                self:this
            },function(e){
                e.data.self.imgPlay.stop(true,true).fadeIn('fast');
            });
            this.wrap.bind('mouseleave',{
                self:this
            },function(e){
                e.data.self.imgPlay.stop(true,true).fadeOut('fast');
            });
        },//end bind event
        /**
         * handles gallery after aclick occurs. and sets active classes
         *
         * @method imgClick
         * @return undefined
         */
        imgClick: function(e,x) {
            var self = e.data.self;
            if(self.animating){
                return;
            }
            self.caption.fadeOut('slow');
            if(typeof(x) == 'undefined' || x.how != "auto")
            {
                //arrive here if natural click
                if(self.options.autoPlay)
                {
                    self.imgPlay.trigger('click');
                }
            }
            self.animating = true;
            self.active.fadeTo(300,0.4).removeClass('active');
            self.active = $(this);
            self.active.addClass('active').fadeTo(200,1);
            var data = $.data(this);
            $('<img />').bind('load', {
                self:self,
                data:data
            }, function()

            {
                //in this scope self referes to the PikaChoose object
                self.aniDiv.css({
                    height:self.image.innerHeight(),
                    width:self.image.innerWidth()
                    }).fadeIn('fast');
                self.aniDiv.children('div').css({
                    'width':'20%',
                    'height':'20%',
                    'float':'left'
                });

                //decide our transition
                var n = 0;
                if(self.options.transition[0] == -1)
                {	//random
                    n = Math.floor(Math.random()*6);
                }else{
                    n = self.options.transition[self.step];
                    self.step++;
                    if(self.step >= self.options.transition.length){
                        self.step=0;
                    }
                }
                if(self.options.IESafe && $.browser.msie){
                    n = 0;
                }
                self.doAnimation(n,data);

            }).attr('src',$.data(this).source);//end image preload
        },//end bindEvents


        doAnimation: function(n,data)
        {

            var self = this; //self in this scope refers to PikaChoose object. Needed for callbacks on animations
            var aWidth = self.aniDiv.children('div').eq(0).innerWidth();
            var aHeight = self.aniDiv.children('div').eq(0).innerHeight();
            self.aniDiv.children().each(function()
            {
                //position myself absolutely
                var div = $(this);
                var xOffset = Math.floor(div.parent().innerWidth()/5)*div.attr('col');
                var yOffset = Math.floor(div.parent().innerHeight()/5)*div.attr('row');
                div.css({
                    'background':'url('+data.source+') -'+xOffset+'px -'+yOffset+'px',
                    'width':'0px',
                    'height':'0px',
                    'position':'absolute',
                    'top':yOffset+'px',
                    'left':xOffset+'px',
                    'float':'none'
                });
            });//end ani_divs.children.each

            switch(n)
            {
                case 0:
                    //full frame fade
                    self.aniDiv.css('height',self.image.innerHeight() + 'px').hide().css({
                        'background':'url('+data.source+') top left no-repeat'
                        });
                    self.aniDiv.children('div').hide();
                    self.aniDiv.fadeIn('slow',function(){
                        self.finishAnimating(data);
                        self.aniDiv.css({
                            'background':'transparent'
                        });
                    });

                    break;
                case 1:
                    self.aniDiv.children().hide().each(function(index)
                    {
                        //animate out as blocks
                        var delay = index*30;
                        $(this).css({
                            opacity: 0.1
                        }).delay(delay).animate({
                            opacity: 1,
                            "width":aWidth,
                            "height":aHeight
                        },200,'linear',function()

                        {
                            if($(".animation div").index(this) == 24)
                            {
                                self.finishAnimating(data);
                            }
                        });
                    });
                    break;
                case 2:
                    self.aniDiv.children().hide().each(function(index)
                    {
                        var delay = $(this).attr('row')*30;
                        $(this).css({
                            opacity:0.1,
                            "width":aWidth
                        }).delay(delay).animate({
                            opacity:1,
                            "height":aHeight
                        },500,'linear',function()

                        {
                            if($(".animation div").index(this) == 24)
                            {
                                self.finishAnimating(data);
                            }
                        });
                    });
                    break;
                case 3:
                    self.aniDiv.children().hide().each(function(index)
                    {
                        var delay = $(this).attr('col')*10;
                        aHeight = self.gapper($(this), aHeight);
                        $(this).css({
                            opacity:0.1,
                            "height":aHeight
                        }).delay(delay).animate({
                            opacity:1,
                            "width":aWidth
                        },800,'linear',function()

                        {
                            if($(".animation div").index(this) == 24)
                            {
                                self.finishAnimating(data);
                            }
                        });
                    });
                    break;
                case 4:
                    self.aniDiv.children().show().each(function(index)
                    {
                        var delay = index*Math.floor(Math.random()*5)*10;
                        aHeight = self.gapper($(this), aHeight);

                        if($(".animation div").index(this) == 24)
                        {
                            delay = 800;
                        }
                        $(this).css({
                            "height":aHeight,
                            "width":aWidth,
                            "opacity":.01
                        }).delay(delay).animate({
                            "opacity":1
                        },800,function()

                        {
                            if($(".animation div").index(this) == 24)
                            {
                                self.finishAnimating(data);
                            }
                        });
                    });
                    break;
                case 5:
                    //full frame slide
                    self.aniDiv.css('height',self.image.innerHeight() + 'px').hide().css({
                        'background':'url('+data.source+') top left no-repeat'
                        });
                    self.aniDiv.children('div').hide();
                    self.aniDiv.css({
                        width:0
                    }).animate({
                        width:self.image.innerWidth()
                        },'slow',function(){
                        self.finishAnimating(data);
                        self.aniDiv.css({
                            'background':'transparent'
                        });
                    });

                    break;
                case 6:
                    //fade out then in
                    self.aniDiv.hide();
                    self.image.fadeOut('slow',function(){
                        self.image.attr('src',data.source).fadeIn('slow',function()
                        {
                            self.finishAnimating(data);
                        });
                    });

                    break;
            }

        },//end doAnimation
        finishAnimating: function(data)
        {
            this.animating = false;
            this.image.attr('src',data.source);
            this.aniDiv.hide();
            this.anchor.attr('href',data.clickThrough);
            if(this.options.showCaption)
            {
                this.caption.html(data.caption).fadeIn('slow');
            }
            if(this.options.autoPlay == true)
            {
                var self = this;
                this.image.delay(this.options.speed).fadeIn(0,function(){
                    if(self.options.autoPlay){
                        self.nextClick();
                    }
                });
        }
    },//end finishedAnimating
    gapper: function(ele, aHeight)
    {
        if(ele.attr('row') == 9 && ele.attr('col') == 0)
        {
            //last row, check the gap and fix it!
            var gap = ani_divs.innerHeight()-(aHeight*9);
            return gap;
        }
        return aHeight;
    },
    nextClick : function(e)
    {
        var how = "natural";
        try{
            var self = e.data.self;
        }catch(err)
        {
            var self = this;
            how = "auto";
        }
        var next = self.active.parents('li:first').next().find('img');
        if(next.length == 0){
            next = self.list.find('img').eq(0);
        };
        next.trigger('click',{
            how:how
        });
    },
    prevClick : function(e)
    {
        var self = e.data.self;
        var prev = self.active.parents('li:first').prev().find('img');
        if(prev.length == 0){
            prev = self.list.find('img:last');
        };
        prev.trigger('click');
    },
    playClick: function(e)
    {
        var self = e.data.self;
        self.options.autoPlay = !self.options.autoPlay;
        self.imgPlay.toggleClass('play').toggleClass('pause');
        if(self.options.autoPlay){
            self.nextClick();
        }
    }
    }); //end extend

})(jQuery);
