// jquery.jScrollPane.js
(function(A){A.jScrollPane={active:[]};A.fn.jScrollPane=function(C){C=A.extend({},A.fn.jScrollPane.defaults,C);var B=function(){return false};return this.each(function(){var O=A(this);O.css("overflow","hidden");var X=this;if(A(this).parent().is(".jScrollPaneContainer")){var Ac=C.maintainPosition?O.position().top:0;var L=A(this).parent();var d=L.innerWidth();var Ad=L.outerHeight();var M=Ad;A(">.jScrollPaneTrack, >.jScrollArrowUp, >.jScrollArrowDown",L).remove();O.css({top:0})}else{var Ac=0;this.originalPadding=O.css("paddingTop")+" "+O.css("paddingRight")+" "+O.css("paddingBottom")+" "+O.css("paddingLeft");this.originalSidePaddingTotal=(parseInt(O.css("paddingLeft"))||0)+(parseInt(O.css("paddingRight"))||0);var d=O.innerWidth();var Ad=O.innerHeight();var M=Ad;O.wrap(A("<div></div>").attr({className:"jScrollPaneContainer"}).css({height:Ad+"px",width:d+"px"}));A(document).bind("emchange",function(Ae,Af,p){O.jScrollPane(C)})}if(C.reinitialiseOnImageLoad){var N=A.data(X,"jScrollPaneImagesToLoad")||A("img",O);var G=[];if(N.length){N.each(function(p,Ae){A(this).bind("load",function(){if(A.inArray(p,G)==-1){G.push(Ae);N=A.grep(N,function(Ag,Af){return Ag!=Ae});A.data(X,"jScrollPaneImagesToLoad",N);C.reinitialiseOnImageLoad=false;O.jScrollPane(C)}}).each(function(Af,Ag){if(this.complete||this.complete===undefined){this.src=this.src}})})}}var o=this.originalSidePaddingTotal;var l={height:"auto",width:d-C.scrollbarWidth-C.scrollbarMargin-o+"px"};if(C.scrollbarOnLeft){l.paddingLeft=C.scrollbarMargin+C.scrollbarWidth+"px"}else{l.paddingRight=C.scrollbarMargin+"px"}O.css(l);var m=O.outerHeight();var i=Ad/m;if(i<0.99){var H=O.parent();H.append(A("<div></div>").attr({className:"jScrollPaneTrack"}).css({width:C.scrollbarWidth+"px"}).append(A("<div></div>").attr({className:"jScrollPaneDrag"}).css({width:C.scrollbarWidth+"px"}).append(A("<div></div>").attr({className:"jScrollPaneDragTop"}).css({width:C.scrollbarWidth+"px"}),A("<div></div>").attr({className:"jScrollPaneDragBottom"}).css({width:C.scrollbarWidth+"px"}))));var z=A(">.jScrollPaneTrack",H);var P=A(">.jScrollPaneTrack .jScrollPaneDrag",H);if(C.showArrows){var g;var Ab;var S;var r;var j=function(){if(r>4||r%4==0){y(u+Ab*b)}r++};var K=function(p){A("html").unbind("mouseup",K);g.removeClass("jScrollActiveArrowButton");clearInterval(S)};var Z=function(){A("html").bind("mouseup",K);g.addClass("jScrollActiveArrowButton");r=0;j();S=setInterval(j,100)};H.append(A("<a></a>").attr({href:"javascript:;",className:"jScrollArrowUp"}).css({width:C.scrollbarWidth+"px"}).html("Scroll up").bind("mousedown",function(){g=A(this);Ab=-1;Z();this.blur();return false}).bind("click",B),A("<a></a>").attr({href:"javascript:;",className:"jScrollArrowDown"}).css({width:C.scrollbarWidth+"px"}).html("Scroll down").bind("mousedown",function(){g=A(this);Ab=1;Z();this.blur();return false}).bind("click",B));var Q=A(">.jScrollArrowUp",H);var J=A(">.jScrollArrowDown",H);if(C.arrowSize){M=Ad-C.arrowSize-C.arrowSize;z.css({height:M+"px",top:C.arrowSize+"px"})}else{var s=Q.height();C.arrowSize=s;M=Ad-s-J.height();z.css({height:M+"px",top:s+"px"})}}var w=A(this).css({position:"absolute",overflow:"visible"});var D;var Y;var b;var u=0;var V=i*Ad/2;var a=function(Ae,Ag){var Af=Ag=="X"?"Left":"Top";return Ae["page"+Ag]||(Ae["client"+Ag]+(document.documentElement["scroll"+Af]||document.body["scroll"+Af]))||0};var f=function(){return false};var v=function(){n();D=P.offset(false);D.top-=u;Y=M-P[0].offsetHeight;b=2*C.wheelSpeed*Y/m};var E=function(p){v();V=a(p,"Y")-u-D.top;A("html").bind("mouseup",T).bind("mousemove",h);if(A.browser.msie){A("html").bind("dragstart",f).bind("selectstart",f)}return false};var T=function(){A("html").unbind("mouseup",T).unbind("mousemove",h);V=i*Ad/2;if(A.browser.msie){A("html").unbind("dragstart",f).unbind("selectstart",f)}};var y=function(Ae){Ae=Ae<0?0:(Ae>Y?Y:Ae);u=Ae;P.css({top:Ae+"px"});var Af=Ae/Y;w.css({top:((Ad-m)*Af)+"px"});O.trigger("scroll");if(C.showArrows){Q[Ae==0?"addClass":"removeClass"]("disabled");J[Ae==Y?"addClass":"removeClass"]("disabled")}};var h=function(p){y(a(p,"Y")-D.top-V)};var q=Math.max(Math.min(i*(Ad-C.arrowSize*2),C.dragMaxHeight),C.dragMinHeight);P.css({height:q+"px"}).bind("mousedown",E);var k;var R;var I;var t=function(){if(R>8||R%4==0){y((u-((u-I)/2)))}R++};var Aa=function(){clearInterval(k);A("html").unbind("mouseup",Aa).unbind("mousemove",e)};var e=function(p){I=a(p,"Y")-D.top-V};var U=function(p){v();e(p);R=0;A("html").bind("mouseup",Aa).bind("mousemove",e);k=setInterval(t,100);t()};z.bind("mousedown",U);H.bind("mousewheel",function(Ae,Ag){v();n();var Af=u;y(u-Ag*b);var p=Af!=u;return !p});var F;var W;function c(){var p=(F-u)/C.animateStep;if(p>1||p<-1){y(u+p)}else{y(F);n()}}var n=function(){if(W){clearInterval(W);delete F}};var x=function(Af,p){if(typeof Af=="string"){$e=A(Af,O);if(!$e.length){return}Af=$e.offset().top-O.offset().top}H.scrollTop(0);n();var Ae=-Af/(Ad-m)*Y;if(p||!C.animateTo){y(Ae)}else{F=Ae;W=setInterval(c,C.animateInterval)}};O[0].scrollTo=x;O[0].scrollBy=function(Ae){var p=-parseInt(w.css("top"))||0;x(p+Ae)};v();x(-Ac,true);A("*",this).bind("focus",function(Ah){var Ag=A(this);var Aj=0;while(Ag[0]!=O[0]){Aj+=Ag.position().top;Ag=Ag.offsetParent()}var p=-parseInt(w.css("top"))||0;var Ai=p+Ad;var Af=Aj>p&&Aj<Ai;if(!Af){var Ae=Aj-C.scrollbarMargin;if(Aj>p){Ae+=A(this).height()+15+C.scrollbarMargin-Ad}x(Ae)}});if(location.hash){x(location.hash)}A(document).bind("click",function(Ae){$target=A(Ae.target);if($target.is("a")){var p=$target.attr("href");if(p.substr(0,1)=="#"){x(p)}}});A.jScrollPane.active.push(O[0])}else{O.css({height:Ad+"px",width:d-this.originalSidePaddingTotal+"px",padding:this.originalPadding});O.parent().unbind("mousewheel")}})};A.fn.jScrollPane.defaults={scrollbarWidth:10,scrollbarMargin:5,wheelSpeed:18,showArrows:false,arrowSize:0,animateTo:false,dragMinHeight:1,dragMaxHeight:99999,animateInterval:100,animateStep:3,maintainPosition:true,scrollbarOnLeft:false,reinitialiseOnImageLoad:false};A(window).bind("unload",function(){var C=A.jScrollPane.active;for(var B=0;B<C.length;B++){C[B].scrollTo=C[B].scrollBy=null}})})(jQuery);
// jquery.mousewheel.js <http://brandonaaron.net> Dual MIT and GPL license. Revision 4265.
(function($){$.event.special.mousewheel={setup:function(){var handler=$.event.special.mousewheel.handler;if($.browser.mozilla){$(this).bind("mousemove.mousewheel",function(event){$.data(this,"mwcursorposdata",{pageX:event.pageX,pageY:event.pageY,clientX:event.clientX,clientY:event.clientY});});}if(this.addEventListener){this.addEventListener(($.browser.mozilla?"DOMMouseScroll":"mousewheel"),handler,false);}else{this.onmousewheel=handler;}},teardown:function(){var handler=$.event.special.mousewheel.handler;$(this).unbind("mousemove.mousewheel");if(this.removeEventListener){this.removeEventListener(($.browser.mozilla?"DOMMouseScroll":"mousewheel"),handler,false);}else{this.onmousewheel=function(){};}$.removeData(this,"mwcursorposdata");},handler:function(event){var args=Array.prototype.slice.call(arguments,1);event=$.event.fix(event||window.event);$.extend(event,$.data(this,"mwcursorposdata")||{});var delta=0,returnValue=true;if(event.wheelDelta){delta=event.wheelDelta/120;}if(event.detail){delta=-event.detail/3;}event.data=event.data||{};event.type="mousewheel";args.unshift(delta);args.unshift(event);return $.event.handle.apply(this,args);}};$.fn.extend({mousewheel:function(fn){return fn?this.bind("mousewheel",fn):this.trigger("mousewheel");},unmousewheel:function(fn){return this.unbind("mousewheel",fn);}});})(jQuery);


/*
	Asynchronously load Google Analytics page tracking code.
	Original concept: http://tinyurl.com/jresig-jquery-gajs
*/
 
(function(tracker){
	var prefix   = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
	var source   = prefix + "google-analytics.com/ga.js";
	var pageview = function(){
		try {
			_gat._getTracker(tracker)._trackPageview();
		} catch(e) { }
	};
	jQuery.getScript(source, pageview);
})("UA-1187395-10");

/*
	Basic namespace
*/
salathe = {};

/*
	Super-simple template pluging

	@param template string the template string (with ${var} placeholders)
	@param data     object hash of key/value pairs to be substituted
	@return string         the substituted template
*/
(function($){
	$.template = salathe.template = function(template, data){
		data = data || {};
		return template.replace(/\$\{([\w\.]*)}/g, function(str, key){
			var keys = key.split("."), value = data[keys.shift()];
			$.each(keys, function(){ value = value[this]; })
			return value;
		})
	};
})(jQuery);

/*
	Widget wrapper
	
	Provides a unified appearance/behaviour for the multiple widgets.
	
	@param wrapper  string   CSS selector for the widget wrapper (usually a UL)
	@param url      string   the URL to get JSON content from
	@param getItems function parses the JSON object for widget content
	@param makeItem function generates HTML for widget content
	@return         object   basic object with only one method: fetch()
*/
(function($){
	salathe.widget = function(wrapper, url, getItems, makeItem) {
		var $wrapper = $(wrapper);
		var show     = function(data) {
			try {
				var items = getItems(data);
				$wrapper.empty().hide();
				$.each(items, function(ix,item){
					$wrapper.append(makeItem(item));
				});
				$wrapper.jScrollPane();
				$wrapper.slideDown("slow", function(){ $wrapper.jScrollPane(); });
			} catch (e) {
				$wrapper.show().html('<li class="error">Error: ' + e + "</li>");
			}
		};
		return {
			fetch : function(){
				$wrapper.empty().html('<li class="loading">Loading...</li>');
				$.getJSON(url, show);
			}
		};
	};
	salathe.widgets = {};
})(jQuery);

/*
	Main app initialiser, fires when DOM is loaded
*/
jQuery(function($){

	/*
		Add little triangles below heading boxes
	*/
	$("#name").append('<span class="nipple nipblack"></span>');
	$("#main > div.widget h2").after('<div class="nipple"></div>');
	
	/*
		Fix titles for people with JavaScript :-)
	*/
	$("h1").html("Here are some recent<sup>†</sup> updates.");
	$("h1 + p.note").html("<sup>†</sup> May not be super-recent, or even a bit recent. Perhaps not recent at all.");
	
	/*
		Twitter Widget
	*/
	function formatTwitString(str)
	{
		str=' '+str;
		str = str.replace(/((ftp|https?):\/\/([-\w\.]+)+(:\d+)?(\/([\w\/_\.-]*(\?\S+)?)?)?)/gm,'<a href="$1" target="_blank">$1</a>');
		str = str.replace(/([^\w])\@([\w\-]+)/gm,'$1@<a href="http://twitter.com/$2" target="_blank">$2</a>');
		str = str.replace(/([^\w])\#([\w\-]+)/gm,'$1<a href="http://twitter.com/search?q=%23$2" target="_blank">#$2</a>');
		return str;
	}
	function relativeTime(pastTime)
	{	
		var origStamp = Date.parse(pastTime);
		var curDate = new Date();
		var currentStamp = curDate.getTime();
		
		var difference = parseInt((currentStamp - origStamp)/1000);
	
		if(difference < 0) return false;
	
		if(difference <= 5)				return "just now";
		if(difference <= 20)			return "seconds ago";
		if(difference <= 60)			return "a minute ago";
		if(difference < 3600)			return parseInt(difference/60)+" minute"+(parseInt(difference/60)===1?"":"s")+" ago";
		if(difference <= 1.5*3600) 		return "pne hour ago";
		if(difference < 23.5*3600)		return Math.round(difference/3600)+" hours ago";
		if(difference < 1.5*24*3600)	return "one day ago";
		
		var dateArr = pastTime.split(' ');
		return dateArr[4].replace(/\:\d+$/,'')+' '+dateArr[2]+' '+dateArr[1]+(dateArr[3]!=curDate.getFullYear()?' '+dateArr[3]:'');
	}
	(new salathe.widget(
		"#twitter > ul",
		"http://search.twitter.com/search.json?q=from:cowburn+OR+@cowburn+OR+to:cowburn&rpp=20&callback=?",
		function(json){
			return json.results;
		},
		function(item){
			var template = '<li>\
				<p class="status"><q cite="http://twitter.com/${from_user}/status/${id}">${tweet}</q></p>\
				<div class="meta">\
					<a class="avatar" href="http://twitter.com/${from_user}"><img src="${profile_image_url}" alt="${from_user}"/></a>\
					<a class="user" href="http://twitter.com/${from_user}">${from_user}</a>\
					<span class="time">${time}</span>\
				</div>\
			</li>';
			item.time = relativeTime(item.created_at);
			item.tweet = formatTwitString(item.text);
			return $.template(template, item);
		}
	)).fetch();
	
	/*
		Delicious Widget
	*/
	(new salathe.widget(
		"#delicious > ul",
		"http://query.yahooapis.com/v1/public/yql?q=select%20title%2C%20pubDate%2C%20link%2C%20comments%2C%20description%20from%20delicious.feeds(20)%20where%20username%3D%22petercowburn%22&format=json&diagnostics=false&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys&callback=?",
		function(json){
			return json.query.results.item;
		},
		function(item){
			var template = '<li>\
				<img class="favicon" src="http://getfavicon.appspot.com/${link}" alt="Favicon for ${link}"/>\
				<p class="link">\
					<a href="${link}">${title}</a>\
					on <span class="domain">${domain}</span>\
				</p>\
			</li>';
			item.domain = item.link.match(/^(?:f|h)ttps?:\/\/([^/]+)(:[0-9]{1,6})?/)[1];
			return salathe.template(template, item);
		}
	)).fetch();
	
	
	/*
		Blog widget
	*/
	(new salathe.widget(
		"#blog > ul",
		"http://query.yahooapis.com/v1/public/yql?q=select%20title%2Clink%2CpubDate%20from%20feed(10)%20where%20url%3D'http%3A%2F%2Ffeeds.feedburner.com%2Fcowburn'&format=json&diagnostics=false&callback=?",
		function(json){
			return json.query.results.item;
		},
		function(item){
			var template = '<li>\
				<a href="${link}">${title}</a>\
				on <span class="time">${time}</span>\
			</li>';
			var dt = new Date(Date.parse(item.pubDate));
			var m = ["Jan", "Feb", "Mar", "Apr", "May", "June", "July", "Aug", "Sept", "Oct", "Nov", "Dec"];
			item.time = dt.getDate() + " " + m[dt.getMonth()] + " " + dt.getFullYear();
			return $.template(template, item);
		}
	)).fetch();
	
	/*
		Commits widget
	*/
	(new salathe.widget(
		"#phpdoc > ul",
		"http://salathe.co.uk/tasks/phpdoc/feed.php?limit=10&callback=?",
		function(json){
			return json.commits;
		},
		function(item){
			var template = '<li>\
				<a href="http://svn.php.net/viewvc?view=revision&revision=${revision}" class="revision">r${revision}</a>\
				<span class="log">${log}</span>\
				on <span class="time">${time}</span>\
			</li>';
			var dt = new Date(item.datetime);
			var m = ["Jan", "Feb", "Mar", "Apr", "May", "June", "July", "Aug", "Sept", "Oct", "Nov", "Dec"];
			item.time = dt.getDate() + " " + m[dt.getMonth()] + " " + dt.getFullYear();
			return $.template(template, item);
		}
	)).fetch();
	
});

/*
	Wee tracker :-)
*/
(function(params) {
	var uri = 'track.gif?rand=' + Math.random();
	for (var k in params) {
		var val = params[k] !== null ? params[k] : '';
		uri += '&' + encodeURIComponent(k) + '=' + encodeURIComponent(val);
	}
	document.write('<img src="'+uri+'" />');
})({
	location: document.location,
	vertical: "salathe",
	referer: document.referrer
});



/*
	#buzzwords
*/
jQuery(function($){
	var action  = $('<a href="#buzzwords" title="click to show buzzwords">↓</a>');
	var target  = $('<p id="buzzwords">'
	            + 'HTML, CSS, JavaScript (jQuery), YQL, '
	            + 'PHP, MongoDB (and MongoHQ), JSON(P), RSS, SVN LOG, '
	            + 'Twitter Search API, Delicious API, Feedburner …'
	            + '</p>').hide();
	var wrapper = $("#footer p:first");

	// Change fullstop to colon
	wrapper.html(wrapper.html().replace(/\.\s*$/, ': '));
	// Add button and content
	wrapper.append(action).after(target);
	
	action.toggle(function(){
		action.blur().text('↑').attr('title', 'click to hide buzzwords');
		target.slideDown();
	},function(){
		action.blur().text('↓').attr('title', 'click to show buzzwords');
		target.slideUp();
	});
});