(function($) {
	var DMZoomer = function(element, userOptions) {
		var defaults = {
			articleClass: '.article',
			articleScaleMax: 1.5,
			articleScaleMin: .25,
			articleMoveSpeed: 750,
			frontArticleFadeModifier: 2,
			snapScrollPercent: .8,
			snapEnabled: true
		};
		
		var instance = this;
		var zoomerElement = $(element);
		var options = $.extend({}, defaults, userOptions);
		var articles = zoomerElement.find(options.articleClass);
		var currentArticleIndex = 0;
		
		this.totalArticles = articles.length;
		this.currentArticleIndex = 0;

		var init = function() {
			// Reverse the stack order of the articles
			for (var i = 0; i < articles.length; i++) {
				$(articles[i]).css('z-index', articles.length - i);
			}
	
			// Add a tall element after the zoomer to force a scrollbar to show
			zoomerElement.after('<div style="height:' + (articles.length * 1000) + 'px"></div>');
	
			// Have the window scrollbar zoom the articles
			$(window).bind('scroll', windowScrollHandler);
		};
		
		// Scale and fade the articles to the current scrollbar location
		var windowScrollHandler = function() {
			var scrollBarLength = $(document).height() - $(window).height();
			var scrollPercent = $(window).scrollTop() / scrollBarLength;
			var totalArticleScroll = totalArticleScroll = (articles.length - 1) * scrollPercent;
			var articleScrollPercent = totalArticleScroll % 1;
			var frontArticleIndex = Math.floor(totalArticleScroll);
	
			if (scrollPercent == 1) {
				articleScrollPercent = 1;
				frontArticleIndex = articles.length - 2;
			}
	
			var backArticleIndex = frontArticleIndex + 1;
	
			// Snap to article
			if (options.snapEnabled && backArticleIndex < (articles.length - 1) && articleScrollPercent > options.snapScrollPercent) {
				totalArticleScroll = Math.ceil(totalArticleScroll);
				articleScrollPercent = 1;
			}
	
			// Transition articles
			for (var i = 0; i < articles.length; i++) {
				switch (i) {
					case frontArticleIndex:
						setScale(articles[i], 1 + (options.articleScaleMax - 1) * articleScrollPercent);
						setOpacity(articles[i], 1 - articleScrollPercent * options.frontArticleFadeModifier);
						break;
					case backArticleIndex:
						setScale(articles[i], options.articleScaleMin + (1 - options.articleScaleMin) * articleScrollPercent);
						setOpacity(articles[i], articleScrollPercent);
						break;
					default:
						setOpacity(articles[i], 0);
						break;
				}
			}
			
			// FIXME totalArticleScroll is usually a floating point and currentArticleIndex should really always just be a int.
			currentArticleIndex = totalArticleScroll;
			
			// FIXME And this is just ultra hacky...
			instance.currentArticleIndex = currentArticleIndex;
			
			// FIXME This should be only called when the int of currentArticleIndex changes.
			instance.articleChanged();
		};
		
		this.articleChanged = function() {
		};
	
		// Set the opacity and hide the element if 0
		var setOpacity = function(element, opacity) {
			$(element).css('visibility', (opacity > 0 ? 'visible' : 'hidden'));
			$(element).css('opacity', opacity);
		};
	
		// Set scale for element in all browsers.
		var setScale = function(element, scale) {
			$(element).css('-webkit-transform', 'scale( ' + scale + ')');
			$(element).css('-moz-transform', 'scale( ' + scale + ')');
			$(element).css('-ms-transform', 'scale( ' + scale + ')');
			$(element).css('-o-transform', 'scale( ' + scale + ')');
			$(element).css('transform', 'scale( ' + scale + ')');
		};
	
		this.prevArticle = function(animate) {
			var prevArticleIndex = currentArticleIndex - 1;
			
			if (currentArticleIndex % 1 != 0) {
				prevArticleIndex = Math.floor(currentArticleIndex);
			}
			
			if (prevArticleIndex < 0) {
				prevArticleIndex = 0;
			}
	
			showArticle(prevArticleIndex, animate);
		};
		
		this.nextArticle = function(animate) {
			var nextArticleIndex = currentArticleIndex + 1;
	
			if (currentArticleIndex % 1 != 0) {
				nextArticleIndex = Math.ceil(currentArticleIndex);
			}
	
			if (nextArticleIndex > articles.length - 1) {
				nextArticleIndex = articles.length - 1;
			}
	
			showArticle(nextArticleIndex, animate);
		};
	
		// Scroll to article with id
		this.showArticleId = function(articleId, animate) {
			var articleIndex = 0;
	
			for (var i = 0; i < articles.length; i++) {
				if (articles[i].id == articleId) {
					articleIndex = i;
					break;
				}
			}
	
			showArticle(articleIndex, animate);
		};

		var showArticle = function(articleIndex, animate) {
			var scrollBarLength = $(document).height() - $(window).height();
			var articleScrollPos = scrollBarLength / (articles.length - 1) * articleIndex;
	
			if (animate) {
				options.snapScrollPercent = .98;
	
				$('html,body').animate({ scrollTop:articleScrollPos }, options.articleMoveSpeed, 'swing', function(){ options.snapScrollPercent = .8; });
			} else {
				$('html,body').scrollTop(articleScrollPos);
			}
	
			currentArticleIndex = articleIndex;
		};
		
		init();
	};

	$.fn.dmzoomer = function(userOptions) {
		return this.each(function() {
			if ($(this).data('dmzoomer')) {
				return;
			} else {
				$(this).data('dmzoomer', new DMZoomer(this, userOptions));
			}
		});
	};
})(jQuery);
