//
// MW Carousel - (c)2010 Markus von der Wehd / mw@inpublica.de
//

// available config options, all optional, shown values are the defaults
/*
var carouselOptions = {
	// speed of the animation in ms
	animSpeed: 500,

	// enable slideshow
	timerEnabled: false,
	timerDelay: 5000, // delay until the slideshow interval starts
	timerInterval: 5000, // delay between moving slides

	// container/content selectors
	containerSelector: 'div.mw-carousel-container',
	contentSelector: 'div.mw-carousel-content',

	// leave empty to disable all buttons, regardless of button selectors given
	buttonContainerSelector: '',

	// possible buttons
	prevButtonSelector: '',
	stopButtonSelector: '',
	playButtonSelector: '',
	nextButtonSelector: '',

	// possible button hover class
	buttonsHoverClass: '',

	// fade the button container in/out on carouselContainer mouse enter/leave
	buttonContainerFadeEffect: false,
	buttonContainerFadeInSpeed: 300,
	buttonContainerFadeOutSpeed: 600,
	// on mouse leave, delay the fade out, set to 0 to immediately fade out 
	buttonContainerFadeOutDelay: 1000,

	// save current visible div with cookies, start with saved content div on next page view
	saveCursor: false,

	// save timer enabled/disabled with cookies, remember timer state on next page view
	// makes only sense with 'stop' 'play' buttons
	saveTimer: false,

	// if enabled, the order of the content divs is shuffeled on every page load
	// does not make sense together with saveCursor:true!
	shuffleContent: false,
}
var carousel = new MWCarouselClass(carouselOptions);
*/

// ==========================================================================

var MWCarouselClass = function(optionsObj) {
	// default options
	this.animSpeed = 500;
	this.timerEnabled = true;
	this.timerDelay = 7000; // delay until the slideshow interval starts
	this.timerInterval = 7000; // delay between moving slides

	this.containerSelector = 'div.mw-carousel-container';
	this.contentSelector = 'div.mw-carousel-content';

	this.buttonContainerSelector = '';
	this.prevButtonSelector = '';
	this.stopButtonSelector = '';
	this.playButtonSelector = '';
	this.nextButtonSelector = '';
	this.buttonsHoverClass = '';

	this.buttonContainerFadeEffect = false;
	this.buttonContainerFadeInSpeed = 300;
	this.buttonContainerFadeOutSpeed = 600;
	this.buttonContainerFadeOutDelay = 1000;

	this.saveCursor = false;
	this.saveTimer = false;

	this.shuffleContent = false;

	// internals
	this.saveCursorCookie = 'mw_carousel_cursor';
	this.saveTimerCookie = 'mw_carousel_timer';

	this.animDistance = 0;
	this.animStartPos = {};
	this.animParam = {};
	this.animLocked = false;

	this.cursor = 0;
	this.contentObjList = [];
	this.delayRef = null;
	this.intervalRef = null;

	this.buttonContainerObj = null;
	this.buttonDelayRef = null;

	// sync test
	this.syncObj = null;

	// overwrite setup defaults
	MWCarouselClass.setOptions(this, optionsObj);

	// init on DOM ready
	var carouselObj = this;
	$(document).ready(function() {
		carouselObj.init();
	});
};

MWCarouselClass.setOptions = function(updateObj, optionsObj) {
	if (updateObj === undefined || optionsObj === undefined) {
		return;
	}
	for (var optionName in optionsObj) {
		if (optionsObj[optionName] === undefined) {
			continue;
		}
		if (updateObj[optionName] === undefined) {
			continue;
		}
		updateObj[optionName] = optionsObj[optionName];
	}
};

MWCarouselClass.prototype.init = function() {
	var containerObj = $(this.containerSelector).get(0);
	var containerWidth = $(containerObj).width();

	// container width determines a) animation distance b) start pos of hidden div
	this.animDistance = containerWidth;
	this.animStartPos = {
		next: containerWidth,
		prev: containerWidth * -1
	}
	this.animParam = {
		next: '-=' + containerWidth,
		prev: '+=' + containerWidth
	}

	// remember last seen slide
	if (this.saveCursor) {
		var i = this.getCookieInt(this.saveCursorCookie);
		if (i !== null) {
			this.cursor = i;
		}
	}

	// remember timer state
	if (this.saveTimer) {
		var i = this.getCookieInt(this.saveTimerCookie);
		if (i !== null) {
			this.timerEnabled = !!i; // converts truthy value to true, similar to Boolean(), remember: 0 == false, '0' == true
			// this.timerEnabled = (i === 1);
		}
	}

	// preset divs, build linked object list for easier traversing
	var tmpList = $(containerObj).children(this.contentSelector).get();

	if (this.shuffleContent) {
		this.shuffleArray(tmpList);
	}

	var lastIndex = tmpList.length - 1;

	// check/correct cookie given index, because of potential failure if dynamic dynamic build content and old cookie value
	if (this.saveCursor && this.cursor > lastIndex) {
		this.cursor = lastIndex;
	}

	var mwCarouselObj = this;
	$(tmpList).each(function(index) { // $().each() traversal over dom objects
		$(this).css({
			position: 'absolute',
			top: 0,
			left: (index === mwCarouselObj.cursor) ? 0 : containerWidth
		});
		mwCarouselObj.contentObjList.push({
			prev: (index > 0) ? index - 1 : lastIndex,
			next: (index < lastIndex) ? index + 1 : 0,
			obj: this
		});
	});

	// buttons
	if (this.buttonContainerSelector.length > 0) {
		var buttonContainerObj = $(this.buttonContainerSelector).get(0);

		// buttonContainer fade in/out?
		if (this.buttonContainerFadeEffect) {
			// save buttonContainerObj for later usage in event handler
			this.buttonContainerObj = buttonContainerObj;

			$(buttonContainerObj).hide();
			$(containerObj).hover(function(){
					mwCarouselObj.showButtons();
					// $(buttonContainerObj).show();
				}, function() {
					mwCarouselObj.hideButtons();
					// $(buttonContainerObj).hide();
			});
		}

		// button events
		var buttonNames = ['prev', 'stop', 'play', 'next']; // prefixes of properties and methods!
		$.each(buttonNames, function(index, name) { // $.each() traversal over plain arrays
			var buttonSelector = mwCarouselObj[name + 'ButtonSelector'];
			if (buttonSelector.length > 0) {
				var buttonObj = $(buttonSelector, buttonContainerObj).get(0);
				$(buttonObj).click(function() {
					mwCarouselObj[name + 'Clicked']();
				});
				var buttonHoverClass = mwCarouselObj.buttonsHoverClass;
				if (buttonHoverClass.length > 0) {
					$(buttonObj).hover(function() {
							$(this).addClass(buttonHoverClass);
						}, function() {
							$(this).removeClass(buttonHoverClass);
					});
				}
			}
		});
	}

	// set timer animation
	if (this.timerEnabled) {
		this.restartDelay();
	}
};

MWCarouselClass.prototype.nextClicked = function() {
	if (this.timerEnabled) {
		this.restartDelay();
	}
	this.anim('next'); // 'next' used as object property name!
};

MWCarouselClass.prototype.prevClicked = function() {
	if (this.timerEnabled) {
		this.restartDelay();
	}
	this.anim('prev'); // 'prev' used as object property name!
};

MWCarouselClass.prototype.stopClicked = function() {
	if (this.timerEnabled) {
		this.timerEnabled = false;
		this.stopDelay();
		this.stopInterval();
	}
	if (this.saveTimer) {
		var t = (this.timerEnabled) ? '1' : '0';
		this.setCookie(this.saveTimerCookie, t);
	}
};

MWCarouselClass.prototype.playClicked = function() {
	if (! this.timerEnabled) {
		this.timerEnabled = true;
		this.restartInterval();
		this.anim('next');
	}
	if (this.saveTimer) {
		var t = (this.timerEnabled) ? '1' : '0';
		this.setCookie(this.saveTimerCookie, t);
	}
};

MWCarouselClass.prototype.anim = function(direction) {
	if (this.animLocked) return;
	this.animLocked = true;

	var visibleItem = this.contentObjList[this.cursor];
	var hiddenItem = this.contentObjList[visibleItem[direction]];

	// pos hidden obj, animate both
	$(hiddenItem.obj).css({ left: this.animStartPos[direction] });
	$(visibleItem.obj).animate({ left: this.animParam[direction] }, this.animSpeed);

	var mwCarouselObj = this;
	$(hiddenItem.obj).animate({ left: this.animParam[direction] }, this.animSpeed, function() {
		mwCarouselObj.animDone();
	});

	// update cursor
	this.cursor = visibleItem[direction];

	// call possible synced objects
	if (this.syncObj !== null) {
		this.syncObj.anim(direction);
	}
};

MWCarouselClass.prototype.animDone = function() {
	this.animLocked = false;

	if (this.saveCursor) {
		this.setCookie(this.saveCursorCookie, this.cursor);
	}
};

MWCarouselClass.prototype.showButtons = function() {
	this.stopButtonDelay();
	$(this.buttonContainerObj).stop(true, true).fadeIn(this.buttonContainerFadeInSpeed);
};

MWCarouselClass.prototype.hideButtons = function() {
	if (this.buttonContainerFadeOutDelay > 0) {
		// hide delayed
		this.stopButtonDelay(); // cancel timer, probably previously started

		// start new timer
		var mwCarouselObj = this;
		this.buttonDelayRef = window.setTimeout(function() {
			mwCarouselObj.hideButtonsDelayDone();
		}, this.buttonContainerFadeOutDelay);
	} else {
		// hide immediately
		this.hideButtonsDelayDone();
		// $(this.buttonContainerObj).fadeOut(this.buttonContainerFadeOutSpeed); // avoid redundant code
	}
};

MWCarouselClass.prototype.hideButtonsDelayDone = function() {
	this.buttonDelayRef = null;
	$(this.buttonContainerObj).fadeOut(this.buttonContainerFadeOutSpeed);
};

MWCarouselClass.prototype.stopButtonDelay = function() {
	if (this.buttonDelayRef !== null) {
		window.clearTimeout(this.buttonDelayRef);
		this.buttonDelayRef = null;
	}
};

MWCarouselClass.prototype.restartDelay = function() {
	// cancel timer/interval, probably previously started
	this.stopDelay();
	this.stopInterval();

	// start new timer
	var mwCarouselObj = this;
	this.delayRef = window.setTimeout(function() {
		mwCarouselObj.delayDone();
	}, this.timerDelay);
};

MWCarouselClass.prototype.delayDone = function() {
	this.restartInterval();
};

MWCarouselClass.prototype.stopDelay = function() {
	if (this.delayRef !== null) {
		window.clearTimeout(this.delayRef);
		this.delayRef = null;
	}
};

MWCarouselClass.prototype.restartInterval = function() {
	// cancel timer, probably previously started
	this.stopInterval();

	// start new timer
	var mwCarouselObj = this;
	this.intervalRef = window.setInterval(function() {
		mwCarouselObj.intervalTick();
	}, this.timerInterval);
};

MWCarouselClass.prototype.intervalTick = function() {
	this.anim('next');
};

MWCarouselClass.prototype.stopInterval = function() {
	if (this.intervalRef !== null) {
		window.clearInterval(this.intervalRef);
		this.intervalRef = null;
	}
};

MWCarouselClass.prototype.setCookie = function(name, value, expires) {
	var cookie = name + '=' + value;
	if (expires) {
		var expSec = parseInt(expires);
		if (! isNaN(expSec)) {
			var exp = new Date();
			var expNew = exp.getTime() + (expires * 1000);
			exp.setTime(expNew);
			var expStr = '; expires=' + exp.toGMTString();

			cookie += expStr;
		}
	}
	document.cookie = cookie;
};

MWCarouselClass.prototype.getCookie = function(name) {
	if (document.cookie) {
		var re = new RegExp(name + '=([^;]+)', 'i');
		var match = document.cookie.match(re);
		var value = (match !== null) ? RegExp.$1 : null;
		return value;
	} else {
		return null;
	}
};

MWCarouselClass.prototype.getCookieInt = function(name) {
	var value = parseInt(this.getCookie(name), 10);
	return (isNaN(value)) ? null : value;
};

MWCarouselClass.prototype.shuffleArray = function(sortedArray) {
	var arrayLength = sortedArray.length;
	for (var index = 0; index < arrayLength; index++) {
		var randomIndex = Math.floor(Math.random() * arrayLength);
		var tempVal = sortedArray[index];
		sortedArray[index] = sortedArray[randomIndex]; 
		sortedArray[randomIndex] = tempVal;
	}
};

MWCarouselClass.prototype.sync = function(syncObj) {
	// disable all concurrent functions in slave
	syncObj.timerEnabled = false;
	syncObj.saveCursor = false; // to enable this, update syncObj's cookie names (but this should be done at init time! not after)
	syncObj.saveTimer = false;

	this.syncObj = syncObj;
};

