﻿if (typeof(Tangora) == 'undefined') var Tangora = {};
Tangora.BroadcastController = new BroadcastController();

if (typeof(AddEventListener)=='undefined')
{    
    if (typeof(addEventListener)!='undefined')
    {
        AddEventListener = addEventListener;
    }
    else
    {
        AddEventListener = function(what, where, elm)
        {                     
            if (!elm) window.attachEvent('on'+what, where);
            else
            {
                elm.attachEvent('on'+what, where);
            }
        }
    }
}

function BroadcastController()
{
    var observers = [];       
    
    this._findObserverIndex = function (obs)
    {        
        for(var i=0;i<observers.length;i++)
        {
            var observer = observers[i];
            if (observer.who == obs.who && observer.what == obs.what && observer.where == obs.where)
            {
                return i;
            }
        }
        return -1;
    }
    
    this._findObservers = function (who,what)
    {       
        if (typeof(who)!='string') who = who.id;
        var listeners = []; 
        for(var i=0;i<observers.length;i++)
        {
            var observer = observers[i];
            if (observer.who == who && observer.what == what)
            {
                listeners.push(observer);
            }
        }
        return listeners;
    }
    
    this._wrap = function(who, what, where)
    {
        return {'who':who,'what':what,'where':where}
    }
    
    // --- //
    
    this.Register = function(who, what, where)
    {
        observers.push(this._wrap(who,what,where));
    }
    
    this.UnRegister = function(who, what, where)
    {      
        var idx = this._findObserverIndex(this._wrap(who,what,where));  
        if (idx>-1)
        {
            observers = observers.splice(idx,1);
            return true;
        }
        return false;
    }
    
    this.Broadcast = function(who, what, data)
    {        
        var listeners = this._findObservers(who, what);
        for(var i=0;i<listeners.length;i++)
        {
            listeners[i].where.call(who, who, what, data);
        }
    }
}



// ImageRotator API -----------------------------------------------------------------------

Tangora.ImageRotator = ImageRotator;
Tangora.ImageRotatorManager = new ImageRotatorManager();

function ImageRotatorManager()
{
    var _instances = [];
    var _htmltransitions = null;
    var _pending = false;
    
    this.GetInstance = function (id)
    {
        if (typeof (_instances[id]) == 'undefined') _instances[id] = new ImageRotator(id, true);
        return _instances[id];
    }

    this.GetHtmlTransitions = function ()
    {
        if (!_htmltransitions)
        {
            if (_pending == false)
            {
                _pending = true;
                var ajax = tsAjax.createInstance('Transitions');
                ajax.method = 'post';
                ajax.postData = 'action=imagerotatorhtmltransitions';
                var result = tsAjax.innerHTML('/publicajax.ashx?ts=' + (new Date().getTime()), null, 'Transitions', null, null, null, true);
                result = eval('(' + result + ')');
                _htmltransitions = result;
            }
            else
            {
                var start = new Date().getTime()
                while (true)
                {
                    // Avoid infinite loop
                    var now = new Date().getTime();
                    if (now - start > 5000) break;
                    if (!_htmltransitions) break;
                }
            }
        }
        return _htmltransitions;
    }

    this.CheckFlash = function (id, domId, width, height, dynamicImages)
    {
        // ----------------------------------------------------------------------------------------------------
        // Flash detection code from: http://www.featureblend.com/javascript-flash-detection-library.html
        // http://www.featureblend.com/license.txt
        var FlashDetect = new function ()
        {
            var self = this; self.installed = false; self.raw = ""; self.major = -1; self.minor = -1; self.revision = -1; self.revisionStr = ""; var activeXDetectRules = [{ "name": "ShockwaveFlash.ShockwaveFlash.7", "version": function (obj) { return getActiveXVersion(obj); } }, { "name": "ShockwaveFlash.ShockwaveFlash.6", "version": function (obj)
            {
                var version = "6,0,21"; try { obj.AllowScriptAccess = "always"; version = getActiveXVersion(obj); } catch (err) { }
                return version;
            }
            }, { "name": "ShockwaveFlash.ShockwaveFlash", "version": function (obj) { return getActiveXVersion(obj); } }]; var getActiveXVersion = function (activeXObj)
            {
                var version = -1; try { version = activeXObj.GetVariable("$version"); } catch (err) { }
                return version;
            }; var getActiveXObject = function (name)
            {
                var obj = -1; try { obj = new ActiveXObject(name); } catch (err) { obj = { activeXError: true }; }
                return obj;
            }; var parseActiveXVersion = function (str) { var versionArray = str.split(","); return { "raw": str, "major": parseInt(versionArray[0].split(" ")[1], 10), "minor": parseInt(versionArray[1], 10), "revision": parseInt(versionArray[2], 10), "revisionStr": versionArray[2] }; }; var parseStandardVersion = function (str) { var descParts = str.split(/ +/); var majorMinor = descParts[2].split(/\./); var revisionStr = descParts[3]; return { "raw": str, "major": parseInt(majorMinor[0], 10), "minor": parseInt(majorMinor[1], 10), "revisionStr": revisionStr, "revision": parseRevisionStrToInt(revisionStr) }; }; var parseRevisionStrToInt = function (str) { return parseInt(str.replace(/[a-zA-Z]/g, ""), 10) || self.revision; }; self.majorAtLeast = function (version) { return self.major >= version; }; self.minorAtLeast = function (version) { return self.minor >= version; }; self.revisionAtLeast = function (version) { return self.revision >= version; }; self.versionAtLeast = function (major) { var properties = [self.major, self.minor, self.revision]; var len = Math.min(properties.length, arguments.length); for (i = 0; i < len; i++) { if (properties[i] >= arguments[i]) { if (i + 1 < len && properties[i] == arguments[i]) { continue; } else { return true; } } else { return false; } } }; self.FlashDetect = function () { if (navigator.plugins && navigator.plugins.length > 0) { var type = 'application/x-shockwave-flash'; var mimeTypes = navigator.mimeTypes; if (mimeTypes && mimeTypes[type] && mimeTypes[type].enabledPlugin && mimeTypes[type].enabledPlugin.description) { var version = mimeTypes[type].enabledPlugin.description; var versionObj = parseStandardVersion(version); self.raw = versionObj.raw; self.major = versionObj.major; self.minor = versionObj.minor; self.revisionStr = versionObj.revisionStr; self.revision = versionObj.revision; self.installed = true; } } else if (navigator.appVersion.indexOf("Mac") == -1 && window.execScript) { var version = -1; for (var i = 0; i < activeXDetectRules.length && version == -1; i++) { var obj = getActiveXObject(activeXDetectRules[i].name); if (!obj.activeXError) { self.installed = true; version = activeXDetectRules[i].version(obj); if (version != -1) { var versionObj = parseActiveXVersion(version); self.raw = versionObj.raw; self.major = versionObj.major; self.minor = versionObj.minor; self.revision = versionObj.revision; self.revisionStr = versionObj.revisionStr; } } } } } ();
        };
        FlashDetect.JS_RELEASE = "1.0.4";
        // ----------------------------------------------------------------------------------------------------

        if (FlashDetect.installed && FlashDetect.major >= 9) return;

        var ajax = tsAjax.createInstance('ImageRotator_' + domId);
        ajax.method = 'post';
        ajax.postData = 'action=imagerotatorfallback&id=' + id + '&domid=' + domId + '&width=' + width + '&height=' + height + '&dynimages=' + dynamicImages;
        tsAjax.innerHTML('/publicajax.ashx?ts=' + (new Date().getTime()), 'tsAjax', 'ImageRotator_' + domId, null, null, callback, true);

        function callback()
        {
            if (ajax.ajaxObj.readyState == 4)
            {
                var result = ajax.ajaxObj.responseText;
                result = eval('(' + result + ')');

                var temp = document.createElement('div');
                temp.innerHTML = result.html;

                var flash = document.getElementById('imagerotator_ie_' + domId);
                flash.parentNode.insertBefore(temp, flash);
                flash.parentNode.removeChild(flash);

                var html = temp.getElementsByTagName('div')[0];
                temp.parentNode.insertBefore(html, temp);
                temp.parentNode.removeChild(temp);

                var script = eval('(' + result.js + ')');
                ImageRotator(domId).Ready(script.images, script.settings);
            }
        }
    }
}

function ImageRotatorImage(source)
{
    this.Source = source;
    this.Duration = 'inherit';
    this.Link = 'inherit';
    this.NewWindow = 'inherit';
    this.Name = source;
    this.Description = '';
    this.EditDate = null;
    var _transitions = null;
    this.AddTransition = function(id)
    {
        if (!_transitions) _transitions = [];
        _transitions.push(id);
    }
    this.GetTransitions = function()
    {
        if (!_transitions) return 'inherit';
        else return _transitions.join(',');
    }
}

function ImageRotator(id, isNew)
{
    var _debug, _isBusy, _isPaused, _images, _currentIndex;
    var _transitions = null;    

    if (isNew)
    {
        this.OnImageChange = null;
        this.OnImageBegin = null;
        this.OnImageEnd = null;
        this.OnReady = null;
        this.IsReady = function(){ return false; }
        this.Ready = function(imagesJSON, stateJSON)
        {
            var that = this;
            function ready()
            {
                if (document.readyState == 'complete')
                {
                    init(that, imagesJSON, stateJSON); 
                    if (that.OnReady) that.OnReady(id);                 
                }
                else
                {
                    setTimeout(ready, 100);
                }
            }
            ready();
        }
        return this;
    }
    else
    {
        return Tangora.ImageRotatorManager.GetInstance(id);
    }
    
    function init(obj, imagesJSON, stateJSON)
    {
        // This method is called from flash, ensuring that flash is loaded and ready to accept commands from this API

        _debug = false;
        _isBusy = true;
        _isPaused = false;
        _images = [];
        _currentIndex = 0;
        _isHtmlFallback = false;
        
        updateState(stateJSON);
        updateImages(imagesJSON);

        var _object = document.getElementById('imagerotator_ie_' + id); // swf object as detected by ie
        if (_object && typeof (_object.Next) == 'undefined')
        {
            _object = document.getElementById('imagerotator_' + id); // swf  object as detected by firefox, chrome, safari etc
        }
        if (!_object || (_object && typeof(_object.Next) == 'undefined'))
        {
            _object = new HTMLalternative(stateJSON);
            _isHtmlFallback = true;
        }      
        
        obj.Start = function() { return doChainCommand('start'); }
        obj.Pause = function() { return doChainCommand('pause'); }
        obj.Next = function() { return doChainCommand('next'); }
        obj.Previous = function() { return doChainCommand('previous'); }
        obj.Goto = function(index) { return doChainCommand('goto', index, _images.length - 1); }
        obj.Add = function(index, image) { checkIndex(index, _images.length); checkSize(); return doChainCommand('add', index, _images.length, image);}
        obj.Remove = function(index) { checkIndex(index, _images.length - 1); return doChainCommand('remove', index, _images.length - 1); }
        obj.ClearCommandQueue = function() { return doChainCommand('clear'); }
        obj.GetItem = function(index) { checkIndex(index, _images.length - 1); return _images[index]; }
        obj.GetLength = function() { return _images.length; }
        obj.GetCurrentIndex = function() { return _currentIndex; }
        obj.IsBusy = function() { return _isBusy; }
        obj.IsPaused = function() { return _isPaused; }
        obj.IsReady = function() { return true; }
        
          
        // Publicly exposed but not part of the API: Used for state-synch between flash and javascript, and for debugging...
        obj.StateChange = function(stateJSON) { updateState(stateJSON); }       // Called from flash when state changes
        obj.ImageChange = function(imagesJSON) { updateImages(imagesJSON); }    // Called from flash when images are added/removed
        obj.IndexChange = function(indexJSON) { debugIndex(indexJSON); }        // Called from flash when debug is on
        obj.QueueChange = function(queueJSON) { debugQueue(queueJSON); }        // Called from flash when debug is on
        obj.Debug = function() { _debug=true; showDebug(); _object.Debug(); }  // Shows debug window
        
        
        function checkIndex(index, maxIndex)
        {
            if (index != null && !/^\d+$/.test(index)) throw new Error('ImageRotator: Index must be an integer.');
            if (index < 0 || index > maxIndex) throw new Error('ImageRotator: Index out of bounds.');
        }
        
        function checkSize()
        {
            if (_images.length >= 64) throw new Error('ImageRotator: Cannot contain more than 64 images.');
        }
        
        function doChainCommand(cmd, index, maxIndex, image)
        {
            if (index) checkIndex(index, maxIndex);
            switch (cmd)
            {
                case 'start': _object.Start(); break;
                case 'pause': _object.Pause(); break;
                case 'next': _object.Next(); break;
                case 'previous': _object.Previous(); break;
                case 'goto': _object.Goto(index); break;
                case 'add': 
                    if (_isHtmlFallback) _object.Add(index, image);
                    else _object.Add(index, image.Source, image.Duration, image.Link, image.NewWindow, image.GetTransitions()); 
                    break;
                case 'remove': _object.Remove(index); break;
                case 'clear': _object.ClearCommandQueue(); break;
                default: throw new Error('ImageRotator: Method not supported.'); break;
            }
            return obj;
        }
        
        function updateImages(json)
        {
            while (_images.length > 0) _images.shift(0, 1);
            var images = typeof(json) == 'object' ? json : eval('('+ json +')');
            for (var i=0; i<images.length; i++)
            {
                var image = new ImageRotatorImage(images[i].source);
                image.Duration = images[i].duration;
                image.Link = images[i].link;
                image.NewWindow = images[i].newwindow;
                for (var j=0; j<images[i].transitions.length; j++)
                {
                    image.AddTransition(images[i].transitions[j]);
                }
                if (typeof (images[i].editdate) != 'undefined') image.EditDate = new Date(images[i].editdate);
                if (typeof (images[i].name) != 'undefined') image.Name = images[i].name;
                if (typeof (images[i].description) != 'undefined') image.Description = images[i].description;
                _images.push(image);
            }
            if (_debug) debugImages();
        }
        
        function updateState(json)
        {
            var state = typeof(json) == 'object' ? json : eval('('+ json +')');
            _isBusy = state.busy;
            _isPaused = state.paused;
            _currentIndex = state.currentindex;
            switch (state.event)
            {
                case "ImageChange": if (obj.OnImageChange) obj.OnImageChange(); break;
                case "ImageBegin": if (obj.OnImageBegin) obj.OnImageBegin(); break;
                case "ImageEnd": if (obj.OnImageEnd) obj.OnImageEnd(); break;
            }
            if (_debug) debugState(state);
        }
        
        function showDebug()
        {
            var div = document.getElementById('debugrotator');
            if (!div)
            {
                div = document.createElement('div');
                div.id = 'debugrotator';
                div.style.border = '#000 1px solid';
                div.style.backgroundColor = '#fff';
                div.style.padding = '10px'
                document.body.appendChild(div);
                
                var events = document.createElement('div');
                events.id = 'debugrotator_events';
                events.style.height = '200px';
                events.style.overflow = 'auto';
                events.style.marginBottom = '10px';
                events.innerHTML = '<table border="1" style="width:100%;border:1px solid #ccc;margin-bottom:10px;"><tr><th>Event</th><th>Busy</th><th>Paused</th><th>Current index</th></tr>&nbsp;</table>'
                div.appendChild(events);
                var sorting = document.createElement('div');
                sorting.id = 'debugrotator_sorting';
                div.appendChild(sorting);
                var queue = document.createElement('div');
                queue.id = 'debugrotator_queue';
                div.appendChild(queue);
                var images = document.createElement('div');
                images.id = 'debugrotator_images';
                div.appendChild(images);
            }
        }
        
        function debugState(state)
        {
            var events = document.getElementById('debugrotator_events');
            if (!events) return;
            events.innerHTML = events.innerHTML.replace('&nbsp;', '&nbsp;<tr><td>'+ state.event +'</td><td>'+ state.busy +'</td><td>'+ state.paused +'</td><td>'+ state.currentindex +'</td></tr>');
        }
        
        function debugImages()
        {
            var div = document.getElementById('debugrotator_images');
            if (!div) return;
            
            var html = '<table border="1" style="width:100%;border:1px solid #ccc;margin-bottom:10px;"><tr><th>Index</th><th>Source</th><th>Duration</th><th>Link</th><th>New window</th><th>Transitions</th></tr>';
            for (var i=0; i<_images.length; i++)
            {
                html += '<tr>';
                html += '<td>'+ i +'</td>';
                html += '<td>'+ _images[i].Source +'</td>';
                html += '<td>'+ _images[i].Duration +'</td>';
                html += '<td>'+ _images[i].Link +'</td>';
                html += '<td>'+ _images[i].NewWindow +'</td>';
                html += '<td>'+ _images[i].GetTransitions() +'</td>';
                html += '</tr>';
            }
            html += '</table>';
            div.innerHTML = html;  
        }
        
        function debugIndex(indexJSON)
        {
            if (!_debug) return;
            var sorting = document.getElementById('debugrotator_sorting');
            if (!sorting) return;
             
            var state = eval('('+ indexJSON +')');
            var html = '<table border="1" style="width:100%;border:1px solid #ccc;margin-bottom:10px;"><tr><th>Array</th><th>Stage</th><th>Index</th><th>Name</th></tr>';
            for (var i=0; i<state.length; i++)
            {
                html += '<tr>';
                html += '<td>'+ state[i].array +'</td>';
                html += '<td>'+ state[i].stage +'</td>';
                html += '<td>'+ state[i].index +'</td>';
                html += '<td>'+ state[i].name +'</td>';
                html += '</tr>';
            }
            html += '</table>';
            sorting.innerHTML = html;
        }
        
        function debugQueue(queueJSON)
        {
        
            if (!_debug) return;
            var div = document.getElementById('debugrotator_queue');
            if (!div) return;    
            
            var queue = eval('('+ queueJSON +')');
            var html = '<table border="1" style="width:100%;border:1px solid #ccc;margin-bottom:10px;"><tr><th>Queue</th></tr>';
            for (var i=0; i<queue.length; i++)
            {
                html += '<tr>';
                html += '<td>'+ queue[i] +'</td>';
                html += '</tr>';
            }
            html += '</table>';
            div.innerHTML = html;
        }

        // =============================================================================================================================

        // HTML fallback:

        function HTMLalternative(json)
        {
            var transitions = Tangora.ImageRotatorManager.GetHtmlTransitions();
            var settings = typeof(json) == 'object' ? json : eval('(' + json + ')');
            var autoStart = settings.autostart;
            var randomStart = settings.randomstart;
            var previewTransitionMode = settings.previewtransition;
            var hideQueuedImages = settings.hidequeuedimages;
            var durationTimer = null;
            var durationStartTime = null;
            var startAfterPause = null;
            var commandQueue = [];

            // Public API

            this.Start = function ()
            {
                if (!_isBusy)
                {
                    if (_isPaused)
                    {
                        if (startAfterPause) startAfterPause();
                        startAfterPause = null;
                    }
                }
                else
                {
                    queueCommand(_object.Start);
                }
            }
            this.Pause = function ()
            {
                if (!_isBusy)
                {
                    if (!_isPaused)
                    {
                        _isPaused = true;
                        clearTimeout(durationTimer);

                        var timeElapsed = (durationStartTime) ? new Date().getTime() - durationStartTime : 0;
                        var timeLeft = Math.max(1, _images[_currentIndex].Duration - timeElapsed);

                        startAfterPause = function ()
                        {
                            durationTimer = setTimeout(swapImages, timeLeft);
                            durationStartTime = new Date().getTime() - timeElapsed;
                            startAfterPause = null;
                            _isPaused = false;
                        }
                    }
                }
                else
                {
                    queueCommand(_object.Pause);
                }
            }
            this.Next = function ()
            {
                if (!_isBusy)
                {
                    swapImages();
                }
                else
                {
                    queueCommand(_object.Next);
                }
            }
            this.Previous = function ()
            {
                if (!_isBusy)
                {
                    var index = _currentIndex > 0 ? _currentIndex - 1 : _images.length - 1;
                    goto(index);
                }
                else
                {
                    queueCommand(_object.Previous);
                }
            }
            this.Goto = function (index)
            {
                if (!_isBusy)
                {
                    goto(index);
                }
                else
                {
                    queueCommand(_object.Goto, [index]);
                }
            }
            this.Add = function (index, image)
            {
                //if (!_isBusy && (_images.length == 0 || index != _currentIndex))
                if (!_isBusy)
                {
                    if (image.Duration == 'inherit') image.Duration = settings.defaultduration;
                    if (image.Link == 'inherit') link = image.Link = settings.defaultlink;
                    if (image.NweWindow == 'inherit') image.NewWindow = settings.defaultnewwindow;
                    if (image.GetTransitions() == 'inherit')
                    {
                        // TODO: - transitions not handled yet...
                    }

                    if (index == _images.length)
                    {
                        _images.push(image);
                    }
                    else
                    {
                        _images.splice(index, 0, image);
                        for (var i = _images.length - 1; i > index; i--)
                        {
                            getImg(i - 1).setAttribute('id', id + '_' + i);
                        }
                    }

                    var wrapper = document.getElementById('imagerotator_htmlwrapper_' + id);
                    var img = document.createElement('img');
                    img.id = id + '_' + index;
                    img.src = image.Source;
                    img.width = settings.width;
                    img.height = settings.height;
                    img.style.visibility = (hideQueuedImages) ? 'hidden' : 'visible';
                    img.style.position = 'absolute';

                    if (_images.length == 0 || index == _images.length - 1)
                    {
                        wrapper.appendChild(img);
                    }
                    else
                    {
                        wrapper.insertBefore(img, getImg(index + 1));
                    }
                    setCssIndexOrder();
                }
                else
                {
                    queueCommand(_object.Add, [index, image]);
                }
            }
            this.Remove = function (index)
            {
                if (!_isBusy && (_images.length == 1 || index != _currentIndex))
                {
                    _images.splice(index, 1);
                    var img = getImg(index);
                    img.parentNode.removeChild(img);
                    for (var i = index; i < _images.length; i++)
                    {
                        var elm = document.getElementById(id + '_' + (i + 1));
                        elm.setAttribute('id', id + '_' + i);
                    }
                    setCssIndexOrder();
                    if (_images.length < 2)
                    {
                        ImageRotator(id).Pause();
                        _currentIndex = 0;
                    }
                }
                else
                {
                    queueCommand(_object.Remove, [index]);
                }
            }
            this.ClearCommandQueue = function ()
            {
                while (commandQueue.length > 0) commandQueue.pop();
            }
            this.Debug = function ()
            {
                alert('Debug is not supported in HTML mode.\nUse DOM explorer instead.');
            }



            function queueCommand(cmd, args)
            {
                if (!args) args = [];
                commandQueue.push([cmd, args]);
            }

            function executeCommandQueue()
            {
                // We need to only execute the number of queued commands present at this time, because looping through this, commands might be queued again.
                var cmdCount = commandQueue.length;
                for (var i = 0; i < cmdCount; i++)
                {
                    var cmd = commandQueue.shift();
                    if (typeof (cmd[0]) == 'function')
                    {
                        var argsCount = cmd[1].length;
                        switch (argsCount)
                        {
                            case 0: cmd[0](); break;
                            case 1: cmd[0](cmd[1][0]); break;
                            case 2: cmd[0](cmd[1][0], cmd[1][1]); break;
                            default: throw new Error('ImageRotator: Number of arguments not supported.');
                        }
                    }
                }
            }





            init();

            function init()
            {
                if (randomStart)
                {
                    _currentIndex = Math.floor(Math.random() * _images.length);
                    setCssIndexOrder();
                }

                if (previewTransitionMode) _isPaused = false;
                else _isPaused = !autoStart || _images.length < 2;

                if (_isPaused)
                {
                    startAfterPause = function ()
                    {
                        durationTimer = setTimeout(swapImages, _images[_currentIndex].Duration);
                        durationStartTime = new Date().getTime();
                        startAfterPause = null;
                        _isPaused = false;
                    }     
                }

                if (_images.length == 0)
                {
                    // waiting for images to added via the API
                    _isBusy = false;
                    return;
                }
                

                if (hideQueuedImages)
                {
                    // if queued images are not visible, run transition in on the first image
                    doTransition('in', startDuration);
                    // make sure image is visible but after transition is started to avoid flicker (this is normally handled in the goto function except for this first transition)
                    getCurrImg().style.visibility = 'visible';
                }
                else
                {
                    // if queued images are visible, skip first transition in and start with duration
                    startDuration();
                }
            }

            // ------------------------------------------------

            
            function getCurrImg() { return document.getElementById(id + '_' + _currentIndex); }
            function getNextImg() { return document.getElementById(id + '_' + (_currentIndex < _images.length - 1 ? _currentIndex + 1 : 0)); }
            function getPrevImg() { return document.getElementById(id + '_' + (_currentIndex > 0 ? _currentIndex - 1 : _images.length - 1)); }
            function getImg(index) { return document.getElementById(id + '_' + index); }


            function startDuration()
            {
                var objImg = _images[_currentIndex];
                var elmImg = getCurrImg();

                getCurrImg().style.visibility = 'visible';

                // activating link if any
                if (objImg.Link != '')
                {
                    elmImg.onclick = function ()
                    {
                        if (objImg.NewWindow) window.open(objImg.Link, 'rotatorimage'+ _currentIndex +'_link', 'location=1,resizable=1,toolbar=1,scrollbars=1,status=1'); 
                        else location.href = objImg.Link;
                    }
                    elmImg.style.cursor = 'pointer';
                }

                // beginning duration if not paused
                if (!_isPaused)
                {
                    durationTimer = setTimeout(swapImages, objImg.Duration);
                    durationStartTime = new Date().getTime();
                }

                // dispatching OnImageBegin event if subscriber
                if (obj.OnImageBegin) obj.OnImageBegin();

                _isBusy = false;

                executeCommandQueue();
            }


            function goto(index)
            {
                if (!previewTransitionMode && _images.length < 2) return;

                _isBusy = true;

                clearTimeout(durationTimer);
                durationStartTime = null;

                // dispatching OnImageEnd event if subscriber
                if (obj.OnImageEnd) obj.OnImageEnd();

                var oldImg = getCurrImg();
                var newImg = getImg(index);

                // clearing link on old image
                oldImg.onclick = null;
                oldImg.style.cursor = 'default';

                // resetting all images z-index
                for (var i = 0; i < _images.length; i++) getImg(i).style.zIndex = 0;

                // setting old and new z-index
                oldImg.style.zIndex = _images.length;
                newImg.style.zIndex = _images.length - 1;

                // transition old image out
                doTransition('out', callbackTransitionOutComplete);

                function callbackTransitionOutComplete()
                {
                    if (hideQueuedImages)
                    {
                        oldImg.style.visibility = 'hidden';
                    }

                    newImg.style.visibility = 'hidden';

                    // Set old behind new
                    oldImg.style.zIndex = _images.length - 1;
                    newImg.style.zIndex = _images.length;

                    _currentIndex = index;

                    // dispatching OnImageChange event if subscriber
                    if (obj.OnImageChange) obj.OnImageChange();

                    // transition new image in
                    doTransition('in', callbackTransitionInComplete);
                    getCurrImg().style.visibility = 'visible';
                }

                function callbackTransitionInComplete()
                {
                    // reordering the images z-index
                    setCssIndexOrder
                    startDuration();
                }
            }


            function setCssIndexOrder()
            {
                if (_images.length < 2) return;
                getCurrImg().style.zIndex = _images.length;
                var zIndex = _images.length - 1;
                var imgIndex = _currentIndex + 1;
                while (imgIndex < _images.length)
                {
                    getImg(imgIndex).style.zIndex = zIndex;
                    imgIndex++;
                    zIndex--;
                }
                imgIndex = 0;
                while (imgIndex < _currentIndex)
                {
                    getImg(imgIndex).style.zIndex = zIndex;
                    imgIndex++;
                    zIndex--;
                }
            }

            function swapImages()
            {
                var index = _currentIndex < _images.length - 1 ? _currentIndex + 1 : 0;
                goto(index);
            }

            function doTransition(direction, callback)
            {
                var trans = null;
                if (previewTransitionMode)
                {
                    if (settings.previewtransitionobj.direction == direction) trans = settings.previewtransitionobj;
                }
                else
                {
                    var transArray = _images[_currentIndex].GetTransitions().split(',');
                    for (var i = 0; i < transArray.length; i++)
                    {
                        if (transArray[i] == '-1')
                        {
                            // Default HTML fallback when no other fallback is specified...
                            if (direction == 'in') trans = getDefaultFallbackTransition(direction);
                            else if (hideQueuedImages) trans = getDefaultFallbackTransition(direction);
                        }
                        else
                        {
                            if (typeof (transitions['trans_' + transArray[i]]) == 'undefined') continue;
                            if (transitions['trans_' + transArray[i]].direction == direction)
                            {
                                trans = transitions['trans_' + transArray[i]];
                                break;
                            }
                        }
                    }
                }
                if (!trans)
                {
                    // if no transition found just call the callback and leave...
                    callback();
                    return;
                }
                var funcs = [];
                var resetFuncs = [];
                var funcsCompleted = 0;
                for (var i = 0; i < trans.effects.length; i++)
                {
                    var func = null;
                    switch (trans.effects[i].name)
                    {
                        case 'htmlfade': funcs.push(htmlfade) ; break;
                        case 'htmlwipe': funcs.push(htmlwipe); break;
                    }
                }
                // executing each of the effect functions
                if (funcs.length > 0)
                {
                    for (var i = 0; i < funcs.length; i++) funcs[i](direction, trans.duration, trans.effects[i], internalCallback);
                }
                else callback();
                // called from effect functions
                function internalCallback(resetFunc)
                {
                    resetFuncs.push(resetFunc);
                    funcsCompleted++;
                    if (funcsCompleted == funcs.length)
                    {
                        for (var i = 0; i < resetFuncs.length; i++) resetFuncs[i]();
                        callback();
                    }
                }
            }

            function ease(type, direction, val)
            {
                if (direction == 'in') val = 100 - val;
                var retval = val;
                switch (type)
                {
                    case 'regular':
                        var log = 10;
                        retval = (Math.log(((val * log) / 100) + 1) / Math.log(log)) * 100;
                        break;
                    case 'strong':
                        var log = 50;
                        retval = (Math.log(((val * log) / 100) + 1) / Math.log(log)) * 100;
                        break;
                }
                if (direction == 'in') retval = 100 - retval;
                return Math.max(Math.min(retval, 100), 0);
            }

            function getDefaultFallbackTransition(direction)
            {
                var easing = {};
                easing.method = '';
                easing.type = 'none';
                var effect = {};
                effect.name = 'htmlfade';
                effect.easing = easing;
                var trans = {};
                trans.direction = direction;
                trans.duration = 1000;
                trans.effects = [];
                trans.effects.push(effect);
                return trans;
            }


            // Transitions effects ------------------------------------------------------

            function htmlfade(direction, duration, effect, callback)
            {
                var elm = getCurrImg();
                var startTime = new Date().getTime();
                var opacity = direction == 'in' ? 0 : 100;
                var timer = null;
                fade();
                function fade()
                {
                    var elapsed = new Date().getTime() - startTime;
                    var percent = Math.max(Math.min((elapsed / duration) * 100, 100), 0);
                    var dist = ease(effect.easing.type, effect.easing.method, Math.floor(percent));
                    opacity = direction == 'in' ? Math.floor(dist) : Math.floor(100 - dist);
                    setOpacity();
                    if ((direction == 'in' && opacity < 100) || (direction == 'out' && opacity > 0)) timer = setTimeout(fade, 10);
                    else callback(reset);
                }
                function setOpacity()
                {
                    elm.style.opacity = (opacity / 100).toString();

                    //if (navigator.userAgent.indexOf('MSIE') != -1)
                    if (typeof (document.createElement("div").style.opacity) == 'undefined')
                    {
                        // IE 8 and below - elm needs to be visible to have filter applied....
                        var visibility = elm.style.visibility;
                        elm.style.visibility = 'visible';
                        elm.style.filter = 'Alpha(Opacity=' + opacity + ')';
                        elm.style.visibility = visibility;
                    }
                }
                function reset()
                {
                    if (timer) clearTimeout(timer);
                    opacity = 100;
                    setOpacity();
                }
            }

            function htmlwipe(direction, duration, effect, callback)
            {
                var elm = getCurrImg();
                var startTime = new Date().getTime();
                var top = 0;
                var right = elm.width;
                var bottom = elm.height;
                var left = 0;
                var timer = null;
                wipe();
                function wipe()
                {
                    var elapsed = new Date().getTime() - startTime;
                    var percent = Math.max(Math.min((elapsed / duration) * 100, 100), 0);
                    var dist = ease(effect.easing.type, effect.easing.method, Math.floor(percent));
                    switch (effect.startpointvertical)
                    {
                        case 'top':
                            bottom = Math.floor((dist * elm.height) / 100);
                            if (direction == 'out') bottom = elm.height - bottom;
                            break;
                        case 'bottom':
                            top = Math.floor(elm.height - ((dist * elm.height) / 100));
                            if (direction == 'out') top = elm.height - top;
                            break;
                    }
                    switch (effect.startpointhorizontal)
                    {
                        case 'left':
                            right = Math.floor((dist * elm.width) / 100);
                            if (direction == 'out') right = elm.width - right;
                            break;
                        case 'right':
                            left = Math.floor(elm.width - ((dist * elm.width) / 100));
                            if (direction == 'out') left = elm.width - left;
                            break;
                    }
                    elm.style.clip = 'rect(' + top + 'px,' + right + 'px,' + bottom + 'px,' + left + 'px)';
                    if (percent >= 100) callback(reset);
                    else timer = setTimeout(wipe, 10);
                }
                function reset()
                {
                    if (timer) clearTimeout(timer);
                    elm.style.clip = 'rect(auto, auto, auto, auto)';
                }
            }
        }
    }
}
