changeset 81:256b8df1c686

add life_calendar
author paulo
date Fri, 17 Jun 2016 22:24:17 -0700
parents ff0878207f0e
children d7d67887102f
files life_calendar/happy.js life_calendar/index.html life_calendar/tooltip.js
diffstat 3 files changed, 578 insertions(+), 0 deletions(-) [+]
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/life_calendar/happy.js	Fri Jun 17 22:24:17 2016 -0700
     1.3 @@ -0,0 +1,134 @@
     1.4 +/*global $*/
     1.5 +(function happyJS($) {
     1.6 +    function trim(el) {
     1.7 +        return (''.trim) ? el.val().trim() : $.trim(el.val());
     1.8 +    }
     1.9 +    $.fn.isHappy = function isHappy(config) {
    1.10 +        var fields = [], item;
    1.11 +        var pauseMessages = false;
    1.12 +
    1.13 +        function isFunction(obj) {
    1.14 +            return !!(obj && obj.constructor && obj.call && obj.apply);
    1.15 +        }
    1.16 +        function defaultError(error) { //Default error template
    1.17 +            var msgErrorClass = config.classes && config.classes.message || 'unhappyMessage';
    1.18 +            return $('<span id="' + error.id + '" class="' + msgErrorClass + '" role="alert">' + error.message + '</span>');
    1.19 +        }
    1.20 +        function getError(error) { //Generate error html from either config or default
    1.21 +            if (isFunction(config.errorTemplate)) {
    1.22 +                return config.errorTemplate(error);
    1.23 +            }
    1.24 +            return defaultError(error);
    1.25 +        }
    1.26 +        function handleSubmit() {
    1.27 +            var  i, l;
    1.28 +            var errors = false;
    1.29 +            for (i = 0, l = fields.length; i < l; i += 1) {
    1.30 +                if (!fields[i].testValid(true)) {
    1.31 +                    errors = true;
    1.32 +                }
    1.33 +            }
    1.34 +            if (errors) {
    1.35 +                if (isFunction(config.unHappy)) config.unHappy();
    1.36 +                return false;
    1.37 +            } else if (config.testMode) {
    1.38 +                if (isFunction(config.happy)) return config.happy();
    1.39 +                if (window.console) console.warn('would have submitted');
    1.40 +                return false;
    1.41 +            }
    1.42 +            if (isFunction(config.happy)) return config.happy();
    1.43 +        }
    1.44 +        function handleMouseUp() {
    1.45 +            pauseMessages = false;
    1.46 +        }
    1.47 +        function handleMouseDown() {
    1.48 +            pauseMessages = true;
    1.49 +            $(window).bind('mouseup', handleMouseUp);
    1.50 +        }
    1.51 +        function processField(opts, selector) {
    1.52 +            var field = $(selector);
    1.53 +            var error = {
    1.54 +                message: opts.message || '',
    1.55 +                id: selector.slice(1) + '_unhappy'
    1.56 +            };
    1.57 +            var errorEl = $(error.id).length > 0 ? $(error.id) : getError(error);
    1.58 +            var handleBlur = function handleBlur() {
    1.59 +                if (!pauseMessages) {
    1.60 +                    field.testValid();
    1.61 +                } else {
    1.62 +                    $(window).bind('mouseup', field.testValid.bind(this));
    1.63 +                }
    1.64 +            };
    1.65 +
    1.66 +            fields.push(field);
    1.67 +            field.testValid = function testValid(submit) {
    1.68 +                var val, gotFunc, temp;
    1.69 +                var el = $(this);
    1.70 +                var errorTarget = (opts.errorTarget && $(opts.errorTarget)) || el;
    1.71 +                var error = false;
    1.72 +                var required = !!el.get(0).attributes.getNamedItem('required') || opts.required;
    1.73 +                var password = (field.attr('type') === 'password');
    1.74 +                var arg = isFunction(opts.arg) ? opts.arg() : opts.arg;
    1.75 +                var fieldErrorClass = config.classes && config.classes.field || 'unhappy';
    1.76 +
    1.77 +                // handle control groups (checkboxes, radio)
    1.78 +                if (el.length > 1) {
    1.79 +                  val = [];
    1.80 +                  el.each(function(i,obj) {
    1.81 +                    val.push($(obj).val());
    1.82 +                  });
    1.83 +                  val = val.join(',');
    1.84 +                } else {
    1.85 +                  // clean it or trim it
    1.86 +                  if (isFunction(opts.clean)) {
    1.87 +                      val = opts.clean(el.val());
    1.88 +                  } else if (!password && typeof opts.trim === 'undefined' || opts.trim) {
    1.89 +                      val = trim(el);
    1.90 +                  } else {
    1.91 +                      val = el.val();
    1.92 +                  }
    1.93 +
    1.94 +                  // write it back to the field
    1.95 +                  el.val(val);
    1.96 +                }
    1.97 +
    1.98 +                // get the value
    1.99 +                gotFunc = ((val.length > 0 || required === 'sometimes') && isFunction(opts.test));
   1.100 +
   1.101 +                // check if we've got an error on our hands
   1.102 +                if (submit === true && required === true && val.length === 0) {
   1.103 +                    error = true;
   1.104 +                } else if (gotFunc) {
   1.105 +                    error = !opts.test(val, arg);
   1.106 +                }
   1.107 +
   1.108 +                if (error) {
   1.109 +                    errorTarget.addClass(fieldErrorClass).after(errorEl);
   1.110 +                    return false;
   1.111 +                } else {
   1.112 +                    temp = errorEl.get(0);
   1.113 +                    // this is for zepto
   1.114 +                    if (temp.parentNode) {
   1.115 +                        temp.parentNode.removeChild(temp);
   1.116 +                    }
   1.117 +                    errorTarget.removeClass(fieldErrorClass);
   1.118 +                    return true;
   1.119 +                }
   1.120 +            };
   1.121 +            field.bind(opts.when || config.when || 'blur', handleBlur);
   1.122 +        }
   1.123 +
   1.124 +        for (item in config.fields) {
   1.125 +            processField(config.fields[item], item);
   1.126 +        }
   1.127 +
   1.128 +        $(config.submitButton || this).bind('mousedown', handleMouseDown);
   1.129 +
   1.130 +        if (config.submitButton) {
   1.131 +            $(config.submitButton).click(handleSubmit);
   1.132 +        } else {
   1.133 +            this.bind('submit', handleSubmit);
   1.134 +        }
   1.135 +        return this;
   1.136 +    };
   1.137 +})(this.jQuery || this.Zepto);
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/life_calendar/index.html	Fri Jun 17 22:24:17 2016 -0700
     2.3 @@ -0,0 +1,386 @@
     2.4 +<html>
     2.5 +    <head>
     2.6 +        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
     2.7 +        <title>Life Calendar</title>
     2.8 +        <style type="text/css">
     2.9 +            body {
    2.10 +                padding: 0;
    2.11 +                margin: 0;
    2.12 +                position: relative;
    2.13 +                font-family: sans-serif;
    2.14 +                background-size: cover;
    2.15 +            }
    2.16 +
    2.17 +            a, a:visited {
    2.18 +                color: #70BCF9;
    2.19 +            }
    2.20 +
    2.21 +            table {
    2.22 +                width: 100%;
    2.23 +                height: 100%;
    2.24 +            }
    2.25 +
    2.26 +            table, td {
    2.27 +                border: 1px solid;
    2.28 +                border-collapse: collapse;
    2.29 +                padding: 0;
    2.30 +                margin: 0;
    2.31 +            }
    2.32 +
    2.33 +            div#welcome {
    2.34 +                display: none;
    2.35 +
    2.36 +                width: 50vw;
    2.37 +                margin: 8vh auto 0;
    2.38 +            }
    2.39 +
    2.40 +            div#welcome h1 {
    2.41 +                text-align: center;
    2.42 +                text-transform: uppercase;
    2.43 +                font-family: serif;
    2.44 +                font-size: 4em;
    2.45 +            }
    2.46 +            div#welcome form {
    2.47 +                border: 1px solid #999999;
    2.48 +                padding: 10px 20px;
    2.49 +            }
    2.50 +
    2.51 +            div#welcome .settings {
    2.52 +                margin-left: 5px;
    2.53 +                padding: 5px;
    2.54 +            }
    2.55 +
    2.56 +            div#welcome div#footer {
    2.57 +                color: #a6a6a6;
    2.58 +                padding-top: 50px;
    2.59 +                text-align: center;
    2.60 +            }
    2.61 +
    2.62 +            #settingsForm span.unhappyMessage {
    2.63 +                font-size: 0.8em;
    2.64 +                padding-left: 10px;
    2.65 +            }
    2.66 +
    2.67 +            div#tooltip {
    2.68 +                background-color: #5070D0;
    2.69 +                padding: 0.5em;
    2.70 +            }
    2.71 +
    2.72 +            div#tooltip h1 {
    2.73 +                color: #DFDFDF;
    2.74 +                font-size: 1.25em;
    2.75 +                margin: 1px;
    2.76 +            }
    2.77 +
    2.78 +            div#tooltip h2 {
    2.79 +                color: #AAAAAA;
    2.80 +                font-size: 0.75em;
    2.81 +                font-weight: normal;
    2.82 +                margin: 1px;
    2.83 +            }
    2.84 +
    2.85 +            td {
    2.86 +                border-color: #FFFFFF;
    2.87 +            }
    2.88 +
    2.89 +            table {
    2.90 +                border-color: #FFFFFF;
    2.91 +            }
    2.92 +
    2.93 +            tr.previous td {
    2.94 +                background-color: #BBBBBB;
    2.95 +            }
    2.96 +
    2.97 +            tr.current td.partial {
    2.98 +                background-color: #CCDDCC;
    2.99 +            }
   2.100 +
   2.101 +            tr.current td.today {
   2.102 +                background-color: #DDDDCC;
   2.103 +            }
   2.104 +
   2.105 +            tr.current td.future {
   2.106 +                background-color: #DDCCCC;
   2.107 +            }
   2.108 +
   2.109 +            tr.future {
   2.110 +                background-color: #DDDDDD;
   2.111 +            }
   2.112 +
   2.113 +        </style>
   2.114 +        <script src="jquery-2.1.3.min.js"></script>
   2.115 +        <script src="happy.js"></script>
   2.116 +        <script src="tooltip.js"></script>
   2.117 +
   2.118 +        <script type="text/javascript">
   2.119 +            var nWeeks = 52;
   2.120 +            var nMsPerDay = 3600 * 24 * 1000;
   2.121 +            var nMsPerWeek = 7 * nMsPerDay;
   2.122 +            var QSParams;
   2.123 +
   2.124 +            var birthday;
   2.125 +            var birthdayString;
   2.126 +            var birthdayDate;
   2.127 +
   2.128 +            var lifespan;
   2.129 +
   2.130 +            var age;
   2.131 +            var ageYears;
   2.132 +            var remainder;
   2.133 +
   2.134 +            var previousYears;
   2.135 +            var futureYears;
   2.136 +            var remainderWeeks;
   2.137 +            var restOfYearWeeks;
   2.138 +
   2.139 +            function validateBirthday(birthday) {
   2.140 +                var birthdayPattern = /^[0-9]{4}-[0-9]{2}-[0-9]{2}$/;
   2.141 +                return birthdayPattern.test( $("#birthday").val());
   2.142 +            }
   2.143 +
   2.144 +            function validateLifespan(lifespan) {
   2.145 +                var lifespanPattern = /^[0-9]{1,3}$/;
   2.146 +                return lifespanPattern.test( $("#lifespan").val());
   2.147 +            }
   2.148 +
   2.149 +            // Read a page's GET URL variables and return them as an associative array.
   2.150 +            // From http://jquery-howto.blogspot.ca/2009/09/get-url-parameters-values-with-jquery.html
   2.151 +            function getQSParams()
   2.152 +            {
   2.153 +                var vars = [], hash;
   2.154 +                var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
   2.155 +                for(var i = 0; i < hashes.length; i++)
   2.156 +                {
   2.157 +                    hash = hashes[i].split('=');
   2.158 +                    vars.push(hash[0]);
   2.159 +                    vars[hash[0]] = hash[1];
   2.160 +                }
   2.161 +                return vars;
   2.162 +            }
   2.163 +
   2.164 +            function isLeapYear(year) {
   2.165 +                var d = new Date(year, 1, 28);
   2.166 +                d.setDate(d.getDate() + 1);
   2.167 +                return d.getMonth() == 1;
   2.168 +            }
   2.169 +
   2.170 +            function getAge(date) {
   2.171 +                var d = new Date(date), now = new Date();
   2.172 +                var years = now.getFullYear() - d.getFullYear();
   2.173 +                d.setFullYear(d.getFullYear() + years);
   2.174 +                if (d > now) {
   2.175 +                    years--;
   2.176 +                    d.setFullYear(d.getFullYear() - 1);
   2.177 +                }
   2.178 +                var days = (now.getTime() - d.getTime()) / nMsPerDay;
   2.179 +                return years + days / (isLeapYear(now.getFullYear()) ? 366 : 365);
   2.180 +            }
   2.181 +
   2.182 +            function getCalendar(d, week, year) {
   2.183 +                var c = new Date(d);
   2.184 +                c.setFullYear(d.getFullYear() + year);
   2.185 +                c.setTime(c.getTime() + (week * nMsPerWeek)); 
   2.186 +                return c;
   2.187 +            }
   2.188 +
   2.189 +            function getWeekYearCal(e, week) {
   2.190 +                var yearParent = e.parentNode;
   2.191 +                var calParent = yearParent.parentNode;
   2.192 +                var year;
   2.193 +                for (year = 0; year < calParent.childNodes.length; year++) {
   2.194 +                    if (calParent.childNodes[year] === yearParent) {
   2.195 +                        break;
   2.196 +                    }
   2.197 +                }
   2.198 +                var cal = getCalendar(birthdayDate, week, year);
   2.199 +                return {
   2.200 +                    week: week,
   2.201 +                    year: year,
   2.202 +                    cal: cal,
   2.203 +                }
   2.204 +            }
   2.205 +
   2.206 +            function loop(x, f) {
   2.207 +                for (var i = 0; i < x; f(i++));
   2.208 +            }
   2.209 +
   2.210 +            function mapLoop(x, f) {
   2.211 +                var ret = [];
   2.212 +                for (var i = 0; i < x; i++) {
   2.213 +                  ret.push(f(i));
   2.214 +                }
   2.215 +                return ret;
   2.216 +            }
   2.217 +
   2.218 +            function cycleColors(e) {
   2.219 +                var colors = [
   2.220 +                    undefined,
   2.221 +                    "black",
   2.222 +                    "red",
   2.223 +                    "green",
   2.224 +                    "blue",
   2.225 +                    "white",
   2.226 +                ];
   2.227 +
   2.228 +                var i = 0;
   2.229 +                for (; i < colors.length, colors[i] != e._fill_color; i++);
   2.230 +                e._fill_color = colors[(i + 1) % colors.length];
   2.231 +                if (e._fill_color != undefined) {
   2.232 +                    e.setAttribute("style", "background-color: " + e._fill_color);
   2.233 +                } else {
   2.234 +                    e.setAttribute("style", undefined);
   2.235 +                }
   2.236 +            }
   2.237 +
   2.238 +            function _createElement(tagName, className) {
   2.239 +                var e = document.createElement(tagName);
   2.240 +                if (className) {
   2.241 +                    e.className = className;
   2.242 +                }
   2.243 +                return e;
   2.244 +            }
   2.245 +
   2.246 +            function weekElem(className) {
   2.247 +                return _createElement("td", className);
   2.248 +            }
   2.249 +
   2.250 +            function yearElem(className) {
   2.251 +                return _createElement("tr", className);
   2.252 +            }
   2.253 +
   2.254 +            function yearWeekElems() {
   2.255 +                return mapLoop(nWeeks, function(i) {
   2.256 +                    var e = weekElem();
   2.257 +                    addWeekMouseEvents(e, i);
   2.258 +                    return e;
   2.259 +                });
   2.260 +            }
   2.261 +
   2.262 +            function addWeekMouseEvents(e, i) {
   2.263 +                e.addEventListener("mouseover", function(evt) {
   2.264 +                    var wyc = getWeekYearCal(e, i);
   2.265 +                    var calYear = wyc.cal.getFullYear();
   2.266 +                    var calMonth = wyc.cal.getMonth() + 1;
   2.267 +                    var calDay = wyc.cal.getDate();
   2.268 +                    createTooltip(evt, calYear + '-' + calMonth + '-' + calDay, "(Week: " + wyc.week + ", Year: " + wyc.year + ")");
   2.269 +                });
   2.270 +                e.addEventListener("click", function() {
   2.271 +                    cycleColors(e);
   2.272 +                });
   2.273 +            }
   2.274 +
   2.275 +            $( document ).ready(function() {
   2.276 +                $( "#settingsForm" ).isHappy({
   2.277 +                    fields: {
   2.278 +                        '#birthday': {
   2.279 +                            required: true,
   2.280 +                            test: validateBirthday,
   2.281 +                            message: 'Please enter your birthday (like YYYY-MM-DD).'
   2.282 +                        },
   2.283 +                        '#lifespan': {
   2.284 +                            required: true,
   2.285 +                            test: validateLifespan,
   2.286 +                            message: 'Please enter your expected lifespan (like 90)'
   2.287 +                        }
   2.288 +                    }
   2.289 +                });
   2.290 +
   2.291 +                QSParams = getQSParams();
   2.292 +
   2.293 +                if(!("birthday" in QSParams) | !("lifespan" in QSParams)) {
   2.294 +                    // Show the form
   2.295 +                    $("#welcome").css("display", "block");
   2.296 +
   2.297 +                    // Give the first field focus
   2.298 +                    $("#birthday").focus();
   2.299 +                    return;
   2.300 +                }
   2.301 +
   2.302 +                $("body").append( '<table id="calendar"></table>' );
   2.303 +
   2.304 +                birthday = QSParams["birthday"];
   2.305 +                birthdayString = birthday + "T00:00:00";
   2.306 +                birthdayDate = new Date(birthdayString);
   2.307 +
   2.308 +                lifespan = QSParams["lifespan"];
   2.309 +
   2.310 +                age = getAge(birthdayDate);
   2.311 +                ageYears = Math.floor(age);
   2.312 +                remainder = age - ageYears;
   2.313 +
   2.314 +                previousYears = (ageYears).toFixed(0);
   2.315 +
   2.316 +                if( lifespan > ageYears) {
   2.317 +                    futureYears = lifespan - ageYears - 1;
   2.318 +                    remainderWeeks = Math.floor(remainder * nWeeks);
   2.319 +                    restOfYearWeeks = Math.ceil(nWeeks - remainderWeeks) - 1;
   2.320 +                } else {
   2.321 +                    futureYears = 0;
   2.322 +                    remainderWeeks = nWeeks;
   2.323 +                    restOfYearWeeks = 0;
   2.324 +                }
   2.325 +
   2.326 +                // Fill in lived years
   2.327 +                loop(previousYears, function() {
   2.328 +                    var tr = yearElem("previous");
   2.329 +                    $(tr).append(yearWeekElems());
   2.330 +                    $("#calendar").append(tr);
   2.331 +                })
   2.332 +
   2.333 +                // Fill in the current birth-year (the number of weeks elapsed since the most recent birthday.)
   2.334 +                var current_tr = yearElem("current");
   2.335 +                loop(remainderWeeks, function(i) {
   2.336 +                    var e = weekElem("partial");
   2.337 +                    addWeekMouseEvents(e, i);
   2.338 +                    $(current_tr).append(e);
   2.339 +                })
   2.340 +                var e = weekElem("today");
   2.341 +                addWeekMouseEvents(e, remainderWeeks);
   2.342 +                $(current_tr).append(e);
   2.343 +                loop(restOfYearWeeks, function(i) {
   2.344 +                    var e = weekElem("future");
   2.345 +                    addWeekMouseEvents(e, remainderWeeks + 1 + i);
   2.346 +                    $(current_tr).append(e);
   2.347 +                })
   2.348 +                $("#calendar").append(current_tr);
   2.349 +
   2.350 +                // Fill in future years
   2.351 +                loop(futureYears, function() {
   2.352 +                    var tr = yearElem("future");
   2.353 +                    $(tr).append(yearWeekElems());
   2.354 +                    $("#calendar").append(tr);
   2.355 +                })
   2.356 +            });
   2.357 +        </script>
   2.358 +    </head>
   2.359 +    <body>
   2.360 +        <div id="welcome">
   2.361 +            <h1>Life Calendar</h1>
   2.362 +            <p>
   2.363 +                A minimalist life calendar. Shows the number of weeks you've lived and the number of weeks you
   2.364 +                have left.
   2.365 +            </p>
   2.366 +            <form method="get" action="" id="settingsForm">
   2.367 +                <p>
   2.368 +                    <label for="birthday">Birthday:
   2.369 +                        <input class="settings" id="birthday" name="birthday" placeholder="1985-01-01"/>
   2.370 +                    </label>
   2.371 +                </p>
   2.372 +                <p>
   2.373 +                    <label for="lifespan">Life expectancy:
   2.374 +                        <input class="settings" id="lifespan" name="lifespan" placeholder="90"/>
   2.375 +                    </label>
   2.376 +                </p>
   2.377 +                <p>
   2.378 +                    <input type="submit" id="submit" value="Show calendar" />
   2.379 +                </p>
   2.380 +            </form>
   2.381 +            <div id="footer">
   2.382 +                <p>
   2.383 +                    Based on <a href="http://waitbutwhy.com/2014/05/life-weeks.html">Your Life in Weeks</a>
   2.384 +                    and <a href="http://count.life">count.life</a>.
   2.385 +                </p>
   2.386 +            </div>
   2.387 +        </div>
   2.388 +    </body>
   2.389 +</html>
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/life_calendar/tooltip.js	Fri Jun 17 22:24:17 2016 -0700
     3.3 @@ -0,0 +1,58 @@
     3.4 +// Based on http://matthias-schuetz.js.org/tooltip.js/
     3.5 +
     3.6 +var _tt_options = {
     3.7 +	tooltipId: "tooltip",
     3.8 +	offsetDefault: 15
     3.9 +};
    3.10 +
    3.11 +function getTooltipElm() {
    3.12 +	return document.querySelector("#" + _tt_options.tooltipId);
    3.13 +}
    3.14 +
    3.15 +function adjustTooltip(evt, tooltipElm, title, text) {
    3.16 +	var offset = _tt_options.offsetDefault;
    3.17 +	var scrollY = window.scrollY || window.pageYOffset;
    3.18 +	var scrollX = window.scrollX || window.pageXOffset;
    3.19 +	var tooltipTop = evt.pageY + offset;
    3.20 +	var tooltipLeft = evt.pageX + offset;
    3.21 +
    3.22 +	tooltipTop = (tooltipTop - scrollY + tooltipElm.offsetHeight + 20 >= window.innerHeight ? (tooltipTop - tooltipElm.offsetHeight - 20) : tooltipTop);
    3.23 +	tooltipLeft = (tooltipLeft - scrollX + tooltipElm.offsetWidth + 20 >= window.innerWidth ? (tooltipLeft - tooltipElm.offsetWidth - 20) : tooltipLeft);
    3.24 +
    3.25 +	tooltipElm.style.top = tooltipTop + "px";
    3.26 +	tooltipElm.style.left = tooltipLeft + "px";
    3.27 +
    3.28 +	setTooltipText(tooltipElm, title, text);
    3.29 +}
    3.30 +
    3.31 +function removeTooltip() {
    3.32 +	document.querySelector("body").removeChild(getTooltipElm());
    3.33 +}
    3.34 +
    3.35 +function createTooltip(evt, title, text) {
    3.36 +	var tooltipElm = getTooltipElm();
    3.37 +
    3.38 +	if (!tooltipElm) {
    3.39 +		tooltipElm = document.createElement("div");
    3.40 +		tooltipElm.appendChild(document.createElement("h1"));
    3.41 +		tooltipElm.appendChild(document.createElement("h2"));
    3.42 +
    3.43 +		tooltipElm.style.position = "absolute";
    3.44 +		tooltipElm.setAttribute("id", _tt_options.tooltipId);
    3.45 +
    3.46 +		document.querySelector("body").appendChild(tooltipElm);
    3.47 +	}
    3.48 +
    3.49 +	adjustTooltip(evt, tooltipElm, title, text);
    3.50 +}
    3.51 +
    3.52 +function setTooltipText(tooltipElm, title, text) {
    3.53 +	var eTitle = tooltipElm.children[0];
    3.54 +	var eText = tooltipElm.children[1];
    3.55 +	if (eTitle && title) {
    3.56 +		eTitle.textContent = title;
    3.57 +	}
    3.58 +	if (eText && text) {
    3.59 +		eText.textContent = text;
    3.60 +	}
    3.61 +}