Build a popular posts widget with Flybase

On the home page of this site, there's a populate posts widget, I use the same widget on my main personal blog and also on the Flybase Blog.

It works great, it uses javascript to store page views inside a Flybase database and then outputs them in a handy little table which is also easy to edit.

I thought I'd share how this is set up so you can add it yourself.

Getting started

First, you'll need a Flybase account so head on over and signup if you haven't already.

The Brains

Create a file called popular.js, and update it with your flybase credentials. That's it.

$document.ready(function () {
	var url = document.location.pathname;
	var title = document.title;
	if ( url === "/" ){
		var page = getParameterByName("page");
		if( page === "" || page === "1" ){
			var popular = new mostPopular().getPages( $("#popularpart") );
		}
	}else {
		//	not home page so let's record the url count.
		var popular = new mostPopular().updatePage(url, title);
	}
});

var mostPopular = function(){
	this.api_key = "FLYBASE-API-KEY";
	this.db = "FLYBASE-APP";
	this.collection = "mostpopular";
	this.pages = [];

	this.flybaseRef = new Flybase( this.api_key, this.db, this.collection );
	this.populate();
	return this;
};

mostPopular.prototype.populate = function(){
	var _this = this;

	//	let's get a list of pages for use later...
	this.flybaseRef.orderBy( {"views":-1} ).on('value', function( data ){
		if( data.count() ){
			data.forEach( function(snapshot) {
				var item = snapshot.value();
				_this.pages[ item.key ] = item;
			});
		}
	});
};

mostPopular.prototype.getPages = function( div_id ){
	var _this = this;
	var r={
		headline:"Recently popular posts…",
		clickhere:"(click to load)",
		loading:"(loading…)"
	};
	$('<aside id="popular"><div class="header"><h1>'+r.headline+"</h1></div></aside>").prependTo( div_id );
	this.flybaseRef.orderBy({"views":-1}).limit(6).on('value',function( data ){
		if( data.count() ){
			var pages = [];
			data.forEach( function(snapshot) {
				var item = snapshot.value();
				pages[item._id] = item;
			});

			var aside = $("#popular");
			var header = $("header",aside);

			var ul = $("<ul />").attr("style","display:none");
			for( var i in pages ){
				var item = pages[i];
				$('<li/>').attr("id",item._id).prepend(
					$("<a>")
						.attr("href",item.url)
						.attr("title",item.title.replace("&#x27;","'") )
						.attr("data-count",item.views)
						.text(item.title.replace("&#x27;","'") )
				).appendTo( ul );
				_this.pages[ item.key ] = item;
			}
			aside.append( ul );
			ul.slideDown(400);
			header.removeClass("loading").addClass("loaded");
			header.find("h1").html(r.headline);
		}
	});
	return this;
};

mostPopular.prototype.updatePage = function(url, title){
	var key = url.replace(/[\/-]/g,'');

	var _this = this;
	var cnt = 0;
	_this.flybaseRef.where({"url": url}).orderBy( {"views":-1} ).once('value').then( function( data ){
		data.forEach( function(snapshot) {
			var item = snapshot.value();
			item.views = item.views + 1;
			_this.flybaseRef.update(item._id,item, function(resp) {
				console.log( key + " updated" );
			});
		});
	},function(){
		// no count, so never added before..
		_this.flybaseRef.push({
			"key": key,
			"url": url,
			"title": title,
			"views": 1
		}, function(resp) {
			console.log( "URL added" );
		});
	});
	return this;
};

function hashCode( str ){
	if (Array.prototype.reduce){
		return str.split("").reduce(function(a,b){a=((a<<5)-a)+b.charCodeAt(0);return a&a},0);
	}
	var hash = 0;
	if (str.length === 0) return hash;
	for (var i = 0; i < str.length; i++) {
		var character  = str.charCodeAt(i);
		hash  = ((hash<<5)-hash)+character;
		hash = hash & hash; // Convert to 32bit integer
	}
	return hash;
}

function getParameterByName(name) {
	name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
	var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),results = regex.exec(location.search);
	return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
}

One thing this file does is check the current URL and only display the popular posts if on the home page /:

$document.ready(function () {
	var url = document.location.pathname;
	var title = document.title;
	if ( url === "/" ){
		var page = getParameterByName("page");
		if( page === "" || page === "1" ){
			var popular = new mostPopular().getPages( $("#popularpart") );
		}
	}else {
		//	not home page so let's record the url count.
		var popular = new mostPopular().updatePage(url, title);
	}
});

You can adjust that url check easily enough if you wanted it displayed on a sidebar for example like I did with the Flybase blog.

Or if you just wanted to use for tracking page views, you can disable the getPages section entirely.

The Beauty

Next, let's add our CSS, create a file called popular.css

#popular {
	float:left;
	clear: both;
	margin: 0 0 30px;
	background: #fff;
	width: 100%;
	max-width:100%;
	-moz-border-radius: 6px;
	-webkit-border-radius: 6px;
	border-radius: 6px;
	border: solid 1px #ddd;
	font-size: 81%;
}
#popular .header {
	background: #f3f3f3;
	border-bottom: 1px solid #ddd;
	-moz-border-radius: 5px;
	-webkit-border-radius: 5px;
	border-radius: 5px;
	padding: 6px 0px 0px 10px;
	cursor: s-resize;
}
#popular .header h1 {
	font-size: 1em;
	color: #666;
	font-family: "Open Sans", "Helvetica Neue", Arial, sans-serif;
	padding-bottom: 6px;
	margin: 0;
}
#popular .header.loading span {
	-webkit-animation-name: pulse;
	-webkit-animation-duration: 1s;
	-webkit-animation-iteration-count: 100;
	color: #bbb;
}
#popular .header.loaded {
	-moz-border-radius-bottomleft: 0;
	-webkit-border-bottom-left-radius: 0;
	border-bottom-left-radius: 0;
	-moz-border-radius-bottomright: 0;
	-webkit-border-bottom-right-radius: 0;
	border-bottom-right-radius: 0;
	cursor: default;
}
#popular ul {
	list-style-type: none;
	padding: 0;
	margin: 10px;
	color: #007892;
}
#popular ul li {
	float: left;
	width: 49%;
	margin: 0.25em 0;
}
#popular ul li a {
	display: block;
	margin: 0;
	padding: 2px;
	width: 91%;
	color: #0689a6;
	height: 1.5em;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	font-size: .85em;
}
/*
#popular ul li a {
	display: block;
	margin: 0;
	padding: 2px;
	width: 91%;
	color: #0689a6;
	height: 1.5em;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	font-size: 15px;
}
*/
@media only screen and (max-width: 790px) {
	#popular ul li {
	float: none;
	width: 100%}
}

The Bones

Finally, here is our sample.html file:

<html>
<head>
	<title>Sample Page</title>
	<link rel="stylesheet" type="text/css" href="popular.css">
</head>
<body>
	<!-- placeholder for popular posts -->
	<div id="popularpart"></div>

	<script src="https://cdn.flybase.io/flybase.js"></script>
	<script type="text/javascript" src="popular.js"></script>
</body>
</html>

Inside your own HTML file, you really just need these four important lines:

<link rel="stylesheet" type="text/css" href="popular.css">
<div id="popularpart"></div>
<script src="https://cdn.flybase.io/flybase.js"></script>
<script type="text/javascript" src="popular.js"></script>

Where ever you place the <div id="popularpart"></div> is where the popular posts will show up.

The End

One thing I've been doing after saving a couple records, is using the manage schema section in Flybase to make the URL field unique, this is optional but handy and it's what indexes are there for.

That's it, you've got a plug and play popular posts widget to take and play with and expand on however you want.

This widget doesn't care about what platform your site is on, be it static site, Ghost, WordPress, does not matter one tiny little bit.