/* js/global.js for Appspin site
   site-wide functionality */

var $j = jQuery.noConflict();

/******************************************************************************
	Extending jQuery */

$j.urlParam = function(name) {
	var results = new RegExp('[\\?&]' + name + '=([^&#]*)').exec(window.location.href);
	if (!results) { return 0; }
	return results[1] || 0;
}

$j(document).ready(function() {

	// external links
	$j('a.external').click(function() {
		var linkClasses = this.className.split(' ');
		window.open(this.href, linkClasses[2], linkClasses[1].replace(/-/g, '=').replace(/_/g, ','));
		return false;
	});
	
	// contact box popup - see http://colorpowered.com/colorbox/
	$j('#contact-link a').colorbox({
			height: '520',
			iframe: true,
			width: '932'
		});
	
	// footer link popups
	$j('#footer a').colorbox({
			height: '500',
			iframe: true,
			width: '500'
		});
	
	// add background tags
	var hour = (new Date()).getHours();
	var timeOfDayClass = '';
	if (hour < 7 || hour >= 19) {
		timeOfDayClass = 'night';
	}
	else if (hour > 7 && hour <= 15) {
		timeOfDayClass = 'day';
	}
	else {
		timeOfDayClass = 'afternoon';
	}
	$j('body').append('<div id="background" class="' + timeOfDayClass + '"><div></div></div>');

	// add markup to buttons and tabs to fix UI of same
	$j('a.btn-more').append('<span></span>');
	$j('a.btn-back').prepend('<span></span>');
	$j('h2.arrow-right').append('<span>&nbsp;</span>');

	// slideshows
	initSlideshow();
});


/******************************************************************************
	Slideshow */

var slideshowTransitionInterval = null;
var slideshowTransitionSlide = null;

function initSlideshow() {
	var numSlideshowItems = $j('ul.slideshow').children().length;  // avoids child selector bug in IE
	var allowAutoRotate = $j('ul.slideshow').hasClass('transition-slideOff');
	
	if (numSlideshowItems) {
		
		var $slides = $j('li.slide');
		
		// determine first slide
		var currentSlideIndex = $j.urlParam('slide') || 1;
		var currentSlide = $j('li.slide:nth-child(' + currentSlideIndex + ')').addClass('current');
			
		if (numSlideshowItems > 1) {
			
			// build slideshow nav
			var $slideshowMap = $j('ul.slideshow-map');
			for (var slideshowItemIndex = 1; slideshowItemIndex <= numSlideshowItems; slideshowItemIndex++) {
				$slideshowMap.append('<li class="clickable slide' + slideshowItemIndex + (slideshowItemIndex == currentSlideIndex ? ' current' : '') + '">' + slideshowItemIndex + '</li>');
			}
			
			// set up auto-rotation
			if (!$j.urlParam('slide') && allowAutoRotate) {
				var slideshowInterval = setInterval("autoRotate()", 8000);
			}
			
			// slide nav click: switch current slide and stop auto-rotation
			$j('ul.slideshow-map li').click(function() {
					
					// show slide
					showSlide(this);
					
					// turn off auto-rotation
					clearTimeout(slideshowInterval);
				});
			
			// slide mouseover: stop auto-rotation
			$slides.mouseover(function() {
					clearTimeout(slideshowInterval);
				});
			
			// slide mouseout: start auto-rotation
			$slides.mouseout(function() {
					clearTimeout(slideshowInterval);
					if (allowAutoRotate) {
						slideshowInterval = setInterval("autoRotate()", 8000);
					}
				});
		}
		else {
			$j('ul.slideshow').css('margin-top', '10px');
		}
		
		// initialization variable
		$slides.each(function() {
				this.initialized = false;
			});
		
		// initialize first slide
		initSlide(currentSlide[0]);
	}
}

function showSlide(slideLink) {

	// check for transitions
	var oldSlide = $j('ul.slideshow li.current');
  if ($j('ul.slideshow').hasClass('transition-slideOff')) {
    if (slideshowTransitionSlide) {
      slideshowTransitionEnd(slideshowTransitionSlide);
    }
    oldSlide.addClass('slideOff');
    slideshowTransitionInterval = setInterval('slideshowSlideOff(' + (oldSlide.width() / 15) + ')', 50);
    slideshowTransitionSlide = oldSlide;
	}
  
  // remove "current" classes
	oldSlide.removeClass('current');
	$j('ul.slideshow-map li.current').removeClass('current');
	
	// set new "current" classes
	var classNames = slideLink.className;
	var slideId = classNames.replace(/clickable slide/, '');
	var $slide = $j('ul.slideshow').children(':nth-child(' + slideId + ')');  // avoids child selector bug in IE
	$j(slideLink).addClass('current');
	$slide.addClass('current');

	// do any slide initialization
	var slide = $slide[0];
	if (!slide.initialized) {
		initSlide(slide);
	}
};

function autoRotate() {
	var nextSlideLink = $j('ul.slideshow-map li.current + li');
	if (nextSlideLink.length == 0) {
		nextSlideLink = $j('ul.slideshow-map li:first');
	}
	showSlide(nextSlideLink[0]);
}

function initSlide(slide) {

	// screenshot magnifier slides
	var $screenshots = $j('div.screenshot img', $j(slide));
	if ($screenshots.length > 0) {
		$screenshots.each( function(index, element) {
				new Magnifier(element);
			});
	}

	// carosel slides
	var $carosel = $j('ul.carosel', $j(slide));
	if ($carosel.length > 0) {
		$carosel.each( function(index, element) {
				(new Carosel(this)).startRotate(1);
			});
	}

	if (slide) {
		slide.initialized = true;
	}
}

function slideshowSlideOff(step) {
  var slide = slideshowTransitionSlide;
	var left = parseInt(slide.css('left')) - step;
	var width = slide.width();
	
  slide.css('left', left + 'px');
	slide.css('clip', 'rect(0 ' + width + 'px ' + slide.height() + 'px ' + (-left) + 'px)');
	
	if (-left >= width) {
		slideshowTransitionEnd(slide);
	}
}

function slideshowTransitionEnd(slide) {
	slide.removeClass('slideOff');
	slide.css('left', 0);
	slide.css('clip', 'rect(0 ' + slide.width() + 'px ' + slide.height() + 'px 0)');
	//slide.css('clip', 'auto');  causes error in IE
	clearInterval(slideshowTransitionInterval);
	slideshowTransitionSlide = null;
}

/******************************************************************************
	Screenshot Magnifier */

function Magnifier(element) {
	var $element = $j(element);
	var elementOffsetLeft = 0;
	var elementOffsetTop = 0;
	var scale = 0;

	var bodyelem = $j('html,body');
	if ($j.browser.safari) { bodyelem = $j('body') }
	
	var $slide = $element.closest('li.slide');
	var slideTop = getOffsetTop($slide[0], true);
	var $lastNote = $j('div.note:last', $slide);
	var lastNoteBottom = 0;
	if ($lastNote.length > 0) {
		lastNoteBottom = getOffsetTop($lastNote[0], false) + $lastNote[0].offsetHeight;
	}
	
	$j($slide).append('<div class="screenshot-magnifier"><img src="' + element.src + '" /></div>');

	var $magnifierPort = $j('div.screenshot-magnifier', $slide);
	var $magnifiedImage = $j('img', $magnifierPort);
	var halfMagnifierPortWidth = $magnifierPort.width() / 2;
	var halfMagnifierPortHeight = $magnifierPort.height() / 2;
	var magnifiedImageWidth = 0;
	var magnifiedImageHeight = 0;
	
	$element.mouseenter( function() {
			$magnifierPort.css({
					'display': 'block',
					'top': lastNoteBottom
				});
			elementOffsetLeft = getOffsetLeft(this, true);
			elementOffsetTop = getOffsetTop(this, true);
			magnifiedImageWidth = $magnifiedImage.width();
			magnifiedImageHeight = $magnifiedImage.height();
			if (scale == 0) {
				scale = magnifiedImageWidth / $element.width();
			}
		});

	$element.mouseleave( function() {
			$magnifierPort.css('display', 'none');
		});

	$element.mousemove( function(e) {
			var x = (e.pageX - elementOffsetLeft) * scale;
			var y = (e.pageY - elementOffsetTop) * scale;

			// prevent magnified image from showing whitespace outside image
			if (x < halfMagnifierPortWidth) { x = halfMagnifierPortWidth; }
			else if (x > magnifiedImageWidth - halfMagnifierPortWidth) { x = magnifiedImageWidth - halfMagnifierPortWidth; }
			if (y < halfMagnifierPortHeight) { y = halfMagnifierPortHeight; }
			else if (y > magnifiedImageHeight - halfMagnifierPortHeight) { y = magnifiedImageHeight - halfMagnifierPortHeight; }

			// update position of magnified image
			$magnifiedImage.css('clip', 'rect(' + (y - halfMagnifierPortHeight) + 'px ' + (x + halfMagnifierPortWidth) + 'px ' + (y + halfMagnifierPortHeight) + 'px ' + (x - halfMagnifierPortWidth) + 'px)');
			$magnifiedImage.css('left', -(x - halfMagnifierPortWidth) + 'px');
			$magnifiedImage.css('top', -(y - halfMagnifierPortHeight) + 'px');
			
			// update position of magnified port so it is in view (to account for page scrolling)
			var magnifierTop = parseInt($magnifierPort.css('top'));
			var pageTop = bodyelem.scrollTop();
			var windowHeight = $j(window).height();
			if (pageTop > slideTop + magnifierTop - 20) {
				$magnifierPort.css('top', (pageTop - slideTop + 20) + 'px');
			}
			else if ( (pageTop + windowHeight < slideTop + magnifierTop + (halfMagnifierPortHeight * 2) + 40) &&
							  (pageTop + windowHeight - slideTop - (halfMagnifierPortHeight * 2) - 40 > lastNoteBottom) ) {
				$magnifierPort.css('top', (pageTop + windowHeight - slideTop - (halfMagnifierPortHeight * 2) - 40) + 'px');
			}
		});
}


/******************************************************************************
	Carosel */

function Carosel(list) {
	var carosel = this;

	var numItemsToShow = this.numItemsToShow = 5;
	this.itemWidth = 255;
	this.radius = 500;
	this.elevation = 100;

	this.$list = $j(list);
	this.viewportWidth = this.$list.width();

	var itemSeparation = 200;
	var rotationChunk = itemSeparation / this.radius;  // in radians
	this.rotationStep = rotationChunk / 10;
	var $items = $j('li', this.$list);

	// jquery's each() screws up scope - using for-loop instead
	for (var index = $items.length - 1; index >= 0; index--) {
		var element = $items[index];

		// store reference to contents
		var $element = $j(element);
		element.$contents = $j('> div', $element);
		element.$imgs = $j('img', $element);

		// calculate angle
		element.angle = (index + 1 - (Math.round(numItemsToShow / 2))) * rotationChunk;
		if (element.angle == 0) {
			this.currentItem = $element;
		}
	}

	var $slide = this.$list.closest('li.slide');
	if ($items.length > this.numItemsToShow) {
		
		// add arrows
		$slide.append('<div class="carosel-back carosel-btn clickable"></div><div class="carosel-forward carosel-btn clickable"></div>');
		
		// arrow functionality
		$j('div.carosel-back', $slide).click( function(e) {
				carosel.startRotate(-1);
			});
		$j('div.carosel-forward', $slide).click( function(e) {
				carosel.startRotate(1);
			});

		// hide any items beyond what should be shown
		$j('li:gt(' + (this.numItemsToShow - 1) + ')', this.$list).css('display', 'none');
	}

	// start rotation animation
	this.startRotate = function(direction) {
		if (Carosel.currentCarosel) {
			return;
		}
		Carosel.currentCarosel = carosel;

		// going forward: show next item along
		if (direction > 0) {
			$j('li:eq(' + this.numItemsToShow + ')', this.$list).css('display', 'block');
			this.currentItem = this.currentItem.next();
		}

		// going back: move last item to start of list and show it
		if (direction < 0) {
			var $oldFirstItem = $j('li:first', this.$list);
			var $newFirstItem = $j('li:last', this.$list).prependTo(this.$list).css('display', 'block');
			$newFirstItem[0].angle = $oldFirstItem[0].angle - rotationChunk;
			this.currentItem = this.currentItem.prev();
		}
		
		// change main link to the current item
		$j('a.carosel-main-link', $slide).attr('href', $j('a', this.currentItem).attr('href'));
		
		Carosel.rotate(direction, rotationChunk);
	}

	// end rotation animation
	this.endRotate = function(direction) {
		Carosel.currentCarosel = null;
		clearTimeout(Carosel.rotationTimer);

		// going forward: move first item to end of list
		if (direction > 0) {
			var $oldLastItem = $j('li:last', this.$list);
			var $newLastItem = $j('li:first', this.$list).appendTo(this.$list);
			$newLastItem[0].angle = $oldLastItem[0].angle + rotationChunk;
		}

		// hide any items beyond what should be shown
		$j('li:gt(' + (this.numItemsToShow) + ')', this.$list).css('display', 'none');
	}
}

Carosel.currentCarosel = null;
Carosel.rotationTimer = null;

Carosel.rotate = function(direction, leftToGo) {
	var carosel = Carosel.currentCarosel;
	if (!carosel) {
		return;
	}

	// update angle for visible items (and, therefore, position)
	// jquery's each() screws up scope - using for-loop instead
	var $items = $j('li', carosel.$list)
	for (var index = $items.length - 1; index >= 0; index--) {
		var element = $items[index];
		element.angle -= carosel.rotationStep * direction;

		if (index <= carosel.numItemsToShow) {
			var elementLeft = (carosel.viewportWidth / 2) + (carosel.radius * Math.sin(element.angle));
			var elementTop = carosel.elevation * Math.cos(element.angle);
			var contentWidth = carosel.itemWidth * Math.cos(element.angle);

			$j(element).css( {
					'left' : elementLeft + 'px',
					'top' : elementTop + 'px',
					'z-index' : Math.round(elementTop)
				});
			element.$contents.css('left', -(contentWidth / 2) + 'px');
			element.$imgs.css('width', contentWidth + 'px');
		}
	}

	// decrement the amount we need to move
	leftToGo -= carosel.rotationStep;

	// if this is the last step in rotation, do clean-up
	if (Math.round(leftToGo * 100) == 0) {
		carosel.endRotate(direction);
	}

	// call next step in rotation
	Carosel.rotationTimer = setTimeout("Carosel.rotate(" + direction + ", " + leftToGo + ")", 20);
}


/******************************************************************************
	Utilities */

function getOffsetTop(element, deep) {
   return getOffsetProperty(element, 'Top', deep);
}

function getOffsetLeft(element, deep) {
   return getOffsetProperty(element, 'Left', deep);
}

function getOffsetProperty(element, property, deep) {
   var offsetValue = 0;
   offsetProperty = 'offset' + property;

   do {
      offsetValue += element[offsetProperty];
      element = element.offsetParent;
   } while (deep == true && element != document.body && element != null);
   return offsetValue;
}

function isDefined(property) {
  return (typeof property != 'undefined');
}

