Mercurial > hg > index.fcgi > www > www-1
comparison life_calendar/index.html @ 81:256b8df1c686
add life_calendar
author | paulo |
---|---|
date | Fri, 17 Jun 2016 22:24:17 -0700 |
parents | |
children | a0fdf2cf1d53 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:e29e1c14c918 |
---|---|
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 } | |
13 | |
14 a, a:visited { | |
15 color: #70BCF9; | |
16 } | |
17 | |
18 table { | |
19 width: 100%; | |
20 height: 100%; | |
21 } | |
22 | |
23 table, td { | |
24 border: 1px solid; | |
25 border-collapse: collapse; | |
26 padding: 0; | |
27 margin: 0; | |
28 } | |
29 | |
30 div#welcome { | |
31 display: none; | |
32 | |
33 width: 50vw; | |
34 margin: 8vh auto 0; | |
35 } | |
36 | |
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 } | |
47 | |
48 div#welcome .settings { | |
49 margin-left: 5px; | |
50 padding: 5px; | |
51 } | |
52 | |
53 div#welcome div#footer { | |
54 color: #a6a6a6; | |
55 padding-top: 50px; | |
56 text-align: center; | |
57 } | |
58 | |
59 #settingsForm span.unhappyMessage { | |
60 font-size: 0.8em; | |
61 padding-left: 10px; | |
62 } | |
63 | |
64 div#tooltip { | |
65 background-color: #5070D0; | |
66 padding: 0.5em; | |
67 } | |
68 | |
69 div#tooltip h1 { | |
70 color: #DFDFDF; | |
71 font-size: 1.25em; | |
72 margin: 1px; | |
73 } | |
74 | |
75 div#tooltip h2 { | |
76 color: #AAAAAA; | |
77 font-size: 0.75em; | |
78 font-weight: normal; | |
79 margin: 1px; | |
80 } | |
81 | |
82 td { | |
83 border-color: #FFFFFF; | |
84 } | |
85 | |
86 table { | |
87 border-color: #FFFFFF; | |
88 } | |
89 | |
90 tr.previous td { | |
91 background-color: #BBBBBB; | |
92 } | |
93 | |
94 tr.current td.partial { | |
95 background-color: #CCDDCC; | |
96 } | |
97 | |
98 tr.current td.today { | |
99 background-color: #DDDDCC; | |
100 } | |
101 | |
102 tr.current td.future { | |
103 background-color: #DDCCCC; | |
104 } | |
105 | |
106 tr.future { | |
107 background-color: #DDDDDD; | |
108 } | |
109 | |
110 </style> | |
111 <script src="jquery-2.1.3.min.js"></script> | |
112 <script src="happy.js"></script> | |
113 <script src="tooltip.js"></script> | |
114 | |
115 <script type="text/javascript"> | |
116 var nWeeks = 52; | |
117 var nMsPerDay = 3600 * 24 * 1000; | |
118 var nMsPerWeek = 7 * nMsPerDay; | |
119 var QSParams; | |
120 | |
121 var birthday; | |
122 var birthdayString; | |
123 var birthdayDate; | |
124 | |
125 var lifespan; | |
126 | |
127 var age; | |
128 var ageYears; | |
129 var remainder; | |
130 | |
131 var previousYears; | |
132 var futureYears; | |
133 var remainderWeeks; | |
134 var restOfYearWeeks; | |
135 | |
136 function validateBirthday(birthday) { | |
137 var birthdayPattern = /^[0-9]{4}-[0-9]{2}-[0-9]{2}$/; | |
138 return birthdayPattern.test( $("#birthday").val()); | |
139 } | |
140 | |
141 function validateLifespan(lifespan) { | |
142 var lifespanPattern = /^[0-9]{1,3}$/; | |
143 return lifespanPattern.test( $("#lifespan").val()); | |
144 } | |
145 | |
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 } | |
160 | |
161 function isLeapYear(year) { | |
162 var d = new Date(year, 1, 28); | |
163 d.setDate(d.getDate() + 1); | |
164 return d.getMonth() == 1; | |
165 } | |
166 | |
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 } | |
178 | |
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 } | |
185 | |
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 } | |
202 | |
203 function loop(x, f) { | |
204 for (var i = 0; i < x; f(i++)); | |
205 } | |
206 | |
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 } | |
214 | |
215 function cycleColors(e) { | |
216 var colors = [ | |
217 undefined, | |
218 "black", | |
219 "red", | |
220 "green", | |
221 "blue", | |
222 "white", | |
223 ]; | |
224 | |
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 } | |
234 | |
235 function _createElement(tagName, className) { | |
236 var e = document.createElement(tagName); | |
237 if (className) { | |
238 e.className = className; | |
239 } | |
240 return e; | |
241 } | |
242 | |
243 function weekElem(className) { | |
244 return _createElement("td", className); | |
245 } | |
246 | |
247 function yearElem(className) { | |
248 return _createElement("tr", className); | |
249 } | |
250 | |
251 function yearWeekElems() { | |
252 return mapLoop(nWeeks, function(i) { | |
253 var e = weekElem(); | |
254 addWeekMouseEvents(e, i); | |
255 return e; | |
256 }); | |
257 } | |
258 | |
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 } | |
271 | |
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 }); | |
287 | |
288 QSParams = getQSParams(); | |
289 | |
290 if(!("birthday" in QSParams) | !("lifespan" in QSParams)) { | |
291 // Show the form | |
292 $("#welcome").css("display", "block"); | |
293 | |
294 // Give the first field focus | |
295 $("#birthday").focus(); | |
296 return; | |
297 } | |
298 | |
299 $("body").append( '<table id="calendar"></table>' ); | |
300 | |
301 birthday = QSParams["birthday"]; | |
302 birthdayString = birthday + "T00:00:00"; | |
303 birthdayDate = new Date(birthdayString); | |
304 | |
305 lifespan = QSParams["lifespan"]; | |
306 | |
307 age = getAge(birthdayDate); | |
308 ageYears = Math.floor(age); | |
309 remainder = age - ageYears; | |
310 | |
311 previousYears = (ageYears).toFixed(0); | |
312 | |
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 } | |
322 | |
323 // Fill in lived years | |
324 loop(previousYears, function() { | |
325 var tr = yearElem("previous"); | |
326 $(tr).append(yearWeekElems()); | |
327 $("#calendar").append(tr); | |
328 }) | |
329 | |
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); | |
346 | |
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> |