annotate life_calendar/index.html @ 81:256b8df1c686

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