Skip to content

Instantly share code, notes, and snippets.

@barnettjw
Created January 14, 2014 05:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save barnettjw/8413328 to your computer and use it in GitHub Desktop.
Save barnettjw/8413328 to your computer and use it in GitHub Desktop.
A Pen by James Barnett.
<div class = "container">
<h1>Simple Treehouse API</h1>
<form class = "pure-form-aligned">
<div class="pure-control-group">
<label for = "username">Treehouse Username</label>
<input id = "username" class = "username" placeholder = "Treehouse username" tabindex = "1" autofocus = "autofocus"/>
</div>
<!--<div class="pure-control-group">
<label for = "timeframe-length">Get data for the past</label>
<input id = "timeframe-length" class = "timeframe-length" placeholder = "7" tabindex = "2"/>
<div class="styled-select">
<select id = "timeframe-unit" class = "timeframe-unit" tabindex = "3">
<option>days</option>
<option>weeks</option>
<option>months</option>
</select>
</div>
</div>-->
<div class="pure-button pure-button-primary" tabindex = "4">Pull My Stats</div>
<!------------------------------------------->
</form>
<img class = "spinner" id = "ac-spinner" src = "http://jamesbarnett.me/img/ajax-loader.gif" />
<div class = "activity-chart">
<h2>Treehouse Activity</h2>
<ol class = "days-of-week">
<li>M</li>
<li>W</li>
<li>F</li>
</ol>
<div id = "month" class = "month clearfix"></div>
<div id = "days" class = "days"></div>
<div class = "key">
<span>Less</span>
<ul>
<li class = "activity-four"></li>
<li class = "activity-three"></li>
<li class = "activity-two"></li>
<li class = "activity"></li>
<li class = "day-key"></li>
</ul>
<span>More</span>
</div>
</div>
<img class = "spinner" id = "badges-spinner" src = "http://jamesbarnett.me/img/ajax-loader.gif" />
<div class = "stats">
<div class = "year-badges stats-box">
<div id = "year-badges-num" class = "stats-box-title"></div>
<div class = "year-badges-date">
<span id ="year-start" class = "date"></span> -
<span id ="year-end" class = "date"></span>
<p>Year of Badges</p>
</div>
</div>
<div class = "max-streak stats-box">
<div id = "max-streak-num" class = "stats-box-title"></div>
<div class = "max-streak-date">
<span id ="max-streak-start" class = "date"></span> -
<span id ="max-streak-end" class = "date"></span>
<p>Longest Streak</p>
</div>
</div>
<div class = "current-streak stats-box">
<div id = "current-streak-num" class = "stats-box-title"></div>
<div class = "current-streak-date">
<span id ="current-streak-start" class = "date"></span> -
<span id ="current-streak-end" class = "date"></span>
<p>Current Streak</p>
</div>
</div>
</div>
<div id = "badges" class = "badges"></div>
</div>
$(document).ready(function () {
var user = null;
/*var duration_num = null;
var duration_unit = null;*/
$(".spinner").hide();
getData();
function getData() {
user = $("#username").val();
/*duration_num = parseInt($("#timeframe-length").val(), 10);
duration_unit = $('#timeframe-unit').find('option:selected').text();*/
user = "benjaminsimmons";
//user = "jessicasideways";
//user = "jackcrane";
$(".activity-chart").hide();
$(".stats").hide();
$.getJSON("http://teamtreehouse.com/" + user + ".json", function (data) {
var start_time = moment().subtract("year", 1);
var dataDate = moment(day).format("MM-DD-YY");
// stick collection of earned dates in an array
var dates = [];
// because we are filtering by date, don't use the for loop counter because it will add in undefined items to the arry for each of the filtered items
var countBadges = 0;
for (var a in data.badges) {
if ((moment(data.badges[a].earned_date)).isAfter(moment(start_time))) {
countBadges++;
dates[countBadges] = moment(data.badges[a].earned_date).format("MM-DD-YY");
}
}
// count number of occurrences of a date
var counted = compressArray(dates);
/****** draw months ******/
var month = moment();
var outputMonth = "<ol>";
for (i = 0; i <= 12; i++) {
var durationMonth = moment.duration(1, 'months');
outputMonth += "<li>";
outputMonth += moment(month).format("MMM");
outputMonth += "</li>";
month = moment(month).subtract(durationMonth);
}
outputMonth += "</ol>";
var output = "<ol><div class = 'week'>";
var day = moment();
/* Calculate the offset for days of the week to line up correctly */
var dayOfWeekOffset = 6 - (parseInt(moment().format("d"), 10));
/* draw offset */
for (i = 0; i < (dayOfWeekOffset); i++) {
output += "<li class = 'offset'></li>";
}
/****** draw calendar ******/
var badgeHigh = 0;
for (i = 365; i >= 0; i--) {
var badges = "No";
var activity4 = false;
var activity3 = false;
var activity2 = false;
var activity = false;
checkDay = moment(day).format("MM-DD-YY");
/* find highest single day badge count */
for (b = 0; b < counted.length; b++) {
badges = counted[b].count;
if (badges > badgeHigh) {
badgeHigh = badges;
}
}
/* apply activity colors relative to highest single day badge count */
for (c = 0; c < counted.length; c++) {
if (counted[c].value === checkDay) {
badges = counted[c].count;
if (badges >= (Math.floor(badgeHigh * 0.75))) {
activity4 = true;
} else if (badges > (Math.floor(badgeHigh * 0.5))) {
activity3 = true;
} else if (badges > (Math.floor(badgeHigh * 0.25))) {
activity2 = true;
} else {
activity = true;
}
}
}
/* stick date in data attribute so we can parse it later for filtering purposes */
var li = "<li data-date = '" + checkDay + "' class =";
if (activity4 === true) {
output += li + "'activity-four'>";
} else if (activity3 === true) {
output += li + "'activity-three'>";
} else if (activity2 === true) {
output += li + "'activity-two'>";
} else if (activity === true) {
output += li + "'activity'>";
} else {
output += li + "''>";
}
output += '<span class = "tooltip"><span class = "bold">' + badges + " badges</span> earned on " + moment(day).format("MMMM Do YYYY") + '</span>';
output += "</li>";
var duration = moment.duration(1, 'days');
day = moment(day).subtract(duration);
}
output += "</div></ol>";
document.getElementById("days").innerHTML = output;
document.getElementById("month").innerHTML = outputMonth;
$("#ac-spinner").hide();
$(".activity-chart").show();
$(".stats").css( "padding-bottom", "125px" );
$(".stats").show();
/****** parse which day was clicked ******/
var selectedDay = moment().format("MM-DD-YY");
$(".days li").click(function () {
selectedDay = ($(this).attr("data-date"));
var outputBadge = '<ul>';
for (var d in data.badges) {
if ((moment(data.badges[d].earned_date)).isSame(moment(selectedDay), 'day')) {
outputBadge += "<li>";
outputBadge += '<img src ="' + data.badges[d].icon_url + '" />';
outputBadge += '<span class = "tooltip">' + data.badges[d].name + '</span>';
outputBadge += "</li>";
}
}
outputBadge += "</ul>";
$("#badges-spinner").hide();
document.getElementById("badges").innerHTML = outputBadge;
});
/****** max streak count ******/
var streakOverall = 0;
var streakMax = streakOverall;
var streakMaxEnd = null;
for (var e = 0; e < counted.length; e++) {
var current = counted[e].value;
var next = current;
var tomorrow = moment(current).add("days", 1);
//don't overflow array by comparing last item
if ((e + 1) < counted.length) {
next = counted[(e + 1)].value;
}
// check if "next" item in array is the same day as the "current" item + 1 day
if (moment(next).isSame(moment(tomorrow), 'day')) {
streakOverall++;
} else {
streakOverall = 1;
}
// check if current streak is longer than previous max streak
if (streakOverall > streakMax) {
streakMax = streakOverall;
streakMaxEnd = next; //get date of end of streak
}
}
/****** current streak count ******/
var mostRecent = counted[(counted.length-1)].value;
//console.log(mostRecent);
// If user has earned a badge today streakCurrent >= 1
// else streakCurrent = 0
var streakCurrent = 1;
if(moment(mostRecent).isSame(moment(), 'day')){
streakCurrent = 1;
}
else {
streakCurrent = 0;
}
for (var f = counted.length - 1; f > 0; f--) {
var dayCurrent = counted[f].value;
var previous = dayCurrent;
//don't overflow array by comparing last item
if (f > 1) {
previous = counted[(f - 1)].value;
}
// check if "previous" item in array is the same day as the "current" item - 1 day
var yesterday = moment(dayCurrent).subtract("days", 1);
if (moment(previous).isSame(moment(yesterday), 'day')) {
streakCurrent++;
} else {
break;
}
}
/***** output stats *****/
document.getElementById("year-badges-num").innerHTML = countBadges + " Total";
document.getElementById("year-start").innerHTML = moment(start_time).format("MMM DD YYYY");
document.getElementById("year-end").innerHTML = moment().format("MMM DD YYYY");
// Because when counting a streak "today" counts as 1, you need to subtract 1 day when doing date math to get a start/end date
document.getElementById("max-streak-num").innerHTML = streakMax + " Days";
document.getElementById("max-streak-start").innerHTML = moment(streakMaxEnd).subtract("days", (streakMax - 1)).format("MMM DD YYYY");
document.getElementById("max-streak-end").innerHTML = moment(streakMaxEnd).format("MMM DD YYYY");
document.getElementById("current-streak-num").innerHTML = streakCurrent + " Days";
document.getElementById("current-streak-start").innerHTML = moment().subtract("days", (streakCurrent - 1)).format("MMM DD YYYY");
document.getElementById("current-streak-end").innerHTML = moment().format("MMM DD YYYY");
});
}
/****** button clicked ******/
$(".pure-button-primary").keypress(function (e) {
if (e.which == 13) {
$("#ac-spinner").show();
getData();
}
});
$(".pure-button-primary").click(function () {
$("#ac-spinner").show();
getData();
});
/****** count occurrence of value, in this case earned dates ******/
function compressArray(original) {
var compressed = [];
var copy = original.slice(0);
for (var i = 0; i < original.length; i++) {
var myCount = 0;
for (var w = 0; w < copy.length; w++) {
if (original[i] == copy[w]) {
myCount++;
delete copy[w];
}
}
if (myCount > 0) {
var a = {};
a.value = original[i];
a.count = myCount;
compressed.push(a);
}
}
return compressed;
}
});
/* resets */
* { box-sizing: border-box; }
.clearfix:before, .clearfix:after { content: ""; display: table; }
.clearfix:after { clear: both; }
ol, li { padding: 0; margin: 0; list-style: none;}
/******/
body {
background: #E0E0E0 url('http://subtlepatterns.com/patterns/creampaper.png');
font-family: verdana, arial;
}
body, select { color: #525252; }
h1 {
font-size: 3em;
margin: 0 0 10px 10px;
font-weight: 600;
}
.container, .badges {
width: 900px;
background: #fff;
}
.container {
position: relative;
margin: 100px auto;
padding: 25px;
border-radius: 7px;
}
/*** form ***/
form { margin-left: 5px; }
input {
margin: 10px;
height: 32px;
width: 250px;
padding-left: 10px;
}
.username { width: 225px; }
.timeframe-length {
width: 50px;
margin-right: 11px;
}
.container .pure-control-group label{ width: 200px; }
.container .pure-control-group label, .container select { font-size: 18px; }
.styled-select { display: inline-block; }
.styled-select, input {
border: 1px solid #ccc;
border-radius: 2px;
}
.pure-button-primary {
background: #64A7CE;
margin-left: 12px;
}
.pure-button-primary:focus {
outline: 2px solid goldenrod;
border-radius: 0;
}
/*** custom selectbox ***/
.styled-select {
background: url(http://findicons.com/files/icons/2227/picol/32/arrow_sans_down_32.png) no-repeat 130px 8px #fff;
background-size: 20px;
width: 125px;
box-sizing: border-box;
}
.styled-select, .styled-select select { width: 158px; }
.styled-select select {
height: 35px;
padding: 0 5px;
background: transparent;
border: none;
-webkit-appearance: none;
}
.spinner { margin: 0 50%; }
/********* activity chart *********/
h2 {
margin-left: 20px;
font-size: 1em;
font-weight: normal;
}
.days li, .day-key { background: #eee; }
.activity-chart {
position: relative;
height: 200px;
width: 790px;
margin: 50px auto 0 auto;
}
/*** month headings ***/
.month ol { float: left; }
.month li:first-child{ margin-right: 0; }
.month li {
float: right;
margin-right: 37px;
font-size: 0.75em;
}
/*** day of week heading ***/
.days-of-week {
width: 15px;
font-size: 0.7em;
margin: 0;
position: absolute;
top: 73px;
left: -6px;
}
.days-of-week li:nth-child(2){ margin: 15px 0; }
/*** draw days ***/
/* offset so days of the week line up
over-specified to win specificity battle */
.activity-chart .offset:hover { outline: none; }
.activity-chart .offset { background: none; }
/* create vertical weeks */
.week {
width: 108px;
transform: rotate(90deg);
}
.days {
font-size: 0.75em;
position: absolute;
right: -45px;
top: 110px;
/*outline: solid;*/
}
.days li, .key li {
width: 12px;
height: 12px;
float: right; /* order days starting at the bottom right */
}
.days .bold { font-weight: bold; }
.days li { margin: 1.5px; }
/*** color-code by activity level ***/
.activity-chart .activity { background: #d6e685; }
.activity-chart .activity-two { background: #8cc665; }
.activity-chart .activity-three { background: #44a340; }
.activity-chart .activity-four { background: #1e6823; }
.key {
position: absolute;
bottom: 0;
right: 55px;
}
.key ul {
display: inline-block;
margin: 0;
padding: 0;
}
.key li { margin: 0px 2px; }
/*** tooltips ***/
li .tooltip {
display: none;
font-size: 0.9em;
}
.days li:hover {
/*outline disabled due to firefox cross-broswer issue
outline: 1px solid #555;*/
position: relative;
z-index: 3;
}
li:hover .tooltip {
display: block;
position: absolute;
padding: 10px 5px;
text-align: center;
background-color: #333;
color: #f1f1f1;
}
/*** little triangle on the tooltip ***/
.tooltip:before {
content: "";
position: absolute;
width: 0;
height: 0;
}
/*** days tooltips ***/
.days li:hover .tooltip {
transform: rotate(-90deg);
/* top & left are reversed because the calendar is rotated 90 deg */
left: -217px; /* actually top */
width: 375px;
}
.days .tooltip:before {
bottom: -10px;
left: 195px;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-top: 11px solid #333;
}
/********* display badges for time period *********/
.badges {
padding-top: 25px;
margin-left: -25px;
border-bottom-left-radius: 10px;
border-bottom-right-radius: 10px;
}
.badges ul {
margin: auto;
width: 600px;
padding-bottom: 25px;
}
.badges li {
position: relative;
list-style-type: none;
display: inline-block;
width: 100px;
margin-right: 10px;
}
.badges li:nth-child(8n + 5) { margin-left: 55px; }
.badges img { width: 100%; }
.badges img:hover {
position: relative;
z-index: 2;
transform: scale(1.5);
}
.badges li:hover .tooltip {
top: 15px;
left: -185px;
width: 150px;
min-height: 40px;
}
.badges li:hover .tooltip:before {
top: 25%;
right: -10px;
border-top: 11px solid transparent;
border-bottom: 11px solid transparent;
border-left: 11px solid #333;
}
.badges li, .badges img {
margin: 0 10px 0 0;
padding: 0;
}
/****** stats ******/
.stats {
width: 750px;
margin: auto;
}
.stats-box {
float: left;
width: 230px;
margin: 10px;
padding: 15px 10px;
background: #ccc;
text-align: center;
}
.stats-box-title { font-weight: bold; }
.stats-box p { margin: 0; }
.stats-box .date {
font-style: italic;
font-size: 0.75em;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment