diff --git a/tools/jslint/check-all.js b/tools/jslint/check-all.js index 54ac40594b..ceb871257c 100644 --- a/tools/jslint/check-all.js +++ b/tools/jslint/check-all.js @@ -114,6 +114,10 @@ var exceptions = { "Unexpected 'else' after 'return'." : function () { return true; + }, + + "Don't make functions within a loop." : function () { + return true; } }; diff --git a/zephyr/static/js/timerender.js b/zephyr/static/js/timerender.js index 67a8b5a9a9..c9f13bc27b 100644 --- a/zephyr/static/js/timerender.js +++ b/zephyr/static/js/timerender.js @@ -2,16 +2,123 @@ var timerender = (function () { var exports = {}; +// If this is 5, then times from up to 5 days before the current +// day will be formatted as weekday names: +// +// 1/10 1/11 1/12 +// Sun Mon Tue Wed Thu 1/18 1/19 +// ^ today +var MAX_AGE_FOR_WEEKDAY = 5; + +var next_timerender_id = 0; + +var set_to_start_of_day = function (time) { + return time.setMilliseconds(0).setSeconds(0).setMinutes(0).setHours(0); +}; + +function now() { return (new XDate()); } + +// Given an XDate object 'time', return a two-element list containing +// - a string for the current human-formatted version +// - a string like "2013-01-20" representing the day the format +// needs to change, or null if it will never need to change. +function render_now(time) { + var start_of_today = set_to_start_of_day(now()); + var start_of_other_day = set_to_start_of_day(time.clone()); + + // How many days old is 'time'? 0 = today, 1 = yesterday, 7 = a + // week ago, -1 = tomorrow, etc. + + // Presumably the result of diffDays will be an integer in this + // case, but round it to be sure before comparing to an integer + // constant. + var days_old = Math.round(start_of_other_day.diffDays(start_of_today)); + + if (days_old >= 0 && days_old <= MAX_AGE_FOR_WEEKDAY) + // "\xa0" is U+00A0 NO-BREAK SPACE. + // Can't use   as that represents the literal string " ". + return [time.toString("ddd") + "\xa0" + time.toString("HH:mm"), + start_of_other_day.addDays(MAX_AGE_FOR_WEEKDAY+1) + .toString("yyyy-MM-dd")]; + else { + // For now, if we get a message from tomorrow, we don't bother + // rewriting the timestamp when it gets to be tomorrow. + return [time.toString("MMM dd") + "\xa0\xa0" + time.toString("HH:mm"), + undefined]; + } +} + +// This table associates to each day (represented as a yyyy-MM-dd +// string) a list of timestamps that need to be updated on that day. +// Each timestamp is represented as a list of length 2: +// [id of the span element, XDate representing the time] + +// This is an efficient data structure because we only need to update +// timestamps at the start of each day. If timestamp update times were +// arbitrary, a priority queue would be more sensible. +var update_table = {}; + +// The day that elements are currently up-to-date with respect to. +// Represented as an XDate with hour, minute, second, millisecond 0. +var last_updated = set_to_start_of_day(now()); + +function maybe_add_update_table_entry(update_date, id, time) { + if (update_date === undefined) + return; + if (update_table[update_date] === undefined) + update_table[update_date] = []; + update_table[update_date].push([id, time]); +} + // Given an XDate object 'time', return a DOM node that initially // displays the human-formatted time, and is updated automatically as // necessary (e.g. changing "Mon 11:21" to "Jan 14 11:21" after a week // or so). -// (But for now it just returns a static node.) -exports.render_time = function(time) { - // Wrap the text in a span because (oddly) a text node has no outerHTML attribute. - return $("").text(time.toString("MMM dd") + "\xa0\xa0" + time.toString("HH:mm")); +// (What's actually spliced into the message template is the contents +// of this DOM node as HTML, so effectively a copy of the node. That's +// okay since to update the time later we look up the node by its id.) +exports.render_time = function (time) { + var id = "timerender" + next_timerender_id; + next_timerender_id++; + var rendered_now = render_now(time); + var node = $("").attr('id', id).text(rendered_now[0]); + maybe_add_update_table_entry(rendered_now[1], id, time); + return node; }; +setInterval(function () { + var start_of_today = set_to_start_of_day(now()); + var new_date; + // This loop won't do anything unless the day changed since the + // last time it ran. + for (new_date = last_updated.clone().addDays(1); + new_date <= start_of_today; + new_date.addDays(1)) { + var update_date = new_date.toString("yyyy-MM-dd"); + if (update_table[update_date] !== undefined) + { + var to_process = update_table[update_date]; + var i; + update_table[update_date] = []; + $.each(to_process, function (idx, elem) { + var id = elem[0]; + var element = document.getElementById(id); + // The element might not exist any more (because it + // was in the zfilt table, or because we added + // messages above it and re-collapsed). + if (element !== null) { + var time = elem[1]; + var new_rendered = render_now(time); + $(document.getElementById(id)).text(new_rendered[0]); + + maybe_add_update_table_entry(new_rendered[1], id, time); + } + }); + } + } + last_updated = start_of_today; +}, 60 * 1000); + return exports; }());