Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@danroot
Last active June 1, 2016 01:10
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save danroot/7610103 to your computer and use it in GitHub Desktop.
Save danroot/7610103 to your computer and use it in GitHub Desktop.
Enter, Trello Dojo Status Board Starter Page. This page contains everything you need to stand up a page that shows time, StatusCake, Weather Underground, and Trello information. You may customize it as needed to fit your screen sizes and organization's needs. To learn more, check out http://leanpub.com/trellodojo
<!DOCTYPE html>
<html>
<head>
<title>Status Board</title>
<script type="text/javascript">
//TODO:Fill out the information below to configure your board.
//This page contains everything you need to stand up a page that shows time, StatusCake,
//Weather Underground, and Trello information. To learn more, check out http://leanpub.com/trellodojo
//You may customize it as needed to fit your screen sizes and organization's needs.
//Note that storing API Key and user information client side like this can be a security concern. Only use this approach
//if you are sure that the page can only be viewed on authorized machines and only over secure SSL connections.
window.wundergroundApiKey = ''; //Get API key and location from http://www.wunderground.com/weather/api
window.wundergroundApiLocation = 'MS/Jackson.json';
window.trelloApiKey = ''; //Get a Trello API Key here: https://trello.com/1/appKey/generate
window.trelloBoardId = ''; //Get Trello board id from URL of a board.
window.statusCakeUser = ''; //Get StatusCake Today! https://www.statuscake.com/?aff=5780
window.statusCakeApiKey = ''; //StatusCake Api Key: https://www.statuscake.com/App/APIKey.php
window.weatherImageUrl = 'http://radblast-aws.wunderground.com/cgi-bin/radar/WUNIDS_map?station=DGX&brand=0&num=1&delay=15&type=N0R&frame=0&scale=1.000&noclutter=1&t=1370642716&lat=32.42003632&lon=-90.12447357&label=&showstorms=0&map.x=400&map.y=240&centerx=400&centery=240&transx=0&transy=0&showlabels=0&severe=0&rainsnow=0&lightning=0&smooth=0&';
</script>
<link rel="stylesheet" type="text/css" href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/3.1.0/css/font-awesome.min.css" />
<style type="text/css">
body {
margin: 0;
padding: 0;
background-color: #000;
color: #fff;
font-size: 50px;
font-family: Helvetica, Arial, sans-serif;
}
div, h1, h2, h3, h4, h5 {
padding: 0;
margin: 0;
}
h1, h2, h3, h4, h5 {
font-weight: normal;
}
/*1920x1080*/
.bigscreen {
display: none;
border: 1px solid silver;
position: absolute;
top: 0;
left: 0;
width: 1915px;
height: 1080px;
}
.container {
width: 1915px;
height: 1080px;
/*overflow: hidden;*/
}
.widget {
/*border: 1px solid silver;*/
clear: none;
float: left;
margin-right: 10px;
margin-bottom: 10px;
padding: 4px;
background-color: #000; /* #191919;*/
overflow: hidden;
border: 0 solid #626262;
}
.widget-1x1 {
height: 341px;
width: 363px;
}
.widget-1x2 {
height: 705px;
width: 363px;
}
.widget-4x2 {
height: 705px;
width: 1514px;
}
.rowend {
margin-right: 0;
}
.small-label {
font-size: 35px;
}
/*Widgets*/
/*picture*/
#picture {
background-repeat: no-repeat;
background-size: cover;
}
/*Time*/
.ampm {
font-size: 40px;
}
#time h1 {
font-size: 118px;
width: 100%;
text-align: center;
margin-top: 25px;
/*margin-left: 5px;*/
margin-bottom: 30px;
}
#time h3 {
margin-top: 10px;
margin-left: 30px;
color: #ccc;
font-size: 45px;
}
/*Weather*/
#weather {
background-position: -170px -40px;
}
/*sites*/
#sites {
position: relative;
background-color: #000;
}
#sites .small-label {
text-align: center;
}
#sitesChart {
position: absolute;
top: 0;
left: 0;
width: 363px;
height: 363px;
}
#sites .up {
margin-top: 35px;
}
#sites h1 {
margin-top: 90px;
font-size: 140px;
text-align: center;
}
#sites .downDesc {
font-size: 30px;
color: brown;
}
#sites .upDesc {
font-size: 30px;
color: green;
}
#sites .uptime {
margin-top: 20px;
color: #ccc;
}
#sites .uptime span {
font-size: 30px;
}
#sites .sitesdown {
font-size: 30px;
color: #ffffff;
background-color: brown;
display: block;
position: absolute;
bottom: 0;
width: 100%;
margin-left: -5px;
padding: 3px;
overflow: hidden;
}
/*ad*/
#ad {
text-align: center;
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#263F3F), to(#000));
background-image: -webkit-linear-gradient(top, #263F3F, #000);
background-image: -moz-linear-gradient(top, #263F3F, #000);
background-image: -ms-linear-gradient(top, #263F3F, #000);
background-image: -o-linear-gradient(top, #263F3F, #000);
}
#ad h2 {
padding-top: 60px;
}
#ad a {
text-decoration: none;
color: #ffffff;
}
/*chat*/
#chat #messages {
list-style-type: none;
padding: 0;
margin: 0;
}
/*forecast*/
#forecast ul {
list-style-type: none;
margin: 0;
padding: 0;
font-size: 25px;
color: #ccc;
}
#forecast .day {
font-size: 30px;
font-weight: bold;
color: #fff;
}
/*harvest*/
.trello-listname {
font-size: 40px;
position: relative;
border-bottom: 8px solid #000;
text-align: right;
padding-right: 8px;
}
/*trello*/
.trello {
display: none;
width: 371px;
border: 0;
border-bottom: 0 none;
background-color: #000;
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#263F3F), to(#000));
background-image: -webkit-linear-gradient(top, #263F3F, #000);
background-image: -moz-linear-gradient(top, #263F3F, #000);
background-image: -ms-linear-gradient(top, #263F3F, #000);
background-image: -o-linear-gradient(top, #263F3F, #000);
padding: 0;
}
.trello h3 {
padding-left: 10px;
border-bottom: 10px solid black;
background-color: #263F3F;
text-align: right;
padding-right: 10px;
}
.trello ul {
list-style-type: none;
margin: 0;
padding: 0;
}
.trello ul li {
list-style-type: none;
margin: 0;
padding: 10px 8px;
border-bottom: 8px solid #000;
font-size: 20px;
font-weight: normal;
display: block;
position: relative;
}
.trello ul.colors {
display: block;
clear: both;
width: 100%;
position: absolute;
right: 5px;
top: 5px;
width: auto;
}
.trello .trello-listname ul.colors {
left: 0px;
top: 0px;
right: auto;
}
.trello ul.colors li {
list-style-type: none;
margin: 0 3px;
padding: 0;
width: 10px;
height: 10px;
float: left;
background-color: #fff;
border-bottom: 0px;
border-radius: 5px;
}
.trello .trello-listname ul.colors li {
height: 45px;
border-radius: 0;
margin: 0px;
}
</style>
</head>
<body>
<div class="bigscreen">
</div>
<div class="container">
<div id="time" class="widget widget-1x1">
<h1><span data-bind="text: time"></span><span class="ampm" data-bind=" text: ampm"></span></h1>
<h3 class="dayname" data-bind="text: dayname"></h3>
<h3 class="date" data-bind="text: date"></h3>
</div>
<div id="sites" class="widget widget-1x1">
<h1 data-bind="text: sitesUpCount"></h1>
<div class="small-label">sites up. <span data-bind="visible: anySitesDown, text: sitesDownCount() + ' down'"></span></div>
<canvas id="sitesChart" width="363" height="363"></canvas>
<span class="sitesdown" data-bind="visible: anySitesDown, foreach: sitesDownNames">
<i class="icon-warning-sign"></i>&nbsp<span data-bind="text: $data"></span>
</span>
</div>
<div id="ad" class="widget widget-1x1">
<h2><a href="http://leanpub.com/trellodojo">Enter Trello Dojo</a></h2>
</div>
<div id="forecast" class="widget widget-1x1">
<h1><span data-bind="text: currentTemp"></span>&deg;</h1>
<ul data-bind="foreach: forecast">
<li><span class="day" data-bind="text: $data['date']['weekday_short']"></span>
<span data-bind="text: $data['high']['fahrenheit']"></span>/
<span data-bind="text: $data['low']['fahrenheit']"></span>
<span data-bind="text: $data['conditions']"></span>
</li>
</ul>
</div>
<div id="weather" class="widget widget-1x1 rowend" data-bind="style: { backgroundImage: weatherImage }">
</div>
<div class="widget widget-4x2" data-bind="visible: $root.isTrelloAuthorized() == false, click: updateTrelloInfo">Click to authorize Trello</div>
<!-- ko if:isTrelloAuthorized -->
<!-- ko foreach:{data:statusListNames, afterAdd:flyIn} -->
<div class="widget widget-1x2 trello">
<div class="trello-listname">
<ul class="colors" data-bind="foreach: { data: $root.labelsForList($data), as: 'color' }">
<li data-bind="style: { backgroundColor: color }"></li>
</ul>
<span data-bind="text: $data.name"></span>
</div>
<ul data-bind="foreach: $root.cardsForList($data)">
<li>
<ul class="colors" data-bind="foreach: labels">
<li data-bind="style: { backgroundColor: color }"></li>
</ul>
<div data-bind="text: name"></div>
</li>
</ul>
</div>
<!-- /ko -->
<!-- /ko -->
<div id="else" class="widget widget-1x1 rowend"></div>
<div id="e" class="widget widget-1x1 rowend"></div>
</div>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.0.2/jquery.min.js"></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/moment.js/2.0.0/moment.min.js"></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/knockout/2.2.1/knockout-min.js"></script>
<script type="text/javascript">
document.writeln(' <script type="text/javascript" src="//api.trello.com/1/client.js?key=' + window.trelloApiKey + '"></s' + 'cript>');
function drawGauge(canvasId, color1, color2, lineWidth, min, max, value) {
var canvas = document.getElementById(canvasId);
var context = canvas.getContext("2d");
var path = getGaugeArc(min, max, value);
context.beginPath();
context.lineWidth = lineWidth;
context.strokeStyle = color1;
context.arc(181.5, 181.5, 130, path.startAngle, path.midAngle);
context.stroke();
context.beginPath();
context.strokeStyle = color2;
context.arc(181.5, 181.5, 130, path.midAngle, path.endAngle);
context.stroke();
}
function getGaugeArc(min, max, value) {
var mid = 0;
if (max != value) mid = (Math.PI) + ((value / (max - min)) * Math.PI);
return {
startAngle: Math.PI,
midAngle: mid,
endAngle: 0
};
}
function ViewModel() {
var self = this;
//chat
this.currentMessageIndex = ko.observable(0);
this.messages = ko.observableArray([]);
this.currentMessage = ko.computed(function () {
if (self.messages().length == 0) return '';
return self.messages()[self.currentMessageIndex()];
});
this.rotateMessages = function () {
var next = self.currentMessageIndex() + 1;
if (next >= self.messages().length) next = 0;
self.currentMessageIndex(next);
};
//time
this.time = ko.observable('12:59');
this.ampm = ko.observable('PM');
this.dayname = ko.observable('Friday');
this.date = ko.observable('12 Jun 99');
this.updateTime = function () {
var now = moment();
self.time(now.format('h:mm'));
self.ampm(now.format('a'));
self.dayname(now.format('dddd'));
self.date(now.format('MMM Do YYYY'));
};
//weather
this.weatherImageUrl = window.weatherImageUrl;
this.weatherImage = ko.observable('');
this.currentTemp = ko.observable(98);
this.forecast = ko.observableArray([]);
this.loTemp = ko.observable(0);
this.updateWeather = function () {
self.weatherImage('url(' + self.weatherImageUrl + new Date().getTime() + ')');
if (window.wundergroundApiKey == '') return;
$.ajax({
url: "http://api.wunderground.com/api/" + window.wundergroundApiKey + "/geolookup/conditions/q/" + window.wundergroundApiLocation,
dataType: "jsonp",
success: function (parsed_json) {
var temp_f = parsed_json['current_observation']['temp_f'];
self.currentTemp(temp_f);
}
});
$.ajax({
url: "http://api.wunderground.com/api/" + window.wundergroundApiKey + "/forecast/q/" + window.wundergroundApiLocation,
dataType: "jsonp",
success: function (parsed_json) {
var forecast = parsed_json['forecast']['simpleforecast']['forecastday'];
self.forecast(forecast);
}
});
};
//sites
this.sitesDownCount = ko.observable(0);
this.sitesUpCount = ko.observable(0);
this.sitesDownNames = ko.observable(['']);
this.anySitesDown = ko.computed(function () {
return self.sitesDownCount() > 0;
});
this.updateSitesCount = function () {
if (window.statusCakeUser == '') return;
$.getJSON("http://query.yahooapis.com/v1/public/yql",
{
q: 'select * from json where url=\"https://www.statuscake.com/API/Tests?Username=' + window.statusCakeUser + '&API=' + window.statusCakeApiKey + '\"',
format: "json"
},
function(data) {
if (data.query.results) {
data = data.query.results.json.json;
var downSites = data.filter(function (o) { return o.Status == "Down" && o.Paused == false; });
self.sitesDownCount(downSites.length);
self.sitesUpCount(data.filter(function (o) { return o.Status == "Up"; }).length);
var totalSites = self.sitesDownCount() + self.sitesUpCount();
drawGauge('sitesChart', '#00ff00', '#ff0000', 70, 0, totalSites, self.sitesUpCount());
var downSiteNames = [];
downSites.forEach(function (o) {
downSiteNames.push(o.WebsiteName);
});
self.sitesDownNames(downSiteNames);
}
});
};
//trello
this.statusBoard = ko.observable({});
this.statusListNames = ko.computed(function () {
if (self.statusBoard() == null) return [];
var allLists = self.statusBoard().lists;
if (allLists == null) return [];
return allLists.filter(function (o) { return o.name != 'About'; });
});
this.cardsForList = function (list) {
if (self.statusBoard() == null) return [];
var allCards = self.statusBoard().cards;
if (allCards == null) return [];
return allCards.filter(function (o) { return o.idList == list.id; });
};
this.labelsForList = function (list) {
if (self.statusBoard() == null) return [];
var cards = self.cardsForList(list);
if (cards == null) return [];
if (cards.length == 0) return [];
var uniqueLabels = [];
cards.forEach(function (e) {
if (e.labels == null || e.labels.length == 0) return;
e.labels.forEach(function (l) {
if (uniqueLabels.indexOf(l.color) == -1) {
uniqueLabels.push(l.color);
}
});
});
console.log(list.name + ' ' + uniqueLabels.length);
return uniqueLabels;
};
this.isTrelloAuthorized = ko.observable(false);
this.updateTrelloInfo = function () {
Trello.authorize({
expiration: 'never',
type: 'redirect',
name: 'Status Board',
persist: true,
success: function () {
self.isTrelloAuthorized(true);
Trello.boards.get(window.trelloBoardId, { cards: 'open', lists: 'open', list_fields: 'name' }, function (board) {
self.statusBoard(board);
});
},
error: function () {
self.isTrelloAuthorized(false);
}
});
};
this.flyIn = function (e) {
$(e).fadeIn('slow');
};
//common
this.minute = 60000;
this.hour = 3600000;
this.poll = function (func, interval) {
func();
return window.setInterval(func, interval);
};
//Initialize
this.poll(this.updateTime, this.minute);
this.poll(this.updateWeather, this.hour);
this.poll(this.updateSitesCount, this.minute * 5);
this.poll(this.updateTrelloInfo, this.minute * 5);
// this.initializeChat();
this.poll(this.rotateMessages, this.minute * 0.5);
}
$(function() {
var model = new ViewModel();
ko.applyBindings(model);
});
</script>
</body>
</html>
@danroot
Copy link
Author

danroot commented Nov 24, 2013

This gist contains everything you need to stand up a page that shows time, StatusCake, Weather Underground, and Trello information. To learn more, check out http://leanpub.com/trellodojo

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment