cheeaun-life/index.html
2013-12-03 11:23:11 -05:00

263 lines
6.8 KiB
HTML

<!DOCTYPE html>
<meta charset="UTF-8">
<title>Life</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="format-detection" content="telephone=no">
<link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Open+Sans:400,300">
<style>
*{
font-family: Open Sans, Helvetica, Arial, sans-serif;
}
body{
color: #fff;
background-color: #384047;
margin: 0;
padding: 0;
font-size: 12px;
-webkit-text-size-adjust: none;
position: relative;
}
a{
color: #fff;
}
header{
position: fixed;
bottom: 0;
margin: 0;
padding: 20px;
}
header a{
margin-left: 1em;
opacity: .3;
text-decoration: none;
}
header a:hover{
opacity: .7;
text-decoration: underline;
}
h1{
display: inline;
font-size: 20px;
font-weight: normal;
line-height: 1em;
opacity: .5;
z-index: 3;
font-weight: 300;
white-space: nowrap;
}
#life{
padding-top: 40px;
padding-bottom: 5em;
}
#life section.year{
box-sizing: border-box;
border-left: 1px dashed rgba(255,255,255,.1);
color: rgba(255,255,255,.3);
position: absolute;
top: 0;
bottom: 0;
padding-left: 10px;
padding-top: 10px;
pointer-events: none;
font-weight: 300;
white-space: nowrap;
}
#life .event{
padding-right: 20px;
padding-bottom: 5px;
vertical-align: middle;
white-space: nowrap;
}
#life .event b{
font-weight: normal;
color: rgba(255,255,255,.5);
}
#life .event .time{
display: inline-block;
overflow: hidden;
height: 0;
border: 4px solid #fff;
border-radius: 4px;
margin-right: 10px;
opacity: .3;
position: relative;
left: -2px;
}
#life .event:hover .time{
opacity: .5;
}
</style>
<header>
<h1 id="title">Life</h1>
<a href="https://github.com/cheeaun/life">Fork me</a>
</header>
<div id="life"></div>
<script>
(function(){
var life = {
$title: document.getElementById('title'),
$el: document.getElementById('life'),
yearLength: 120, // 120px per year
start: function(){
life.loadConfig(function(config){
if (config.yearLength) life.yearLength = config.yearLength;
if (config.customStylesheetURL) life.injectStylesheet(config.customStylesheetURL);
life.fetch(function(response){
var data = life.parse(response);
var title = life.parseTitle(response);
life.render(title, data);
});
});
},
loadConfig: function(fn){
var xhr = new XMLHttpRequest();
xhr.open('GET', 'config.json', true);
xhr.onload = function(){
if (xhr.status == 200){
fn(JSON.parse(xhr.responseText));
} else {
fn({});
}
};
xhr.onerror = xhr.onabort = function(){
fn({});
};
xhr.send();
},
injectStylesheet: function(url){
var link = document.createElement('link');
link.rel = 'stylesheet';
link.href = url;
document.body.appendChild(link);
},
fetch: function(fn){
var xhr = new XMLHttpRequest();
xhr.open('GET', 'life.md', true);
xhr.onload = function(){
if (xhr.status == 200) fn(xhr.responseText);
};
xhr.send();
},
parse: function(response){
var list = response.match(/\-\s+[^\n\r]+/ig);
var data = [];
list.forEach(function(l){
var matches = l.match(/\-\s+([\d\/\-\~]+)\s(.*)/i);
var time = matches[1];
var text = matches[2];
data.push({
time: life.parseTime(time),
text: text
});
});
return data;
},
parseTitle: function(response){
return response.match(/[^\r\n]+/i)[0];
},
parseTime: function(time, point){
if (!point) point = 'start';
var data = {};
if (/^\~\d+$/.test(time)){ // ~YYYY
data = {
startYear: parseInt(time.slice(1), 10),
estimate: true
};
} else if (/^\d+$/.test(time)){ // YYYY
data[point + 'Year'] = parseInt(time, 10);
} else if (/^\d+\/\d+$/.test(time)){ // MM/YYYY
var t = time.split('/');
data[point + 'Month'] = parseInt(t[0], 10);
data[point + 'Year'] = parseInt(t[1], 10);
} else if (/^\d+\/\d+\/\d+$/.test(time)){ // DD/MM/YYYY
var t = time.split('/');
data[point + 'Date'] = parseInt(t[0], 10);
data[point + 'Month'] = parseInt(t[1], 10);
data[point + 'Year'] = parseInt(t[2], 10);
} else if (/\d\-/.test(time)){ // TIME-TIME
var splitTime = time.split('-');
var startTime = life.parseTime(splitTime[0]);
var endTime = life.parseTime(splitTime[1], 'end');
for (var k in startTime) { data[k] = startTime[k] }
for (var k in endTime) { data[k] = endTime[k] }
} else if (time == '~'){ // NOW
var now = new Date();
data.endYear = now.getFullYear();
data.endMonth = now.getMonth()+1;
data.endDate = now.getDate();
}
data.title = time;
return data;
},
firstYear: null,
renderEvent: function(d){
var firstYear = life.firstYear;
var yearLength = life.yearLength;
var monthLength = yearLength/12;
var dayLength = monthLength/30;
var time = d.time;
var estimate = time.estimate;
var startYear = time.startYear;
var startMonth = time.startMonth;
var startDate = time.startDate;
var endYear = time.endYear;
var endMonth = time.endMonth;
var endDate = time.endDate;
var width = 0;
// Calculate offset
var startTime = new Date(firstYear, 0, 1);
var endTime = new Date(startYear, startMonth ? startMonth-1 : 0, startDate || 1);
var daysDiff = (endTime - startTime)/(24*60*60*1000);
offset = daysDiff*dayLength;
// Calculate width
if (endYear){
var _endMonth = endMonth ? endMonth-1 : 11;
var _endDate = endDate || new Date(endYear, _endMonth+1, 0).getDate();
startTime = new Date(startYear, startMonth ? startMonth-1 : 0, startDate || 1);
endTime = new Date(endYear, _endMonth, _endDate);
daysDiff = (endTime - startTime)/(24*60*60*1000);
width = daysDiff*dayLength;
} else {
if (startDate){
width = dayLength;
} else if (startMonth){
width = monthLength;
} else {
width = yearLength;
}
}
return '<div class="event" style="margin-left: ' + offset.toFixed(2) + 'px"><div class="time" style="width: ' + width.toFixed(2) + 'px"></div><b>' + d.time.title + '</b> ' + d.text + '&nbsp;&nbsp;</div>';
return '';
},
render: function(title, data){
document.title = title;
life.$title.innerHTML = title;
var firstYear = life.firstYear = data[0].time.startYear;
var nowYear = new Date().getFullYear();
var dayLength = life.yearLength/12/30;
var html = '';
var days = 0;
for (var y=firstYear, age = 0; y<=nowYear+1; y++, age++){
html += '<section class="year" style="left: ' + (days*dayLength) + 'px">' +
y + ' (' + age + ')' +
'</section>';
days += (y % 4 == 0) ? 366 : 365;
}
data.forEach(function(d){
html += life.renderEvent(d);
});
life.$el.innerHTML = html;
}
}
life.start();
})();
</script>