﻿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.RegisterOneShot = function (who, what, where)
	{
		observers.push({ "who": who, "what": what, "where": where, "oneshot": true });
	}

	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 oneShots = [];

		var listeners = this._findObservers(who, what);
		for (var i = 0; i < listeners.length; i++)
		{
			listeners[i].where.call(who, who, what, data);

			if (listeners[i].oneshot) oneShots.push(listeners[i]);
		}

		while (oneShots.length > 0)
		{
			for (var j = 0; j < observers.length; j++)
				if (observers[j] == oneShots[0]) observers.splice(j, 1); break;

			oneShots.shift();
		}
	}
}



// 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;
					gotoFunc(index);
				}
				else
				{
					queueCommand(_object.Previous);
				}
			}
			this.Goto = function (index)
			{
				if (!_isBusy)
				{
					gotoFunc(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 gotoFunc(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;
				gotoFunc(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)';
				}
			}
		}
	}
}

