view life_calendar/index.html @ 81:256b8df1c686

add life_calendar
author paulo
date Fri, 17 Jun 2016 22:24:17 -0700
parents
children a0fdf2cf1d53
line source
1 <html>
2 <head>
3 <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
4 <title>Life Calendar</title>
5 <style type="text/css">
6 body {
7 padding: 0;
8 margin: 0;
9 position: relative;
10 font-family: sans-serif;
11 background-size: cover;
12 }
14 a, a:visited {
15 color: #70BCF9;
16 }
18 table {
19 width: 100%;
20 height: 100%;
21 }
23 table, td {
24 border: 1px solid;
25 border-collapse: collapse;
26 padding: 0;
27 margin: 0;
28 }
30 div#welcome {
31 display: none;
33 width: 50vw;
34 margin: 8vh auto 0;
35 }
37 div#welcome h1 {
38 text-align: center;
39 text-transform: uppercase;
40 font-family: serif;
41 font-size: 4em;
42 }
43 div#welcome form {
44 border: 1px solid #999999;
45 padding: 10px 20px;
46 }
48 div#welcome .settings {
49 margin-left: 5px;
50 padding: 5px;
51 }
53 div#welcome div#footer {
54 color: #a6a6a6;
55 padding-top: 50px;
56 text-align: center;
57 }
59 #settingsForm span.unhappyMessage {
60 font-size: 0.8em;
61 padding-left: 10px;
62 }
64 div#tooltip {
65 background-color: #5070D0;
66 padding: 0.5em;
67 }
69 div#tooltip h1 {
70 color: #DFDFDF;
71 font-size: 1.25em;
72 margin: 1px;
73 }
75 div#tooltip h2 {
76 color: #AAAAAA;
77 font-size: 0.75em;
78 font-weight: normal;
79 margin: 1px;
80 }
82 td {
83 border-color: #FFFFFF;
84 }
86 table {
87 border-color: #FFFFFF;
88 }
90 tr.previous td {
91 background-color: #BBBBBB;
92 }
94 tr.current td.partial {
95 background-color: #CCDDCC;
96 }
98 tr.current td.today {
99 background-color: #DDDDCC;
100 }
102 tr.current td.future {
103 background-color: #DDCCCC;
104 }
106 tr.future {
107 background-color: #DDDDDD;
108 }
110 </style>
111 <script src="jquery-2.1.3.min.js"></script>
112 <script src="happy.js"></script>
113 <script src="tooltip.js"></script>
115 <script type="text/javascript">
116 var nWeeks = 52;
117 var nMsPerDay = 3600 * 24 * 1000;
118 var nMsPerWeek = 7 * nMsPerDay;
119 var QSParams;
121 var birthday;
122 var birthdayString;
123 var birthdayDate;
125 var lifespan;
127 var age;
128 var ageYears;
129 var remainder;
131 var previousYears;
132 var futureYears;
133 var remainderWeeks;
134 var restOfYearWeeks;
136 function validateBirthday(birthday) {
137 var birthdayPattern = /^[0-9]{4}-[0-9]{2}-[0-9]{2}$/;
138 return birthdayPattern.test( $("#birthday").val());
139 }
141 function validateLifespan(lifespan) {
142 var lifespanPattern = /^[0-9]{1,3}$/;
143 return lifespanPattern.test( $("#lifespan").val());
144 }
146 // Read a page's GET URL variables and return them as an associative array.
147 // From http://jquery-howto.blogspot.ca/2009/09/get-url-parameters-values-with-jquery.html
148 function getQSParams()
149 {
150 var vars = [], hash;
151 var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
152 for(var i = 0; i < hashes.length; i++)
153 {
154 hash = hashes[i].split('=');
155 vars.push(hash[0]);
156 vars[hash[0]] = hash[1];
157 }
158 return vars;
159 }
161 function isLeapYear(year) {
162 var d = new Date(year, 1, 28);
163 d.setDate(d.getDate() + 1);
164 return d.getMonth() == 1;
165 }
167 function getAge(date) {
168 var d = new Date(date), now = new Date();
169 var years = now.getFullYear() - d.getFullYear();
170 d.setFullYear(d.getFullYear() + years);
171 if (d > now) {
172 years--;
173 d.setFullYear(d.getFullYear() - 1);
174 }
175 var days = (now.getTime() - d.getTime()) / nMsPerDay;
176 return years + days / (isLeapYear(now.getFullYear()) ? 366 : 365);
177 }
179 function getCalendar(d, week, year) {
180 var c = new Date(d);
181 c.setFullYear(d.getFullYear() + year);
182 c.setTime(c.getTime() + (week * nMsPerWeek));
183 return c;
184 }
186 function getWeekYearCal(e, week) {
187 var yearParent = e.parentNode;
188 var calParent = yearParent.parentNode;
189 var year;
190 for (year = 0; year < calParent.childNodes.length; year++) {
191 if (calParent.childNodes[year] === yearParent) {
192 break;
193 }
194 }
195 var cal = getCalendar(birthdayDate, week, year);
196 return {
197 week: week,
198 year: year,
199 cal: cal,
200 }
201 }
203 function loop(x, f) {
204 for (var i = 0; i < x; f(i++));
205 }
207 function mapLoop(x, f) {
208 var ret = [];
209 for (var i = 0; i < x; i++) {
210 ret.push(f(i));
211 }
212 return ret;
213 }
215 function cycleColors(e) {
216 var colors = [
217 undefined,
218 "black",
219 "red",
220 "green",
221 "blue",
222 "white",
223 ];
225 var i = 0;
226 for (; i < colors.length, colors[i] != e._fill_color; i++);
227 e._fill_color = colors[(i + 1) % colors.length];
228 if (e._fill_color != undefined) {
229 e.setAttribute("style", "background-color: " + e._fill_color);
230 } else {
231 e.setAttribute("style", undefined);
232 }
233 }
235 function _createElement(tagName, className) {
236 var e = document.createElement(tagName);
237 if (className) {
238 e.className = className;
239 }
240 return e;
241 }
243 function weekElem(className) {
244 return _createElement("td", className);
245 }
247 function yearElem(className) {
248 return _createElement("tr", className);
249 }
251 function yearWeekElems() {
252 return mapLoop(nWeeks, function(i) {
253 var e = weekElem();
254 addWeekMouseEvents(e, i);
255 return e;
256 });
257 }
259 function addWeekMouseEvents(e, i) {
260 e.addEventListener("mouseover", function(evt) {
261 var wyc = getWeekYearCal(e, i);
262 var calYear = wyc.cal.getFullYear();
263 var calMonth = wyc.cal.getMonth() + 1;
264 var calDay = wyc.cal.getDate();
265 createTooltip(evt, calYear + '-' + calMonth + '-' + calDay, "(Week: " + wyc.week + ", Year: " + wyc.year + ")");
266 });
267 e.addEventListener("click", function() {
268 cycleColors(e);
269 });
270 }
272 $( document ).ready(function() {
273 $( "#settingsForm" ).isHappy({
274 fields: {
275 '#birthday': {
276 required: true,
277 test: validateBirthday,
278 message: 'Please enter your birthday (like YYYY-MM-DD).'
279 },
280 '#lifespan': {
281 required: true,
282 test: validateLifespan,
283 message: 'Please enter your expected lifespan (like 90)'
284 }
285 }
286 });
288 QSParams = getQSParams();
290 if(!("birthday" in QSParams) | !("lifespan" in QSParams)) {
291 // Show the form
292 $("#welcome").css("display", "block");
294 // Give the first field focus
295 $("#birthday").focus();
296 return;
297 }
299 $("body").append( '<table id="calendar"></table>' );
301 birthday = QSParams["birthday"];
302 birthdayString = birthday + "T00:00:00";
303 birthdayDate = new Date(birthdayString);
305 lifespan = QSParams["lifespan"];
307 age = getAge(birthdayDate);
308 ageYears = Math.floor(age);
309 remainder = age - ageYears;
311 previousYears = (ageYears).toFixed(0);
313 if( lifespan > ageYears) {
314 futureYears = lifespan - ageYears - 1;
315 remainderWeeks = Math.floor(remainder * nWeeks);
316 restOfYearWeeks = Math.ceil(nWeeks - remainderWeeks) - 1;
317 } else {
318 futureYears = 0;
319 remainderWeeks = nWeeks;
320 restOfYearWeeks = 0;
321 }
323 // Fill in lived years
324 loop(previousYears, function() {
325 var tr = yearElem("previous");
326 $(tr).append(yearWeekElems());
327 $("#calendar").append(tr);
328 })
330 // Fill in the current birth-year (the number of weeks elapsed since the most recent birthday.)
331 var current_tr = yearElem("current");
332 loop(remainderWeeks, function(i) {
333 var e = weekElem("partial");
334 addWeekMouseEvents(e, i);
335 $(current_tr).append(e);
336 })
337 var e = weekElem("today");
338 addWeekMouseEvents(e, remainderWeeks);
339 $(current_tr).append(e);
340 loop(restOfYearWeeks, function(i) {
341 var e = weekElem("future");
342 addWeekMouseEvents(e, remainderWeeks + 1 + i);
343 $(current_tr).append(e);
344 })
345 $("#calendar").append(current_tr);
347 // Fill in future years
348 loop(futureYears, function() {
349 var tr = yearElem("future");
350 $(tr).append(yearWeekElems());
351 $("#calendar").append(tr);
352 })
353 });
354 </script>
355 </head>
356 <body>
357 <div id="welcome">
358 <h1>Life Calendar</h1>
359 <p>
360 A minimalist life calendar. Shows the number of weeks you've lived and the number of weeks you
361 have left.
362 </p>
363 <form method="get" action="" id="settingsForm">
364 <p>
365 <label for="birthday">Birthday:
366 <input class="settings" id="birthday" name="birthday" placeholder="1985-01-01"/>
367 </label>
368 </p>
369 <p>
370 <label for="lifespan">Life expectancy:
371 <input class="settings" id="lifespan" name="lifespan" placeholder="90"/>
372 </label>
373 </p>
374 <p>
375 <input type="submit" id="submit" value="Show calendar" />
376 </p>
377 </form>
378 <div id="footer">
379 <p>
380 Based on <a href="http://waitbutwhy.com/2014/05/life-weeks.html">Your Life in Weeks</a>
381 and <a href="http://count.life">count.life</a>.
382 </p>
383 </div>
384 </div>
385 </body>
386 </html>