
/**
 * Date Time processor for parsing dates in the form of:
 * Mon, 25 Feb 2008 03:09:22 GMT
 *
 * By default, if a timestamp is within a month, time will be displayed as
 * x days, x months ago. If the timestamp flag is used, it will be either
 * today, tomorrow or a timestamp
 *
 * Possible classes:
 * timestamp  	-  show a timestamp, regardless of when the time was
 * abbr       	-  abbreviates time unit names
 * descr		  	-  append either "ago" or "remaining"
 * notime    	-  ommits time from the display, supported with timestamp
 * timeonly  	-  shows only the time, only works with timestamp
 * printable 	-  adds a span.print version of the time for printable formats
 */
(function() {

	// the units we are using to calculate time differences
	var units = {
		year: 60 * 60 * 24 * 365,
		month: 60 * 60 * 24 * 30,
		week: 60 * 60 * 24 * 7,
		day: 60 * 60 * 24,
		hour: 60 * 60,
		minute: 60,
		second: 1
	};

	// define some abbreviations
	var abbrs = {
		year:'yr', month:'mth', hour:'hr',
		minute:'min', second:'sec'
	};

	// finds the biggest unit possible in the seconds, returns a tuple with
	// the unit, the quantity and the number of seconds to decrement by
	var findBiggestUnit = function(seconds) {
		for (var key in units) {
			var qty = Math.floor(seconds / units[key]);
			if(qty != 0) return [key,qty,units[key] * qty];
		}
	}

	// calculates the time difference between two times
	var timeDifference = function(time1, time2) {
		var elapsed = Math.abs((time2.getTime() - time1.getTime()) / 1000);
		var tokens = [];

		// break into chunks of time, in descending order
		while(!isNaN(elapsed) && (chunk = findBiggestUnit(elapsed))) {
			suffix = chunk[1] > 1 ? 's' : '';
			tokens.push(chunk[1] + ' ' + chunk[0] + suffix);
			elapsed -= chunk[2];
		}

		return tokens;
	}

	// returns true if an element has a particular class
	var hasClass = function(element, className) {
		return (
			element.className &&
			element.className.match( new RegExp( "\\b"+className+"\\b" ) )
			) ? true : false;
	}

	// gets the first text node that is a direct child of the span
	var findSpanTextNode = function(element) {
		for(var i=0; i<element.childNodes.length; i++) {
			if(element.childNodes[i].nodeType == 3) {
				return element.childNodes[i];
			}
		}
	}

	// calculates the difference in two dates in days
	var getDifferenceInDays = function(date1, date2) {
		var date2 = !date2 ? new Date() : date2;
		return (date1.getTime()-date2.getTime()) / (1000 * 60 * 60 * 24);
	}

	// formats the timestamp, optionally uses the dateformat library
	var formatTimestamp = function(time,config) {

		var now = new Date();
		var today = new Date(now.getFullYear(), now.getMonth(),
			now.getDate());
		var yesterday = new Date(now.getFullYear(), now.getMonth(),
			now.getDate()-1);
		var tomorrow = new Date(today.getTime() + 24*3600000);

		// calculate time
		var timetext = ', ' + time.getHours() + ':' +
			((time.getMinutes()>10) ? time.getMinutes() : '0' + time.getMinutes());

		// display a short time if we can
		if(time.getTime() >= (tomorrow.getTime()+24*3600000)) {
			// do nothing for future times
		} else if(time.getTime() >= tomorrow.getTime()) {
			return 'tomorrow' + (config.notime ? '' : timetext);
		} else if(time.getTime() >= today.getTime()) {
			return 'today' + (config.notime ? '' : timetext);
		} else if(time.getTime() >= yesterday.getTime()) {
			return 'yesterday' + (config.notime ? '' : timetext);
		}

		// fall back to a date string
		if(typeof(dateFormat) == 'function') {
			if(config.notime) {
				var monthMask = config.abbreviate ? 'mmm' : 'mmmm';
				var mask = monthMask + ' d, yyyy';
			} else if(config.timeonly) {
				var mask = 'h:MM tt'
			} else {
				var monthMask = config.abbreviate ? 'mmm' : 'mmmm';
				var mask = monthMask + ' d, yyyy h:MM tt';
			}
			return dateFormat(time, mask);
		} else {
			return time.toLocaleString();
		}
	}

	// gets a date object parsed from the span
	var getSpanDate = function(span) {
		var node = findSpanTextNode(span);
		if(!node) return false;
		return span.gmttime ? span.gmttime : new Date(Date.parse(node.nodeValue));
	}

	// process a span with a class of gmttime
	var formatSpan = function(span) {
		var dateString = '';
		var date = getSpanDate(span);
		if(!date) return false;

		var dayDiff = getDifferenceInDays(date);
		var since = dayDiff < 0;

		var config = {
			relative: !hasClass(span, 'timestamp'),
			abbreviate: !hasClass(span, 'timestamp') && hasClass(span,'abbr'),
			timeonly: hasClass(span, 'timeonly'),
			describe: hasClass(span, 'descr') || hasClass(span, 'desc'),
			notime: hasClass(span, 'notime'),
			print: hasClass(span, 'printable')
		}

		// override relative if the date is more than 28 days old
		if(Math.abs(dayDiff) > 28) config.relative = false;

		if(!config.relative) {
			dateString = formatTimestamp(date, config);
		} else if(config.relative && since) {
			dateString = timeDifference(date, new Date())
				.slice(0,2).join(', ');
		} else {
			dateString = timeDifference(date, new Date())
				.slice(0,2).join(', ');
		}

		if(!dateString) {
			dateString = formatTimestamp(date, config);
		} else if(config.describe && config.relative) {
			dateString += since ? ' ago' : ' remaining';
		}

		// optionally use abbreviations
		if(config.abbreviate) {
			for(var a in abbrs) dateString = dateString.replace(a, abbrs[a]);
		}

		// set the title
		span.title = date.toLocaleString();
		span.gmttime = date;

		// replace the value with the formatted time
		var textNode = findSpanTextNode(span);

		screenSpan = document.createElement('span');
		screenSpan.className = 'screen';
		screenSpan.appendChild(document.createTextNode(dateString));
		span.replaceChild(screenSpan,textNode);

		// optionally, add a print only version
		if(config.print) {
			printSpan = document.createElement('span');
			printSpan.className = 'print';
			printSpan.appendChild(document.createTextNode(date.toLocaleString()));
			span.insertBefore(printSpan,screenSpan);
		}
	}

	// function that sets up a span
	var initializeSpan = function(span) {
		// bind some functions to the span
		span.getDate = function() { return getSpanDate(this); }
		span.format = function() { return formatSpan(this); }

		// process the span
		span.format();
	}

	// process span.gmttime
	if(window.jQuery) {

		// add a jQuery plugin
		jQuery.fn.dateTime = function() {
			return this.each(function(){
				initializeSpan(this);
			});
		};

		// process each span
		jQuery('span.gmttime').dateTime();

	// otherwise, do it the old fashioned way
	} else {
		var spans = document.getElementsByTagName('span');
		for(i=0; i<spans.length; i++) {
			if(hasClass(spans[i], 'gmttime')) initializeSpan(spans[i]);
		}
	}

}());

/**
 * Base library for web services
 */

var Service = Service || {};
Service.Base = {};

(function(Base) {

	Base.ServiceException = function(content) {
		this.message = content.errormessage;
		this.errorcode = content.errorcode;
	};

	/**
	 * Gets the url for a method call
	 */
	Base.urlFor = function(service, version, method) {
		return '/api/'+version+'/rest?service='+
			service+'&format=json&method='+method;
	};

	Base.parseResponse = function(text) {
		// TODO: Remove this function
		if (typeof(text) == 'object') return text;
		var json = eval("("+text+")");
		return json;
	};

	/**
	 * Make a call to the service
	 */
	Base.serviceCall = function(url, params, callback) {
		if(callback) {
			jQuery.post(url,params,function(data){
				callback(Base.parseResponse(data));
			});
		} else {
			var json;
			if (params)
			{
				json = Base.parseResponse(
				jQuery.ajax({type:'POST',url:url,async:false,data:params,dataType:'json'}));
			}
			else
			{
				json = Base.parseResponse(
				jQuery.ajax({type:'POST',url:url,async:false,dataType:'json'}));
			}
			// handle failure
			if(json.status == 'fail') {
				throw new Base.ServiceException(json);
			}

			return json.content;
		}
		return undefined;
	};

	Base.call = function (service, version, method, params, callback) {
		var url = Base.urlFor(service, version, method);
		return Base.serviceCall(url, params, callback);
	};

}(Service.Base));
Service.User = {

	getActiveUser: function (context, callback) {
		if (!context) context = 'default';
		this._call('getActiveUser', { context: context }, function (response) {
			callback(response.content);
		});
	},

	_call: function (method, params, callback) {
		if (!callback) callback = function(){}; // prevent synchronous Service.Base.call
		return Service.Base.call('user', 'v1', method, params, callback);
	}

};var UserBar = {
	isLoaded: false,
	isLoggedIn: undefined,
	callbacks: []
};

UserBar.reset = function () {
	jQuery('#nav-account').addClass('ajax');
	this.isLoaded = false;
	this.isLoggedIn = undefined;
	this.callbacks = [];
	return this;
};

UserBar.load = function (context) {

	if (!this._hasLoginCookie()) {
		UserBar._triggerCallbacks(false);
		return this;
	}

	var loginBar = jQuery('#nav-account');

	if (loginBar.hasClass('ajax'))
		UserBar._loadFromAjax(loginBar, context);
	else
		UserBar._loadFromPage(loginBar, context);

	return this;
};

// callbacks triggered after UserBar.load(), or immediately if already loaded.
UserBar.addCallback = function (callback) {
	if (UserBar.isLoaded) {
		callback(UserBar.isLoggedIn);
	} else {
		UserBar.callbacks.push(callback);
	}
};

UserBar._loadFromPage = function (loginBar) {
	UserBar._triggerCallbacks(loginBar.hasClass('loggedin'));
};

UserBar._loadFromAjax = function (loginBar, context) {

	var loginStatus = jQuery('<li><em>Checking Login...</em></li>');
	loginBar.prepend(loginStatus);

	Service.User.getActiveUser(context, function (data) {

		if (!data) {
			loginStatus.remove();
			UserBar._triggerCallbacks(false);
			return;
		}

		var html =
			'<li class="profile"><a class="displayname" href="/users/' + data.userId + '">' +
			'<img src="' + data.avatarSrc + '" title="' + data.displayName + '" alt="" /> ' +
			'<span class="name">' + data.displayName + '</span>' +
			'</a></li>';

		loginBar
			.addClass('loggedin')
			.toggleClass('admin', data.isAdmin)
			.html(html);

		jQuery.each(data.userLinks, function () {
			loginBar.append(jQuery('<li/>').append(UserBar._buildLink(this)));
		});

		UserBar._triggerCallbacks(true);
	});

};

UserBar._triggerCallbacks = function (isLoggedIn) {
	UserBar.isLoaded = true;
	UserBar.isLoggedIn = isLoggedIn;
	var callback;
	while ((callback = UserBar.callbacks.shift())) {
		callback(UserBar.isLoggedIn);
	}
};

UserBar._buildLink = function (data) {
	var text = data['text'];
	data['text'] = undefined;
	return jQuery('<a>' + text + '</a>').attr(data);
};

UserBar._hasLoginCookie = function () {
	return Cookie.get('PHPSESSID') || Cookie.get('usertokens');
};
(function (jQuery) {

	var Carousel = function (list, delay, autorotate) {
		this.list = jQuery(list);
		this.listItems = this.list.find('li').get();
		this.itemWidth = jQuery(this.listItems).width();
		this.animating = false;
		this.autorotate = autorotate == undefined ? true : autorotate;
		this.delay = delay == undefined ? 6000 : delay;
		this.position = 0;

		var carousel = this;
		jQuery(this.listItems).each(function (index, element) {
			jQuery(element).css({
				left: index*carousel.itemWidth,
				display: 'block'
			});
		});

		if (this.autorotate)
		{
			this.autoRotater = setInterval(function () {
				carousel.slideLeft();
			}, this.delay);
		}

		this.list.bind('next', function () {
			carousel.slideLeft();
		});

		this.list.bind('previous', function () {
			carousel.slideRight();
		});
	};

	Carousel.prototype = {
		slideLeft : function () {
			if (!this.animating)
			{
				var carousel = this;
				if (this.autorotate)
				{
					clearInterval(this.autoRotater);
					this.autoRotater = setInterval(function () {
						carousel.slideLeft();
					}, this.delay);
				}
				this.animating = true;
				var position = this.list.position();
				var newPosition = position.left - this.itemWidth;

				var nextItem = jQuery(this.getNextItem());
				var currentItem = jQuery(this.listItems[this.position]);
				nextItem.css('left', currentItem.position().left + this.itemWidth);

				this.list.animate({
					left: newPosition
				}, 1500, 'swing', function () {
					carousel.animating=false;
				});
				this.position = this.getNextPosition();
			}
		},

		slideRight : function () {
			if (!this.animating)
			{
				var carousel = this;
				if (this.autorotate)
				{
					clearInterval(this.autoRotater);
					this.autoRotater = setInterval(function () {
						carousel.slideLeft();
					}, this.delay);
				}
				this.animating = true;
				var position = this.list.position();
				var newPosition = position.left + this.itemWidth;

				var previousItem = jQuery(this.getPreviousItem());
				var currentItem = jQuery(this.listItems[this.position]);
				previousItem.css('left', currentItem.position().left - this.itemWidth);

				this.list.animate({
					left: newPosition
				}, 1500, 'swing', function () {
					carousel.animating=false;
				});
				this.position = this.getPreviousPosition();
			}
		},

		getNextItem : function () {
			return this.listItems[this.getNextPosition()];
		},

		getPreviousItem : function () {
			return this.listItems[this.getPreviousPosition()];
		},

		getNextPosition : function () {
			var nextPosition = this.position + 1;
			return nextPosition % this.listItems.length;
		},

		getPreviousPosition : function () {
			var previousPosition = this.position - 1;
			if (previousPosition < 0)
			{
				previousPosition = this.listItems.length - 1;
			}
			return previousPosition;
		}
	};

	var VerticalCarousel = function (list, delay, autorotate) {
		this.list = jQuery(list);
		this.listItems = this.list.find('li').get();
		this.itemHeight = jQuery(this.listItems).height();
		this.animating = false;
		this.autorotate = autorotate == undefined ? true : autorotate;
		this.delay = delay == undefined ? 6000 : delay;

		var carousel = this;
		jQuery(this.listItems).each(function (index, element) {
			jQuery(element).css({
				top: index*carousel.itemHeight,
				display: 'block'
			});
		});
		this.listItems.unshift(this.listItems.pop());

		if (this.autorotate)
		{
			this.autoRotater = setInterval(function () {
				carousel.slideUp();
			}, this.delay);
		}

		this.list.bind('next', function () {
			carousel.slideUp();
		});

		this.list.bind('previous', function () {
			carousel.slideDown();
		});
	};

	VerticalCarousel.prototype = {
		slideUp : function () {
			if (!this.animating)
			{
				clearInterval(this.autoRotater);
				var carousel = this;
				this.autoRotater = setInterval(function () {
					carousel.slideUp();
				}, this.delay);
				this.animating = true;
				var position = this.list.position();
				var newPosition = position.top - this.itemHeight -1;
				var carousel = this;

				var item = this.listItems.shift();
				jQuery(item).css('top', jQuery(this.listItems[this.listItems.length-1]).position().top + this.itemHeight);
				this.listItems.push(item);

				this.list.animate({
					top: newPosition
				}, 1500, 'swing', function () {
					carousel.animating=false;
				});
			}
		},

		slideDown : function () {
			if (!this.animating)
			{
				clearInterval(this.autoRotater);
				var carousel = this;
				this.autoRotater = setInterval(function () {
					carousel.slideDown();
				}, this.delay);
				this.animating = true;
				var position = this.list.position();
				var newPosition = position.top + this.itemHeight -1;
				var carousel = this;

				var item = this.listItems.pop();
				jQuery(item).css('top', jQuery(this.listItems[0]).position().top - this.itemHeight);
				this.listItems.unshift(item);

				this.list.animate({
					top: newPosition
				}, 1500, 'swing', function () {
					carousel.animating=false;
				});
			}
		}
	};

	jQuery.fn.extend({
		carousel : function (delay, autorotate)
		{
			var carousel = new Carousel(this, delay, autorotate);
			return carousel;
		},
		verticalCarousel : function (delay, autorotate)
		{
			var carousel = new VerticalCarousel(this, delay, autorotate);
			return carousel;
		}
	});
}(jQuery));
// load user login bar via Ajax.
UserBar.load();

/**
 * Handles the display of the twittermonial
 */
(function () {
	var feature = jQuery('div.feature ul.slideshow');

	var featureCarousel = feature.carousel(8000, false);

	var timeoutId = 0;

	var featureAutoRotate = function (direction) {
		clearTimeout(timeoutId);
		feature.trigger(direction);
		var interval = 8000;
		if (direction == 'next' && featureCarousel.getNextPosition() == 0)
		{
			interval = 12000;
		}
		timeoutId = setTimeout(function () { featureAutoRotate('next') ;},interval);
	};

	timeoutId = setTimeout(function () { featureAutoRotate('next'); }, 8000);


	jQuery('div.feature ul.controls .previous').click(function (event) {
		event.preventDefault();
		featureAutoRotate('previous');
	});

	jQuery('div.feature ul.controls .next').click(function (event) {
		event.preventDefault();
		featureAutoRotate('next');
	});

	if (jQuery('ul.twittermonial li').length > 2)
	{
		jQuery('div#ticker ul.controls').css('display', 'block');
		var twittermonial = jQuery('ul.twittermonial');

		twittermonial.verticalCarousel(30000);

		jQuery('ul.twittermonial li').each(function(index, element) {
			tweet = jQuery(element).find('q a');
			tweet.attr({
				'href' : tweet.attr('data-tweeturl'),
				'target' : '_blank'
			});
			author = jQuery(element).find('span.author a');
			author.attr({
				'href' : 'http://twitter.com/'+author.attr('data-author'),
				'target' : '_blank'
			});
		});
	}
}());

/* Guarantee Modal */

(function($) {

	var modalContent = '\
		<div id="guaranteeExplained" class="genericModal"> \
			<div class="modalToolbar"><a class="exitModal" href="#">Close</a></div> \
			<div class="guaranteeIntro">\
				<h2 class="heading">100% money back guarantee</h2>\
				<p class="message">We offer a full <strong>100% money back guarantee</strong> on all design contests. \
				The <em>only exception</em> being if you’ve already awarded a winner and <strong>paid the winning \
				designer</strong>, or you have chosen to run a <strong>guaranteed contest.</strong></p>\
			</div>\
			<div class="faqs">\
				<div class="faqsContent">\
					<ul class="faqsList">\
						<li class="faq">\
							<h3 class="heading">What is a guaranteed contest?</h3>\
							<p>Guaranteeing your contest means that you\'re committing to choosing a winner.\
							In this case, your contest will not be eligible for a refund.</p>\
						</li>\
						<li class="faq">\
							<h3 class="heading">Not happy with the result? No worries!</h3>\
							<p>We\'ll do our best to make you happy!  Our expert support team will help you re-write your design brief, \
							re-open your contest to ensure you get a design you love, and even help you pick a winner.</p>\
							<p>If you\'re still not completely satisfied, simply <a href="/help/contact">contact us</a> within 60 days from the time your payment was \
							processed and we’ll give you <strong>every last cent of your money back (our fees, the prize money, the lot).</strong></p>\
						</li>\
						<li class="faq">\
							<h3 class="heading">What happens to the designs?</h3>\
							<p>Remember, if you choose to initiate a refund against your contest, you are not legally entitled \
							to use any of the designs submitted to your contest.</p>\
						</li>\
					</ul>\
				</div>\
			</div>\
		</div>\
		';

	$(modalContent).prependTo($('#container'));
	$self = $('#guaranteeExplained');
	ie6 = ($.browser.msie && $.browser.version < 7);

	setSelfPosition(ie6);
	$self.css({left: '50%', marginLeft: ($self.outerWidth() / 2) * -1});
	$(window).keypress(observeEscapePress);

	$self.jqm({
		modal: true, 
		trigger: 'a#showGuaranteeExplained',
		toTop: true,
		closeClass: 'exitModal',
		onShow: function(hash) {
			// hide scrollbars on main page in background
			$('body').css({
				position: ($.browser.msie ? 'absolute' : 'fixed'),
				overflow: 'hidden',
				height: '100%',
				width: '100%',
				top: '0px',
				left: '0px'
			});
			hash.w.show();
		},
		onHide: function(hash) {
			// re-enable scrollbars on main page in background
			$('body').css({
				position: 'relative',
				overflow: 'visible',
				height: '100%',
				width: '100%',
				top: '0px',
				left: '0px'
			});
			hash.w.hide();
			hash.o.remove();
		}
	});

  /* Function to bind to the window to observe the escape key press */
  function observeEscapePress(e) {
		if(e.keyCode == 27 || (e.DOM_VK_ESCAPE == 27 && e.which==0)) $self.jqmHide();
  }

	/* Function to vertically center modal */
  function setSelfPosition(ie6) {
		var s = $self[0].style;

    if (($self.height() + 80  >= $(window).height()) && ($self.css('position') != 'absolute' || ie6)) {
			var topOffset = $(document).scrollTop() + 40;
			$self.css({position: 'absolute', top: topOffset + 'px', marginTop: 0});
			if (ie6) {
				s.removeExpression('top');
			}
    } else if ($self.height()+ 80  < $(window).height()) {
			if (ie6) {
				s.position = 'absolute';
				s.setExpression('top', '(document.documentElement.clientHeight || document.body.clientHeight) / 2 - (this.offsetHeight / 2) + (blah = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + "px"');
				s.marginTop = 0;
			} else {
				$self.css({ position: 'fixed', top: '50%', marginTop: ($self.outerHeight() / 2) * -1});
			}
    }

    if (ie6) {
			s.removeExpression('top');
    }
  }

})(jQuery);

