﻿function findLabel(element) {
	var elt = $(element);
	return elt.closest('form').find('label[for=' + elt.attr(elt.is(':hidden') ? 'name' : 'id').replace('.', '\\.') + ']');
}


// LiteBox widget
// new LiteBox({ content: $('#MyLiteBox').show() });
// new LiteBox({ url: '/ContactForm', hash: '#contact' });
function LiteBox(options) {
	this.options = $.extend($.extend({}, LiteBox.defaults), options);
	if (this.options.hash) {
		this._onHashChanged();
		var self = this;
		$(window).hashchange(function() { self._onHashChanged(); });
	} else
		this.show();
}

LiteBox.defaults = {
	url: null,
	content: null,
	hash: null,
	cssClass: null,
	beforeShowing: null,
	speed: 300,
	reusable: true,
	closeable: true
};

LiteBox.prototype = {
	_container: null,
	_content: null,
	_status: null,

	show: function() {
		this._setVisibility(true);
	},

	close: function() {
		this._setVisibility(false);
	},

	remove: function() {
		if (this._container == null)
			return;
		this._container.remove();
		this._container = null;
		this._content = null;
		this._status = null;
	},

	_onHashChanged: function() {
		if (this.options.hash == window.location.hash.toLowerCase())
			this.show();
		else
			this.close();
	},

	_scaleContent: function(visible) {
		var start = visible ? 0.95 : 1;
		var stop = visible ? 1 : 0.95;
		if (visible && typeof this.options.beforeShowing == 'function')
			this.options.beforeShowing(this._content);
		if (this._content && ($.browser.webkit || $.browser.mozilla))
			this._content
				.css({ transform: 'scale(' + start + ')' })
				.transition({
					scale: stop,
					queue: false,
					duration: this.options.speed,
					complete: function() {
						$(this).css({ transform: 'none' });
					}
				});
	},

	_setVisibility: function(visible) {
		if (this._container && this._container.is(':visible') ? visible : !visible)
			return;
		var self = this;
		if (visible) {
			if (this._container) {
				this._container.fadeIn(this.options.speed, function() { self._container.find('[autofocus]').focus(); });
				this._scaleContent(true);
			} else {
				this._content = $('<ins>')
					.addClass('placeholder')
					.click(function(e) { e.stopPropagation(); });
				this._container = $('<section>')
					.addClass('childwindow overlay')
					.append(this._content)
					.appendTo($(document.body))
					.hide().fadeIn(this.options.speed);
				if (this.options.url) {
					this._status = $('<span>')
						.addClass('state-busy')
						.appendTo(this._content);
					$.ajax({
						url: this.options.url,
						cache: false
					}).complete(function() {
						if (self.options.closeable)
							self._container.click(function(e) {
								self._onClick();
							});
					}).success(function(data) {
						self._status.remove();
						self._status = null;
						self._setData(data);
					}).error(function() {
						self._status.removeClass('state-busy').addClass('state-error');
					});
				} else if (this.options.content) {
					if (this.options.closeable)
						this._container.click(function(e) {
							self._onClick();
						});
					this._setData(this.options.content);
				}
			}
		} else if (this._container) {
			this._container.fadeOut(this.options.speed, function() {
				if (!self.options.reusable)
					self.remove();
			});
			this._scaleContent(false);
		}
	},

	_setData: function(data) {
		var self = this;
		var liteBox = $('<div>').addClass('litebox');
		if (this.options.closeable)
			liteBox.append($('<span>').addClass('close').click(function() {
				self._onClick();
			}));
		if (this.options.cssClass)
			liteBox.addClass(this.options.cssClass);
		this._content
			.append(liteBox)
			.hide().fadeIn(this._speed, function() { liteBox.find('[autofocus]').focus(); });
		liteBox.append(data);
		this._scaleContent(true);
	},

	_onClick: function() {
		if (this.options.hash)
			document.location.hash = '/';
		else
			this.close();
	}
};


(function($) {

jQuery.validator.setDefaults({
	highlight: function(element, errorClass) { findLabel(element).addClass('invalid'); this.setSuccess(false); },
	unhighlight: function(element, errorClass) { findLabel(element).removeClass('invalid'); },
	showErrors: function(errorMap, errorList) {
		$(this.settings.errorContainer).attr('class', 'msg error');
		this.defaultShowErrors();
	}
});

jQuery.validator.prototype.setSuccess = function(success) {
	if (this.successTimeoutId)
		clearTimeout(this.successTimeoutId);
	var form = $(this.currentForm);
	if (success) {
		form.addClass('success');
		this.successTimeoutId = setTimeout(function() { form.removeClass('success'); }, 5000);;
	} else {
		form.removeClass('success');
		this.successTimeoutId = null;
	}
}

$.extend({
	visualEdit: function(vedit, options) {
		var elements = $('[vedit=' + vedit + ']');
		return typeof options === 'undefined' ? elements : elements.resource(options);
	}
});

$.fn.extend({
	msgbox: function(clazz, messages) {
		return this.each(function() {
			var container = $(this).empty()
				.attr('class', 'msg')
				.addClass(clazz);
			for (var i = 0; i < messages.length; i++) {
				container.append($('<label>').html(messages[i]));
			}
		});
	},

	msgprogress: function(messages) {
		return this.msgbox('progress', messages);
	},

	msgsuccess: function(messages) {
		var self = $(this);
		setTimeout(function() { self.fadeOut('slow'); }, 5000);
		return this.msgbox('success', messages);
	},

	msgerror: function(messages) {
		return this.msgbox('error', messages);
	},

	msgassert: function(success, messages) {
		return success ? this.msgsuccess(messages) : this.msgerror(messages);
	},

	disableForm: function() {
		return this.find(':input').each(function () {
			if ($(this).is(':not(:disabled)'))
				$(this).attr('enabled', true);
			$(this).enable(false);
		});
	},

	enableForm: function() {
		return this.find(':input').each(function () {
			if ($(this).attr('enabled'))
				$(this).enable(true).removeAttr('enabled');
		});
	},

	defaultValue: function(defaultValue) {
		function blur() {
			var defaultValue = $(this).attr('default');
			if ($(this).val() == defaultValue || $(this).val() == '')
				$(this).val(defaultValue).css('color', 'gray');
		}
		function focus() {
			var defaultValue = $(this).attr('default');
			if ($(this).val() == defaultValue)
				$(this).val('').css('color', 'black');
		}
		return this.each(function() {
			if (typeof defaultValue === 'undefined') {
				defaultValue = $(this).attr('default')
				if (defaultValue)
					$(this).focus(focus).blur(blur);
				else
					return;
			} else {
				focus.apply(this);
				$(this).attr('default', defaultValue);
			}
			blur.apply(this);
		});
	},

	validateFormSubmit: function(options) {
		var dfd = $.Deferred();
		var form = $(this);
		var errorContainer = $(form.validate().settings.errorContainer);
		var progressMessage = form.attr('data-progressMessage');
		if (progressMessage)
			errorContainer.msgprogress([progressMessage]).show();
		else
			errorContainer.msgprogress([]).hide();
		form.removeClass('success error').ajaxSubmit($.extend({
			cache: false,
			dataType: 'json',
			success: function(data, status, request) {
				if (data.errors) {
					errorContainer.msgerror(data.errors);
					dfd.reject(data);
				} else {
					var successMessage = form.attr('data-successMessage');
					if (successMessage)
						errorContainer.msgsuccess([successMessage]);
					form.validate().setSuccess(true);
					if (data.returnUrl) {
						document.location.href = data.returnUrl;
						return;
					}
					if (data.reload) {
						document.location.reload()
						return;
					}
					dfd.resolve(data);
				}
				errorContainer[errorContainer.is(':empty') ? 'hide' : 'show']();
				form.enableForm();
			},
			error: function (request, status, error) {
				var errorMessage = form.attr('data-errorMessage');
				if (errorMessage)
					errorContainer.msgerror([errorMessage]).show();
				form.addClass('error').enableForm();
				dfd.reject(null);
			},
			complete: function(request, status) {
				form.removeClass('busy');
			}
		}, options || {})).addClass('busy').disableForm();
		return dfd.promise();
	}
});

$.widget("ui.resource", {
	options: {
		showDelay: 0,
		hideDelay: 300,
		mode: '*',
		multiline: false,
		txtEdit: 'click to edit',
		txtCancel: 'Cancel',
		txtSubmit: 'Apply'
	},

	_create: function() {
		var self = this;
		if (!$.ui.resource._visualEdit)
			$.ui.resource._visualEdit = $('<div>').addClass('visualedit')
				.mouseover($.ui.resource._resetHideTimeout)
				.mouseout($.ui.resource._onMouseOut)
				.hide()
				.appendTo($(document).mousedown($.ui.resource._checkExternalClick).find('body'));
		var element = this.element.parents('a,button');
		if (element.length == 0)
			element = this.element;
		element
			.not('a,button').click(
				function(ev) {
					if ($.ui.resource._visualEdit.data('owner') == self)
						self._onClick(ev);
				}).end()
			.mouseover(function(ev) { self._onMouseOver(ev); })
			.mouseout($.ui.resource._onMouseOut);
	},

	_openPopup: function() {
		var offset = this.element.offset();
		var self = this;
		$.ui.resource._visualEdit
			.data('owner', this)
			.empty()
			.append($('<a>').addClass('icon-edit').attr('href','#').text(self.options.txtEdit).click(function(ev) { self._onClick(ev); }))
			.css('top', offset.top + this.element.innerHeight())
			.css('left', offset.left)
			.show();
	},

	_onClick: function(ev) {
		var self = this;
		var container = $.ui.resource._visualEdit.attr('editMode', true);
		var indicator = container.children('a').addClass('icon-busy');
		jQuery.ajax({
			type: 'GET',
			url: this.options.url,
			data: { key: this.options.key },
			cache: false,
			success: function(text) {
				$('<form method="get">' +
				  (self.options.multiline ? '<textarea rows="3" />' : '<input type="text" />') +
				  '<div class="actions">' +
				  '<a class="icon-cross" href="#"></a>' +
				  '<input type="submit" />' +
				  '</div>' +
				  '</form>')
				.appendTo(container.empty())
				.find('textarea,input[type="text"]').val(text).focus().end()
				.find('a').text(self.options.txtCancel).click(function(ev) { $.ui.resource._closePopup(); ev.preventDefault(); }).end()
				.find('input[type="submit"]').val(self.options.txtSubmit).end()
				.submit(function(ev) { self._save(); ev.preventDefault(); });
			},
			error: function(request, status, error) {
				container.removeAttr('editMode');
				indicator.addClass('icon-error red');
			},
			complete: function(request, status) {
				indicator.removeClass('icon-busy');
			}
		});
		ev.preventDefault();
	},

	_setValue: function(value) {
		$.visualEdit(this.element.attr('vedit')).each(function() {
			switch ($(this).data('resource').options.mode) {
				case '*':
					$(this).html(value);
					break;
				default:
					$(this).attr(this.options.mode, value);
					break;
			}
		});
	},

	_save: function() {
		var self = this;
		jQuery.ajax({
			type: 'POST',
			url: this.options.url,
			data: {
				key: this.options.key,
				value: $.ui.resource._visualEdit.find('textarea,input').val()
			},
			cache: false,
			success: function(data) { self._setValue(data); },
			complete: function(request, status) { $.ui.resource._closePopup(); }
		});
	},

	_onMouseOver: function(ev) {
		if ($.ui.resource._isEditMode())
			return;
		$.ui.resource._resetShowTimeout();
		$.ui.resource._resetHideTimeout();
		var self = this;
		var handler = (function() { self._openPopup(); });
		if (this.options.showDelay > 0)
			$.ui.resource._timerShow = setTimeout(handler, this.options.showDelay);
		else
			handler();
	}
});

$.ui.resource._checkExternalClick = function(ev) {
	var owner = $.ui.resource._visualEdit.data('owner');
	if ((owner && owner.element.get(0) != ev.target || $(ev.target).is('a,button')) && $(ev.target).parents('div.visualedit').length == 0)
		$.ui.resource._closePopup();
};

$.ui.resource._closePopup = function() {
	$.ui.resource._visualEdit.removeAttr('editMode').hide();
}

$.ui.resource._resetShowTimeout = function() {
	if ($.ui.resource._timerShow) {
		clearTimeout($.ui.resource._timerShow);
		$.ui.resource._timerShow = null;
	}
};

$.ui.resource._resetHideTimeout = function() {
	if ($.ui.resource._timerHide) {
		clearTimeout($.ui.resource._timerHide);
		$.ui.resource._timerHide = null;
	}
};

$.ui.resource._isEditMode = function() {
	return $.ui.resource._visualEdit.attr('editMode');
},

$.ui.resource._onMouseOut = function(event) {
	if ($.ui.resource._isEditMode())
		return;
	var owner = $.ui.resource._visualEdit.data('owner');
	if (owner != null && owner.options.hideDelay > 0)
		$.ui.resource._timerHide = setTimeout($.ui.resource._closePopup, owner.options.hideDelay);
	else
		$.ui.resource._closePopup();
	$.ui.resource._resetShowTimeout();
};

// Extend jQuery with a new easing function
$.extend($.easing, {
	easeOutQuart: function(x, t, b, c, d) {
		return -c * ((t = t / d - 1) * t * t * t - 1) + b;
	}
});


// Infinite carousel slider
// Slides must be positioned in one line
$.fn.carousel = function(s) {
	s = $.extend({
		show: 3,							// Items to show
		scroll: 1,						// Items to scroll
		auto: null,						// Autorun duration
		speed: 300,						// Animation speed
		infinite: true,				// Infinite loop
		easing: 'swing',				// Easing function
		prevclass: 'prev',			// Prev button class name
		nextclass: 'next'				// Next button class name
	}, s || {});

	// Loop through each instance
	return this.each(function() {
		var start = 0, running = false, container = $(this);
		var ul = $("ul", container), li = $("li", ul), count = li.size();

		if(count > s.show) {
			// Show value must be > 0 and < count
			// Scroll value must be > 0 and <= count - s.show
			s.show = Math.min(count, Math.max(1, s.show));
			s.scroll = Math.min(count - s.show, Math.max(1, s.scroll));

			// Clone LI elements to create infinite loop
			// Update LI collection and start position
			if (s.infinite) {
				ul.prepend(li.slice(count - s.show).clone()).append(li.slice(0, s.show).clone());
				li = $("li", ul);
				count = li.size();
				start += s.show;
			}

			var current = start;

			// Create prev and next buttons
			var prevbutton = $('<span>').addClass(s.prevclass).click(function(){ moveSlide(current - s.scroll); }).appendTo(container);
			var nextbutton = $('<span>').addClass(s.nextclass).click(function(){ moveSlide(current + s.scroll); }).appendTo(container);

			// Set equal LI width and the same value for offset
			var offset = li.width(Math.round(container.width() / s.show)).width();

			// Set full width and initial position of the list
			ul.css({ width: offset * count + "px", left: -(current * offset) + "px" });

			// Enable autorun
			if (s.auto)
				setInterval(function() { moveSlide(current + s.scroll); }, s.auto + s.speed);

			// Update control buttons
			setButtons();
		}

		// If not infinite, hide prev/next buttons at the first/last position
		function setButtons() {
			if (s.infinite)
				return;
			prevbutton.toggle(current - s.scroll >= 0);
			nextbutton.toggle(current + s.scroll <= count - s.show);
		}

		// Move to the target slide
		function moveSlide(target) {
			if (running)
				return;

			// Move direction
			var ltr = target > current;

			// Handle first/last position
			if (s.infinite) {
				var page = count - s.show * 2;
				if (target < 0) {
					// Moving from the first to the last slide
					ul.css({ left: -((current + page) * offset) + "px" });
					current = target + page;
				} else if (target > count - s.show) {
					// Moving from the last to the first slide
					ul.css({ left: -((current - page) * offset) + "px" });
					current = target - page;
				} else
					current = target;
			} else {
				// If non-infinite and target goes out of range, then return
				if (target < 0 || target > count - s.show)
					return;
				else
					current = target;
			}

			running = true;

			// Fade in/fade out moving slides
			li.slice(current + (ltr ? -s.scroll : s.show)).slice(0, s.scroll).fadeOut(s.speed, function() { $(this).show(); });
			li.slice(ltr ? current - s.scroll + s.show : current).slice(0, s.scroll).hide().fadeIn(s.speed);

			ul.animate({ left: -(current * offset) + "px" }, s.speed, s.easing, function() { running = false; });

			setButtons();
		}
	});
};


// Image set with prev/next controls and pager
// Slides must be absolutely positioned LI elements
$.fn.imageset = function(s) {
	s = $.extend({
		speed: 300,						// Animation speed
		pager: true,					// Show pager at the bottom
		prevclass: 'prev',			// Prev button class name
		nextclass: 'next'				// Next button class name
	}, s || {});

	// Loop through each instance
	return this.each(function() {
		var start = 0, container = $(this);
		var li = $('li', container), count = li.size();

		if(count > 1) {
			var current = start;

			// Create prev and next buttons
			$('<span>').addClass(s.prevclass).click(function(){ setImage(current - 1); }).appendTo(container);
			$('<span>').addClass(s.nextclass).click(function(){ setImage(current + 1); }).appendTo(container);

			// Create pager at the bottom of container
			if(s.pager) {
				var pager = $('<ol>').appendTo(container);
				li.each(function(i){
					pager.append($('<li>').css({ opacity: 0 }).html('&bull;').click(function(){ setImage(i); }));
				});
			}

			// Indicate current slide
			setPager();
		}

		// Update pager to show the current slide
		function setPager() {
			if(pager)
				$('li', pager).each(function(index) {
					$(this).animate({ opacity: index == current ? 1 : 0.5 });
				});
		}

		// Switch to the target image
		// Handle first/last position to create infinite loop
		function setImage(target) {
			target = target == count ? 0 : target < 0 ? count - 1 : target;

			$(li[target]).fadeIn(s.speed);
			$(li[current]).fadeOut(s.speed);

			current = target;
			setPager();
		}
	});
};


// Display Twitter feed for a username
// Based on tweetable 1.6 (c) 2009 Philip Beel (http://www.theodin.co.uk/)
// More information: http://theodin.co.uk/blog/jquery/tweetable-1-6-launched.html
$.fn.twitter = function(s) {
	s = $.extend({
		username: '',			// Username tweets to display
		busyclass: '',			// CSS class for container when loading
		listclass: '',			// CSS class of the list
		count: 5,				// Number of tweets to show
		replies: false,		// Filter out replies
		retweets: false,		// Filter out retweets
		template: null			// Template <p>%text%</p>
	}, s || {});

	// Loop through each instance
	return this.each(function() {
		var container = $(this).addClass(s.busyclass);
		var tweetList = $('<ul>').addClass(s.listclass);
		var url = "http://api.twitter.com/1/statuses/user_timeline.json?";
		url += $.param({
			count: s.count,
			screen_name: s.username,
			include_rts: s.retweets,
			exclude_replies: !s.replies
		});
		url += "&callback=?";

		// JSON request to twitter API
		$.getJSON(url, function(data) {

			// Loop through the response
			$.each(data, function(i, item) {
				// There is no native datetime format in javascript
				//date = new Date(item.created_at);

				// Format the tweet
				content = item.text
					.replace(/(\b(https?|ftp|file):\/\/[\-A-Z0-9+&@#\/%?=~_|!:,.;]*[\-A-Z0-9+&@#\/%=~_|])/ig, '<a target="_blank" href="$&">$&</a> ')
					.replace(/#(.*?)(\s|$)/g, '<a href="http://search.twitter.com/search?q=%23$1">#$1</a> ')
					.replace(/@(.*?)(\s|\(|\)|$)/g, '<a target="_blank" href="http://twitter.com/$1">@$1</a> $2');
				if(s.template)
					content = s.template.replace(/%(text)%/ig, content);

				tweetList.append('<li>' + content + '</li>');
			});

			// Insert list in the container
			container.removeClass(s.busyclass).html(tweetList);
		});
	});
};


// Display Flickr feed
// Based on jflickrfeed (c) 2009 Joel Sutherland
// More information: http://www.newmediacampaigns.com/page/jquery-flickr-plugin
// Tags for templates: title, link, date_taken, description, published, author, author_id, tags, image*
$.fn.flickr = function(s, callback) {
	s = $.extend({
		id: '',					// User ID
		limit: 20,				// Images to display
		lang: 'en-us',			// Language code
		format: 'json',		// Data format
		busyclass: '',			// CSS class for container when loading
		listclass: '',			// CSS class of the list
		template: null			// Template <img src="%image_m%" alt="%title%">
	}, s || {});

	var url = "http://api.flickr.com/services/feeds/photos_public.gne?";
	url += $.param({
		id: s.id,
		lang: s.lang,
		format: s.format
	});
	url += "&jsoncallback=?";

	return $(this).each(function() {
		var container = $(this).addClass(s.busyclass);
		var imageList = $('<ul>').addClass(s.listclass);
		$.getJSON(url, function(data) {
			$.each(data.items, function(i,item) {
				if(i < s.limit) {
					// Image sizes
					// http://www.flickr.com/services/api/misc.urls.html
					item['image'] = item.media.m.replace('_m', '');
					item['image_s'] = item.media.m.replace('_m', '_s');
					item['image_t'] = item.media.m.replace('_m', '_t');
					item['image_m'] = item.media.m.replace('_m', '_m');
					item['image_b'] = item.media.m.replace('_m', '_b');
					delete item.media;

					// Process template
					if(s.template) {
						var template = s.template;
						for(var key in item){
							var re = new RegExp('%(' + key + ')%', 'ig');
							template = template.replace(re, item[key]);
						}
						content = template;
					} else {
						content = $('<img>').attr({ src: item['image_s'], alt: item.title });
					}

					imageList.append($('<li>').append(content));
				}
			});

			// Insert list in the container
			container.removeClass(s.busyclass).html(imageList);
		});
	});
};

})(jQuery);