﻿
function Spriter(jQuery, settings) {

    this.j = jQuery;

    this.XMovements = { LeftToRight: 'ltor', RightToLeft: 'rtol', None: 'none' };
    this.YMovements = { UpsideDown: 'down', DownsideUp: 'up', None: 'none' };
    this.Animations = { Fade: 'fade', Move: 'move', Slide: 'slide', Swap: 'swap' };


    /// DEFAULT SETTINGS.
    ///
    this.S = {
        Sprite: '',
        AnchorX: 0,
        AnchorY: 0,
        Width: -1,
        Height: -1,
        SpeedOn: 300,
        SpeedOff: 100,
        XMovement: this.XMovements.None,
        YMovement: this.YMovements.DownsideUp,
        DynamicTextHeight: true,
        Debugging: false,
        Animation: this.Animations.Fade,
        PreserveMainObject: false,
        Automatic: true,
        LogCallBack: null
    };

    this.S = this.j.extend({}, this.S, settings);

    this._compos = new Array();
    this._idcounter = 0;
    this._rankCounter = 0;
    this._warned = false;
    this._declared = new Array();


    /// THIS ACTS AS A CLASS THAT WILL BE RETURNED TO THE CLIENT.
    /// THIS CLASS WILL BE A GATEWAY THAT WILL REDIRECT THE CLIENT REQUESTS (TurnOn, TurnOff, etc)
    /// TO THE MAIN CLASS.
    ///
    this.Sprite = function() {
        this._SpriteCompound = null;
        this._SpriterObj = null;

        this.TurnOn = function() {
            this._SpriterObj.Trigger(true, this._SpriteCompound);
        }

        this.TurnOff = function() {
            this._SpriterObj.Trigger(false, this._SpriteCompound);
        }

    }


    /// THIS WILL WORK AS A HELPER OBJECT TO IDENTIFY ALL
    /// THE GROUPS OF ELEMENTS THAT MAKE UP EACH ANIMATABLE MENU ITEM.
    ///
    this.Compound = function() {

        this.WrapperObj = null;
        this.MainObj = null;
        this.IdleObj = null;
        this.HoverObj = null;
        this.IsSetup = false;
        this.ID = NaN;

        this.IsCreated = false;
        this.Selector = "";

        this.Rank = -1;
        this.MenuWidth = -1;
        this.TextOnly = false;

        /// THE FOLLOWING PROPERTIES WILL STORE TWO CALLBACKS.
        ///
        this.MouseOver = null;
        this.MouseOut = null;
        this.MouseDown = null;

        this.MarginTop = 0;
        this.MarginLeft = 0;
        this.MarginBottom = 0;
        this.MarginRight = 0;
    }



    this.Create = function(selector, settings) {

        var rank = undefined;
        var width = undefined;
        var textOnly = false;

        var mouseOverDelegate = undefined;
        var mouseOutDelegate = undefined;

        var marginTop = 0;
        var marginLeft = 0;
        var marginBottom = 0;
        var marginRight = 0;

        if (settings != undefined) {
            rank = settings.Rank;
            width = settings.Width;
            textOnly = settings.TextOnly == true;
            mouseOverDelegate = settings.MouseOver;
            mouseOutDelegate = settings.MouseOut;
            mouseDownDelegate = settings.MouseDown;

            marginTop = settings.MarginTop;
            marginLeft = settings.MarginLeft;
            marginBottom = settings.MarginBottom;
            marginRight = settings.MarginRight;

            marginTop = isNaN(marginTop) ? 0 : marginTop;
            marginLeft = isNaN(marginLeft) ? 0 : marginLeft;
            marginBottom = isNaN(marginBottom) ? 0 : marginBottom;
            marginRight = isNaN(marginRight) ? 0 : marginRight;

        }

        /// TEXT-ONLY SPRITES DON'T CONSUME SPRITE RANKS. THEY ARE TREATED DIFFERENTLY.
        if (textOnly) rank = -1;

        if (rank == undefined) rank = ++this._rankCounter;
        if (width == undefined) {

            /// LOOKING FOR THE WIDTH IN THE ARRAY OF DECLARED SIZES.
            ///
            for (var n = 0; n < this._declared.length; n++) {
                var declared = this._declared[n];
                if (declared.Rank == rank) {
                    width = declared.Width;
                    break;
                }
            }

            /// WIDTH NOT FOUND, USING THE GENERIC WIDTH.
            ///
            if (width == undefined) width = this.S.Width;
        }


        if (isNaN(rank) || isNaN(width)) {
            alert('Some of the values you provided are not valid numbers.\n\nRank [' + rank + '] Width [' + width + ']\n\n' +
            'The menu for [' + selector + '] will not be created.');
            return;
        }



        /// CHECKING IF THE SUPPLIED RANK IS USED BY A DIFFERENT SELECTOR.
        /// I WILL NOT MAKE THIS CHECK. IF THE CLIENT WANTS TO USE THE SAME RANK (THE SAME MENU ITEM)
        /// ON DIFFERENT SELECTORS, THAT'S HIS PREROGATIVE. THIS MAY BE A DESIRED EFFECT,
        /// AND IF IT'S NOT DESIRED, HE WILL SEE IT ON SCREEN.
        ///



        /// ON THE OTHER HAND...
        /// I WILL CHECK IF THE SELECTOR EXISTS ALREADY IN THE ARRAY OF COMPOUNDS.
        /// IF THAT'S THE CASE, WE ARE JUST UPDATING IT INSTEAD OF CREATING A NEW ONE.
        ///
        var compo = null;

        for (var n = 0; n < this._compos.length; n++) {
            var compound = this._compos[n];
            if (compound.Selector == selector) {
                /// A PREVIOUS COMPOUND WAS FOUND, BUT DOES IT REALLY STILL EXISTS IN THE DOM ?
                ///
                var prevSelector = this.j(compound.WrapperObj).selector;
                var exist = this.j(prevSelector).length == 1;

                if (!exist) {
                    /// NOOPE, IT REALLY DOES NOT EXIST ANYMORE.
                    this.DoLog('BUT IT DOESNT EXISTS ANYMORE, So we are removing it from our compounds list ! ', 3);

                    /// REMOVING IT FROM THE COMPOUNDS ARRAY.
                    this._compos.splice(n, 1);
                } else {
                    /// YES, IT DOES STILL EXISTS !
                    compo = compound;
                }
                break;
            }
        }

        /// A PREVIOUS COMPOUND FOR THIS SELECTOR WAS NOT FOUND, CREATING IT.
        ///
        if (compo == null) {
            var compo = new this.Compound();

            compo.ID = this._idcounter++;
            this._compos[this._compos.length] = compo;
        }

        compo.Selector = selector;
        compo.Rank = rank;
        compo.MenuWidth = width;
        compo.TextOnly = textOnly;
        compo.MouseOver = mouseOverDelegate;
        compo.MouseOut = mouseOutDelegate;
        compo.MouseDown = mouseDownDelegate;

        compo.MarginTop = marginTop;
        compo.MarginLeft = marginLeft;
        compo.MarginBottom = marginBottom;
        compo.MarginRight = marginRight;


        /// THIS METHOD WILL PERFORM THE DOM TRANSFORMATION MAGIC :: )
        this._setup(compo);


        /// DECLARING THIS ITEM WIDTH.
        /// 
        this.SpecifyWidth(rank, width);


        /// RETURNING A GATEWAY OBJECT THAT WOULD ALLOW A CONSUMER
        /// TO ACCESS QUICKLY AND MORE DIRECTLY THE SOME ASPECTS OF THE LOGIC OF 
        /// THIS MAIN CLASS FROM THE GATEWAY OBJ.
        ///
        var returnObject = new this.Sprite();
        returnObject._SpriteCompound = compo;
        returnObject._SpriterObj = this;

        return returnObject;
    }




    this._setup = function(compoObj) {

        ///
        /// IMPORTANT NOTE:
        ///
        /// THE MAIN OBJECT IN THE COMPOUND MAY BE CONTAINED IN A DISPLAY-NONE ELEMENT.
        /// IN SUCH CASE, WIDTH AND HEIGHT CALCULATIONS WILL NOT WORK.
        /// TO FIGHT THAT, WE ARE CREATING THE COMPOUND OBJECTS DIRECTLY IN THE "BODY", SO WE CAN CONTROL THEM.
        /// THEN WE ARE RE-INSERTING THESE OBJECTS BACK INTO THE RIGHT LOCATION.
        ///
        /// THIS REQUIRES ADDITIONAL EFFORT.
        ///
        var selector = compoObj.Selector;

        /// GETTING SOME INITIAL PROPERTIES FROM THE HTML ELEMENT.
        ///
        var bonda = this.j.fn.Boundaries(selector);
        var goLeft = this.j(selector).css('float') == "left";

        var claz = this.j(selector).attr('class');

        var refObj = this.j(selector);
        var idle_styles = this._safeCopyStyles(claz, refObj);
        var hover_styles = idle_styles;
        var make_a_background = false;
        if (claz != '' && claz != undefined) {
            hover_styles = this._safeCopyStyles(claz + '_hover');
        } else {
            make_a_background = true;
        }


        /// TESTING IF THE USER WANTED US TO PROVIDE A FEW STYLES FOR VISIBILITY PURPOSES.
        ///
        var useHelperStyles = false;
        if (useHelperStyles && compoObj.TextOnly) {
            idle_styles = "font-family:Arial; font-size:11px; color:black; background-color:Yellow; border:1px solid #CCC;";
            hover_styles = "font-family:Arial; font-size:11px; color:black; background-color:Orange; border:1px solid #CCC;";
            bonda.MarginTop = 10;
            bonda.MarginBottom = 10;
            bonda.MarginRight = 10;
            bonda.MarginLeft = 10;
            bonda.PaddingTop = 5;
            bonda.PaddingBottom = 5;
            bonda.PaddingRight = 5;
            bonda.PaddingLeft = 5;
        }



        /// GETTING THE CLEAN "ID" FROM THE SUPPLIED SELECTOR.
        /// NOW WE KNOW THE MAIN OBJECT ID.
        var id = selector.replace('#', '');



        /// INSERTING A MARK SO WE KNOW WHERE THE THINGS WILL BE INSERTED IN THE END.
        /// 
        var tempSelector = "_tempIpsum_" + Math.round(Math.random() * 10000);
        this.j(selector).after('<p id="' + tempSelector + '" />');

        /// CLONING THE MAIN ELEMENT INTO MEMORY, THEN REMOVING IT FROM THE DOM.
        var mainObj = this.j(selector).clone(true);
        this.j(selector).remove();

        var innerHtml = mainObj.html();

        this.j('body').append('<div id="' + id + '_w">' +
            '<div id="' + id + '" />' +
            '<div id="' + id + '_i" />' +
            '<div id="' + id + '_h" /></div>');



        var wrapperObj = this.j('#' + id + '_w');
        var mainObj = this.j('#' + id + '');
        var idleObj = this.j('#' + id + '_i');
        var hoverObj = this.j('#' + id + '_h');



        if (goLeft) { wrapperObj.css('float', 'left'); }



        wrapperObj.css('border', this.GetDebugColor());
        wrapperObj.css('width', compoObj.MenuWidth);
        wrapperObj.css('position', 'relative');
        wrapperObj.css('cursor', 'pointer');
        wrapperObj.css('overflow', 'hidden');

        var pos = 'absolute';

        mainObj.css({ 'border': this.GetDebugColor(), position: pos, width: compoObj.MenuWidth, zIndex: 30, overflow: 'hidden', visibility: 'hidden' });
        idleObj.css({ 'border': this.GetDebugColor(), position: pos, width: compoObj.MenuWidth, zIndex: 20, overflow: 'hidden' });
        hoverObj.css({ 'border': this.GetDebugColor(), position: pos, width: compoObj.MenuWidth, zIndex: 10, overflow: 'hidden' });

        var padLeft = bonda.PaddingLeft;
        var padRight = bonda.PaddingRight;
        var padTop = bonda.PaddingTop;
        var padBottom = bonda.PaddingBottom;
        var marLeft = bonda.MarginLeft;
        var marRight = bonda.MarginRight;
        var marTop = bonda.MarginTop;
        var marBottom = bonda.MarginBottom;

        var calcWidth = compoObj.MenuWidth - padLeft - padRight - marLeft - marRight;

        var htmlTemplate = '<div style="padding-top:' + padTop + 'px; padding-bottom:' + padBottom + 'px; ' +
            'padding-left:' + padLeft + 'px; padding-right:' + padRight + 'px; ' +
            'margin-top:' + marTop + 'px; margin-bottom:' + marBottom + 'px; ' +
            'margin-left:' + marLeft + 'px; margin-right:' + marRight + 'px; width:' + calcWidth + 'px; {0} ">{1}</div>';


        var idleHtml = htmlTemplate.replace('{0}', idle_styles).replace('{1}', innerHtml);
        var hoverHtml = htmlTemplate.replace('{0}', hover_styles).replace('{1}', innerHtml);

        mainObj.html(idleHtml);
        idleObj.html(idleHtml);

        hoverObj.html(hoverHtml);
        if (make_a_background) { hoverObj.find('div:first').css('backgroundColor', '#666'); };

        var h = mainObj.height();
        wrapperObj.css('height', h);


        if (!compoObj.TextOnly) {
            wrapperObj.height(this.S.Height);
            mainObj.height(this.S.Height);
            idleObj.height(this.S.Height);
            hoverObj.height(this.S.Height);

            mainObj.html('');
            idleObj.html('');
            hoverObj.html('');

            wrapperObj.css({ marginTop: marTop, marginBottom: marBottom, marginRight: marRight, marginLeft: marLeft });

        } else {
            var calcHeight = h - bonda.PaddingTop - bonda.PaddingBottom - bonda.MarginTop - bonda.MarginBottom;
            mainObj.find('div:first').height(calcHeight);
            idleObj.find('div:first').height(calcHeight);
            hoverObj.find('div:first').height(calcHeight);
        }





        /// STORING THE MENU POSITION IN THE SPRITE (1, 2, 3, ETC, 1-BASED)
        var menuIX = compoObj.Rank;

        /// STORING THE LEFT POSITION OF THE MENU IN THE SPRITE.
        /// IT'S CALCULATED USING THE X-ANCHOR, THE MENU POSITION IN THE SPRITE, AND THE 
        /// MENU WIDTH.
        var menuLeftPos = -this.S.AnchorX - (menuIX - 1) * this.S.Width;


        /// ADDING UP A POSSIBLE OFFSET, IF SOME OF THE MENU ELEMENTS ARE
        /// LARGER OR SMALLER THAN SPECIFIED ORIGINALLY.
        ///
        menuLeftPos += this.GetOffset(menuIX);


        if (!compoObj.TextOnly) {

            idleObj.css({
                backgroundImage: 'url("' + this.S.Sprite + '")',
                backgroundRepeat: 'no-repeat',
                backgroundPosition: menuLeftPos + 'px ' + -this.S.AnchorY + 'px'
            });


            hoverObj.css({
                backgroundImage: 'url("' + this.S.Sprite + '")',
                backgroundRepeat: 'no-repeat',
                backgroundPosition: menuLeftPos + 'px ' + (-this.S.AnchorY - this.S.Height) + 'px'
            });

        }



        /////////////////////////////////////////////
        /////////////////////////////////////////////
        /// MOVING THE OBJECTS FROM THE TEMP CONTAINER WHERE WE CREATED THEM TO THEIR FINAL DESTINATION.
        ///
        var clon = wrapperObj.clone(true);
        this.j('#' + tempSelector).after(clon);
        wrapperObj.remove();
        this.j('#' + tempSelector).remove();


        /// WE ARE RE-BUILDING THE COMPOUND OBJECTS BECAUSE WE MOVED THEM AROUND IN THE DOM AND
        /// THE PREVIOUS OBJ-REFERENCES POINT TO OBJECTS THAT NO LONGER EXISTS.
        ///
        wrapperObj = this.j('#' + wrapperObj.attr('id'));
        mainObj = this.j('#' + mainObj.attr('id'));
        idleObj = this.j('#' + idleObj.attr('id'));
        hoverObj = this.j('#' + hoverObj.attr('id'));
        ///
        /////////////////////////////////////////////
        /////////////////////////////////////////////




        /// PASSING THE DOM-OBJECTS INTO OUR COMPOUND OBJECT.
        ///
        compoObj.WrapperObj = wrapperObj;
        compoObj.MainObj = mainObj;
        compoObj.IdleObj = idleObj;
        compoObj.HoverObj = hoverObj;




        var actionsObject = wrapperObj;

        ///////////////
        ///
        /// EVENTS LOGIC. TRIGGERING OUR EVENTS AND RAISING THE CALLBACKS IF PROVIDED.
        /// ONLY IF AUTOMATIC MODE IS ON. 
        ///
        if (this.S.Automatic) {
            var t = this;
            this.j(actionsObject).bind('mouseover', function() {
                t.Trigger(true, compoObj);
                if (compoObj.MouseOver != undefined) compoObj.MouseOver();
            });
            this.j(actionsObject).bind('mouseout', function() {
                t.Trigger(false, compoObj);
                if (compoObj.MouseOut != undefined) compoObj.MouseOut();
            });
        } else {
            this.j(actionsObject).bind('mouseover', function() {
                if (compoObj.MouseOver != undefined) compoObj.MouseOver();
            });
            this.j(actionsObject).bind('mouseout', function() {
                if (compoObj.MouseOut != undefined) compoObj.MouseOut();
            });
        }

        /// DEALING WITH THE MOUSE DOWN EVENTS.
        ///
        this.j(actionsObject).bind('click', function() {
            if (compoObj.MouseDown != undefined) compoObj.MouseDown();
        });




    }






    /// TESTING IF THE SUPPLIED SPRITE IMAGE EXISTS.
    ///
    this._Test = function() {
        var t = this;
        var id = "_sprite_" + Math.round(Math.random() * 10000);
        var display = 'none';
        if (this.S.Debugging) display = 'inherit';
        this.j('body').append('<img id="' + id + '" alt="" style="display:' + display +
            '; border:1px solid red; padding:10px; margin:10px;" />');
        this.j('#' + id).bind('error', function() {
            while (!confirm('Hey ! Your sprite is missing !\n\n' + t.S.Sprite +
                '\n\nThe animations will not look the way you expect.')) { };
        });
        this.j('#' + id).attr('src', this.S.Sprite);
        if (this.S.Debugging) this.j('#' + id).css({ opacity: 0.2 });
    }
    this._Test();





    ///
    /// THIS METHOD WILL CALCULATE HOW MANY PIXELS A MENU ITEM IS OFFSETED.
    /// IF SOME OF THE MENU ITEMS HAVE DIFFERENT WIDTHS, THIS METHOD WILL HELP ADJUSTING TO
    /// FIX THE GAPS.
    /// 
    this.GetOffset = function(rank) {
        var offset = 0;
        for (var n = 0; n < this._declared.length; n++) {
            var declared = this._declared[n];
            if (declared.Rank < rank && declared.Rank != -1) {
                if (declared.Width != this.S.Width) {
                    offset += this.S.Width - declared.Width;
                }
            }
        }
        return offset;
    }


    /// THIS IS A HELPER METHOD THAT ACTS AS A CLASS.
    /// IT HELPS IDENTIFYING SOME MENU ITEMS THAT HAVE A DIFFERENT
    /// WIDTH THAN THE ONE SPECIFIED WHEN CONSTRUCTING THE OBJECT.
    /// 
    this.DeclaredSize = function() {
        this.Rank = -1;
        this.Width = 0;
    }


    this.SpecifyWidth = function(rank, width) {

        var found = false;
        /// FIRST IT LOOKS IN THE ARRAY OF THE DECLARED SIZES TO SEE IF IT WAS DECLARED BEFORE
        /// 
        for (var n = 0; n < this._declared.length; n++) {
            var declared = this._declared[n];
            if (declared.Rank == rank) {
                declared.Width = width;
                found = true;
                break;
            }
        }

        /// IF NOT FOUND, IT STORES THIS DECLARATION IN A DECLARATIONS ARRAY.
        ///
        if (!found) {
            var declared = new this.DeclaredSize();
            declared.Rank = rank;
            declared.Width = width;
            this._declared[this._declared.length] = declared;
        }

        /// THEN IT WILL LOOK FOR THIS MENU-POSITION IN THE ARRAY IN THE ARRAY OF COMPOUNDS
        /// TO STORE THE DECLARED WIDTH, IF FOUND.
        ///
        for (var n = 0; n < this._compos.length; n++) {
            var compound = this._compos[n];
            if (rank == compound.Rank) {
                compound.MenuWidth = width;
                break;
            }
        }
    }








    /////////////////////////////////////////
    /////////////////////////////////////////
    /////////////////////////////////////////
    //////
    //////  ANIMATION METHODS 
    //////

    this.Slide = function(over, compoObj) {

        var gotcha = false;

        this.SwapZethas(true, compoObj);
        
        var idleO = compoObj.IdleObj;

        var w = idleO.width();
        var h = idleO.height();

//        var marginTop = this.GetNumericPixel(idleO.css('margin-top'));
//        var marginLeft = this.GetNumericPixel(idleO.css('margin-left'));

        var toLeft = 0;
        var fromLeft = toLeft;
        var toTop = 0;
        var fromTop = toTop;


        var fromHeight = 0;
        var toHeight = h;
        var fromWidth = 0;
        var toWidth = w;


        this.S.YMovement = 'up';
        this.S.XMovement = 'ltor';


        if (this.S.YMovement == this.YMovements.DownsideUp)
            this.S.YMovement = this.YMovements.None;

        if (this.S.XMovement == this.XMovements.RightToLeft)
            this.S.XMovement = this.XMovements.None;


        if (this.S.YMovement == this.YMovements.None && this.S.XMovement == this.XMovements.None)
            this.S.YMovement = this.YMovements.UpsideDown;


        if (this.S.XMovement == this.XMovements.None) {
            toWidth = w;
            fromWidth = w;
        }


        if (this.S.YMovement == this.YMovements.None) {
            toHeight = h;
            fromHeight = h;
        }


        var hoverO = compoObj.HoverObj;

        if (!compoObj.IsSetup) {
            hoverO.css({ height: fromHeight, width: fromWidth });
            compoObj.IsSetup = true;
        }

        if (over) {
            hoverO.stop().animate({ height: toHeight, width: toWidth }, this.S.SpeedOn);
        } else {
            hoverO.stop().animate({ height: fromHeight, width: fromWidth }, this.S.SpeedOff);
        }
    }




    this.Move = function(over, compoObj) {

        this.SwapZethas(true, compoObj);

        var idleO = compoObj.IdleObj;

        var w = idleO.outerWidth(false);
        var h = idleO.outerHeight(false);

        var marginTop = this.GetNumericPixel(idleO.css('margin-top'));
        var marginLeft = this.GetNumericPixel(idleO.css('margin-left'));

        var toLeft = 0;
        var fromLeft = toLeft;
        var toTop = 0;
        var fromTop = toTop;


        if (this.S.YMovement == this.YMovements.None && this.S.XMovement == this.XMovements.None)
            this.S.YMovement = this.YMovements.UpsideDown;


        if (this.S.XMovement == this.XMovements.RightToLeft)
            fromLeft = toLeft + w;

        if (this.S.XMovement == this.XMovements.LeftToRight)
            fromLeft = toLeft - w;

        if (this.S.YMovement == this.YMovements.UpsideDown)
            fromTop = toTop - h;


        if (this.S.YMovement == this.YMovements.DownsideUp)
            fromTop = toTop + h;


        var hoverO = compoObj.HoverObj;

        if (!compoObj.IsSetup) {
            hoverO.css({ left: fromLeft, top: fromTop });
            compoObj.IsSetup = true;
        }

        if (over) {
            hoverO.stop().animate({ left: toLeft, top: toTop }, this.S.SpeedOn);
        } else {
            hoverO.stop().animate({ left: fromLeft, top: fromTop }, this.S.SpeedOff);
        }
    }


    this.Swap = function(over, compoObj) {

        this.SwapZethas(false, compoObj);

        if (over) {
            compoObj.IdleObj.stop().css({ opacity: 0 });
        } else {
            compoObj.IdleObj.stop().css({ opacity: 1 });
        }
    }




    this.Fade = function(over, compoObj) {

        this.SwapZethas(false, compoObj);

        if (over) {
            compoObj.IdleObj.stop().animate({ opacity: 0 }, this.S.SpeedOn);
        } else {
            compoObj.IdleObj.stop().animate({ opacity: 1 }, this.S.SpeedOff);
        }
    }


    /// THIS IS A GATEWAY METHOD THAT WILL TAKE CARE OF FORWARDING THE
    /// ANIMATION REQUESTS TO THE RIGHT FUNCTION
    ///
    this.Trigger = function(over, compoundObj) {

        switch (this.S.Animation) {
            case this.Animations.Fade:
                this.Fade(over, compoundObj);
                break;

            case this.Animations.Move:
                this.Move(over, compoundObj);

                break;

            case this.Animations.Slide:
                this.Slide(over, compoundObj);
                break;

            case this.Animations.Swap:
                this.Swap(over, compoundObj);
                break;

            default:
                alert('Unsupported animation type [' + this.S.Animation + ']');
                break;
        }
    }
    
    

    this.SwapZethas = function(hoverOver, compoObj) {

        var idleO = compoObj.IdleObj;
        var hoverO = compoObj.HoverObj;

        var zIdle = idleO.css('zIndex');
        var zHover = hoverO.css('zIndex');

        if ((hoverOver && zHover < zIdle) || (!hoverOver && zHover > zIdle)) {
            idleO.css('zIndex', zHover);
            hoverO.css('zIndex', zIdle);
        }
    }


    this.Reset = function(compoObj) {
    }


    //////
    //////  END - ANIMATION METHODS 
    //////
    /////////////////////////////////////////
    /////////////////////////////////////////
    /////////////////////////////////////////








    /////////////////////////////////////////
    /////////////////////////////////////////
    /////////////////////////////////////////
    //////
    //////  HELPER METHODS 
    //////


    this.GetDebugColor = function(solid) {
        return this._GetDebugColor(solid, false);
    }

    this.GetDebugColorForce = function(solid) {
        return this._GetDebugColor(solid, true);
    }

    this._GetDebugColor = function(solid, force) {

        if (!this.S.Debugging && !force) return "";

        var myColors = ["Red", "Blue", "Green", "Orange", "Maroon"];
        var ix = Math.ceil(Math.random() * myColors.length - 1);

        if (solid) return myColors[ix];

        return "2px solid " + myColors[ix];
    }


    this.GetNumericPixel = function(string) {
        if (string == undefined) string = "";
        var str = string.replace('px', '');
        var num = parseInt(str);
        if (isNaN(num)) num = 0;
        return num;
    }




    this.DoLog = function(msg, dept) {
        if (this.S.LogCallback == undefined) return;
        this.S.LogCallback(msg, dept);
    }





    /// THIS HELPER METHOD WILL CREATE A DOM ELEMENT, ASSIGN A CLASS TO IT, AND THEN PULL SEVERAL CSS
    /// PROPERTIES FROM IT. FINALLY IT WILL DELETE THIS TEMPORARY DOM ELEMENT AND RETURN THE STYLES IN A STRING.
    /// WE NEED TO ADD AN ELEMENT TO THE DOM SO WE CAN PULL ITS STYLES, OTHERWISE WE WOULD NEED TO PARSE THE
    /// CSS DEFINITIOS. THE DOM ELEMENT IS CREATED IN A CONTAINER THAT WOULD NOT OCCUPY ANY SPACE.
    var hlpr = null;
    this._safeCopyStyles = function(classs, refObj) {
        var tmp = "_trg_" + Math.round(Math.random() * 10000);
        var workObj = null;
//        refObj = null;
        if (refObj != undefined) {
            workObj = refObj.clone(false);
        } else {
            workObj = this.j('<div/>');
        }
        workObj.attr('class', classs);
        workObj.attr('id', tmp);

        var html = workObj.wrap('<div/>').parent().html();

        this.j('body').append('<div style="visibility:hidden; height:0px; width:0px; position:absolute;">' +
            html + '</div>');

        var gotcha = true;

        var safeStyles = ['text-align', 'text-decoration', 'text-indent', 'text-transform', 'border',
            'background-color', 'vertical-align', 'font-family', 'font-size', 'font-style', 'font-variant',
            'font-weight', 'color'];

        var rval = "";
        var t = this;
        this.j(safeStyles).each(function(index, prm) {
            var value = t.j('#' + tmp).css(prm);
            if (value != undefined) {
                if (rval != '') rval += '; ';
                rval += prm + ":" + value;
            }
        });

        this.j('#' + tmp).parent().remove();

        return rval;

    }

    //////
    //////  END - HELPER METHODS 
    //////
    /////////////////////////////////////////
    /////////////////////////////////////////
    /////////////////////////////////////////




}
    
