New version of the timezone support for agenda. Now the ongoing

bars work. This also moves the javascript from the agenda.html
to separate timezone.js file. This commit does not include the
moment and moment-timezone javascript libraries that are
needed to get this working, they need to be added to
ietf/externals/static separately.
 - Legacy-Id: 18689
This commit is contained in:
Tero Kivinen 2020-11-12 22:15:23 +00:00
parent 0bf56c93c7
commit f18fe23ea5
6 changed files with 407 additions and 18 deletions

View file

@ -238,6 +238,9 @@ def preprocess_assignments_for_agenda(assignments_queryset, meeting, extra_prefe
d.get_href(meeting=meeting)
d.get_versionless_href(meeting=meeting)
a.start_timestamp = int(a.timeslot.utc_start_time().timestamp())
a.end_timestamp = int(a.timeslot.utc_end_time().timestamp())
return assignments
def tag_assignments_with_filter_keywords(assignments):

View file

@ -1459,6 +1459,7 @@ def agenda(request, num=None, name=None, base=None, ext=None, owner=None, utc=""
"filter_categories": filter_categories,
"non_area_keywords": [label.lower() for label in non_area_labels],
"now": datetime.datetime.now(),
"timezone": meeting.time_zone,
"is_current_meeting": is_current_meeting,
"use_codimd": True if meeting.date>=settings.MEETING_USES_CODIMD_DATE else False,
"cache_time": 150 if is_current_meeting else 3600,
@ -3350,6 +3351,16 @@ def upcoming(request):
entries.extend(list(interim_sessions))
entries.sort(key = lambda o: pytz.utc.localize(datetime.datetime.combine(o.date, datetime.datetime.min.time())) if isinstance(o,Meeting) else o.official_timeslotassignment().timeslot.utc_start_time())
for o in entries:
if isinstance(o, Meeting):
o.start_timestamp = int(pytz.utc.localize(datetime.datetime.combine(o.date, datetime.datetime.min.time())).timestamp())
o.end_timestamp = int(pytz.utc.localize(datetime.datetime.combine(o.end, datetime.datetime.max.time())).timestamp())
else:
o.start_timestamp = int(o.official_timeslotassignment().
timeslot.utc_start_time().timestamp());
o.end_timestamp = int(o.official_timeslotassignment().
timeslot.utc_end_time().timestamp());
# add menu entries
menu_entries = get_interim_menu_entries(request)
selected_menu_entry = 'upcoming'

View file

@ -1460,3 +1460,63 @@ a.fc-event, .fc-event, .fc-content, .fc-title, .fc-event-container {
width: 10em;
}
.rightmarker, .leftmarker {
width: 3px;
padding-right: 0px !important;
padding-left: 0px !important;
}
.ongoing > td:first-child {
background-color: red !important;
}
.ongoing > td:last-child {
background-color: red !important;
}
.timetooltip {
position: relative;
}
.timetooltip .timetooltiptext {
visibility: hidden;
background-color: #eee;
color: #000;
text-align: left;
border-radius: 6px;
padding: 5px 5px;
position: absolute;
z-index: 110;
bottom: 125%;
left: 50%;
margin-left: -60px;
opacity: 0;
transition: opacity 0.3s;
width: 60em;
}
.reschedtimetooltip .timetooltiptext {
margin-left: -300px;
}
.timetooltiptext table tr td {
padding: 1px 5px;
}
.timetooltiptext table tr th {
text-align: center;
}
.timehead {
text-align: right;
font-weight: bold;
}
.timetooltip:hover .timetooltiptext {
visibility: visible;
opacity: 1;
}
#current-time {
display: inline-block;
}

View file

@ -0,0 +1,255 @@
// Initialize moments
function initialize_moments() {
var times=$('span.time')
$.each(times, function(i, item) {
item.start_ts = moment.unix(this.getAttribute("data-start-time")).utc();
item.end_ts = moment.unix(this.getAttribute("data-end-time")).utc();
if (this.hasAttribute("weekday")) {
item.format=2;
} else {
item.format=1;
}
if (this.hasAttribute("format")) {
item.format = +this.getAttribute("format");
}
});
var times=$('[data-slot-start-ts]')
$.each(times, function(i, item) {
item.slot_start_ts = moment.unix(this.getAttribute("data-slot-start-ts")).utc();
item.slot_end_ts = moment.unix(this.getAttribute("data-slot-end-ts")).utc();
});
}
// Initialize timezone system
function timezone_init(current) {
var tz_names = moment.tz.names();
var select = $('#timezone_select');
$.each(tz_names, function(i, item) {
if (current == item) {
select.append($('<option/>', {
selected: "selected", html: item, value: item }));
} else {
select.append($('<option/>', {
html: item, value: item }));
}
});
initialize_moments();
select.change(function () {
update_times(this.value);
});
update_times(current);
add_tooltips();
}
// Select which timezone is used, 0 = meeting, 1 = browser local, 2 = UTC
function use_timezone (val) {
switch (val) {
case 0:
tz = meeting_timezone;
break;
case 1:
tz = local_timezone;
break;
default:
tz = 'UTC';
break;
}
$('#timezone_select').val(tz);
update_times(tz);
}
// Format time for item for timezone. Depending on the fmt
// use different formats.
// Formats: 0 = long format "Saturday, October 24th 2020, 13:52 +00:00 UTC"
// 1 = Short format "13:52", "13:52 (-1)", or "13:52 (+1)"
// 2 = Short format with weekday, "Friday, 13:52 (-1)"
// 3 = Date only "2020-10-24"
// 4 = Date and time "2020-10-24 13:52"
// 5 = Time only "13:52".
function format_time(t, tz, fmt) {
var out;
var mtz = meeting_timezone;
if (mtz == "") {
mtz = "UTC";
}
switch (fmt) {
case 0:
out = t.tz(tz).format('dddd, ') + '<span class="hidden-xs">' +
t.tz(tz).format('MMMM Do YYYY, ') + '</span>' +
t.tz(tz).format('HH:mm') + '<span class="hidden-xs">' +
t.tz(tz).format(' Z z') + '</span>';
break;
case 1:
// Note, this code does not work if the meeting crosses the
// year boundary.
out = t.tz(tz).format("HH:mm");
if (+t.tz(tz).dayOfYear() < +t.tz(mtz).dayOfYear()) {
out = out + " (-1)";
} else if (+t.tz(tz).dayOfYear() > +t.tz(mtz).dayOfYear()) {
out = out + " (+1)";
}
break;
case 2:
out = t.tz(mtz).format("dddd, ").toUpperCase() +
t.tz(tz).format("HH:mm");
if (+t.tz(tz).dayOfYear() < +t.tz(mtz).dayOfYear()) {
out = out + " (-1)";
} else if (+t.tz(tz).dayOfYear() > +t.tz(mtz).dayOfYear()) {
out = out + " (+1)";
}
break;
case 3:
out = t.utc().format("YYYY-MM-DD");
break;
case 4:
out = t.tz(tz).format("YYYY-MM-DD HH:mm");
break;
case 5:
out = t.tz(tz).format("HH:mm");
break;
}
return out;
}
// Format tooltip notice
function format_tooltip_notice(start, end) {
var notice = "";
if (end.isBefore()) {
notice = "Event ended " + end.fromNow();
} else if (start.isAfter()) {
notice = "Event will start " + start.fromNow();
} else {
notice = "Event started " + start.fromNow() + " and will end " +
end.fromNow();
}
return '<span class="tooltipnotice">' + notice + '</span>';
}
// Format tooltip table
function format_tooltip_table(start, end) {
var out = '<table><tr><th>Timezone</th><th>Start</th><th>End</th></tr>';
if (meeting_timezone != "") {
out += '<tr><td class="timehead">Meeting timezone:</td><td>' +
format_time(start, meeting_timezone, 0) + '</td><td>' +
format_time(end, meeting_timezone, 0) + '</td></tr>';
}
out += '<tr><td class="timehead">Local timezone:</td><td>' +
format_time(start, local_timezone, 0) + '</td><td>' +
format_time(end, local_timezone, 0) + '</td></tr>';
if (current_timezone != 'UTC') {
out += '<tr><td class="timehead">Selected Timezone:</td><td>' +
format_time(start, current_timezone, 0) + '</td><td>' +
format_time(end, current_timezone, 0) + '</td></tr>';
}
out += '<tr><td class="timehead">UTC:</td><td>' +
format_time(start, 'UTC', 0) + '</td><td>' +
format_time(end, 'UTC', 0) + '</td></tr>';
out += '</table>' + format_tooltip_notice(start, end);
return out;
}
// Format tooltip for item
function format_tooltip(start, end) {
return '<span class="timetooltiptext">' +
format_tooltip_table(start, end) +
'</span>';
}
// Add tooltips
function add_tooltips() {
$('span.time').each(function () {
var tooltip = $(format_tooltip(this.start_ts, this.end_ts));
tooltip[0].start_ts = this.start_ts;
tooltip[0].end_ts = this.end_ts;
tooltip[0].ustart_ts = moment(this.start_ts).add(-2, 'hours');
tooltip[0].uend_ts = moment(this.end_ts).add(2, 'hours');
$(this).parent().append(tooltip);
});
}
// Update times on the agenda based on the selected timezone
function update_times(newtz) {
current_timezone = newtz;
$('#title-timezone').html(newtz);
$('span.time').each(function () {
if (this.format == 4) {
var tz = this.start_ts.tz(newtz).format(" z");
if (this.start_ts.tz(newtz).dayOfYear() ==
this.end_ts.tz(newtz).dayOfYear()) {
$(this).html(format_time(this.start_ts, newtz, this.format) +
'-' + format_time(this.end_ts, newtz, 5) + tz);
} else {
$(this).html(format_time(this.start_ts, newtz, this.format) +
'-' +
format_time(this.end_ts, newtz, this.format) + tz);
}
} else {
$(this).html(format_time(this.start_ts, newtz, this.format) + '-' +
format_time(this.end_ts, newtz, this.format));
}
});
update_tooltips_all();
update_clock();
// update_calendar(agenda_filter.get_filter())
}
// Highlight ongoing based on the current time
function highlight_ongoing() {
$("div#now").remove("#now");
$('.ongoing').removeClass("ongoing");
var agenda_rows=$('[data-slot-start-ts]')
agenda_rows = agenda_rows.filter(function() {
return moment().isBetween(this.slot_start_ts, this.slot_end_ts);
});
agenda_rows.addClass("ongoing");
agenda_rows.first().children("th, td").
prepend($('<div id="now" class="anchor-target"></div>'));
}
// Update tooltips
function update_tooltips() {
var tooltips=$('.timetooltiptext');
tooltips.filter(function() {
return moment().isBetween(this.ustart_ts, this.uend_ts);
}).each(function () {
$(this).html(format_tooltip_table(this.start_ts, this.end_ts));
});
}
// Update all tooltips
function update_tooltips_all() {
var tooltips=$('.timetooltiptext');
tooltips.each(function () {
$(this).html(format_tooltip_table(this.start_ts, this.end_ts));
});
}
// Update clock
function update_clock() {
$('#current-time').html(format_time(moment(), current_timezone, 0));
}
$.urlParam = function(name) {
var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(window.location.href);
if (results == null) {
return null;
} else {
return results[1] || 0;
}
}
function init_timers() {
var fast_timer = 60000 / (speedup > 600 ? 600 : speedup);
update_clock();
highlight_ongoing();
setInterval(function() { update_clock(); }, fast_timer);
setInterval(function() { highlight_ongoing(); }, fast_timer);
setInterval(function() { update_tooltips(); }, fast_timer);
setInterval(function() { update_tooltips_all(); }, 3600000 / speedup);
}

View file

@ -50,7 +50,7 @@
{# cache this part -- it takes 3-6 seconds to generate #}
{% load cache %}
{% cache cache_time ietf_meeting_agenda_utc schedule.meeting.number request.path %}
<h1>Agenda</h1>
<h1>Agenda <small id="title-timezone" class="pull-right" >{{timezone}}</small></h1>
{% if is_current_meeting %}
<p class="alert alert-info">
@ -97,23 +97,22 @@
{% for item in filtered_assignments %}
{% ifchanged item.timeslot.time|date:"Y-m-d" %}
<tr><th class="gap" colspan="5"></th></tr>
<tr><th class="gap" colspan="7"></th></tr>
<tr class="warning">
<th colspan="5">
<th colspan="7">
{# The anchor here needs to be in a div, not in the th, in order for the anchor-target margin hack to work #}
<div class="anchor-target" id="{{item.timeslot.time|slugify}}"></div>
{% if "-utc" in request.path %}
{{ item.timeslot.utc_start_time|date:"l, F j, Y" }} (UTC)
{% else %}
{{ item.timeslot.time|date:"l, F j, Y" }} ({{item.timeslot.tzname}})
{% endif %}
{{ item.timeslot.time|date:"l, F j, Y" }}
</th>
</tr>
{% endifchanged %}
{% if item.timeslot.type_id == 'regular' %}
{% ifchanged %}
<tr class="info">
<tr class="info"
data-slot-start-ts="{{item.start_timestamp}}"
data-slot-end-ts="{{item.end_timestamp}}">
<td class="leftmarker"></td>
<th class="text-nowrap text-right">
<span class="hidden-xs">
{% include "meeting/timeslot_start_end.html" %}
@ -123,19 +122,19 @@
<span class="hidden-sm hidden-md hidden-lg">
{% include "meeting/timeslot_start_end.html" %}
</span>
{% if "-utc" in request.path %}
{{ item.timeslot.utc_start_time|date:"l"}}
{% else %}
{{ item.timeslot.time|date:"l"}}
{% endif %}
{{ item.timeslot.time|date:"l"}}
{{item.timeslot.name|capfirst_allcaps}}
</th>
<td class="rightmarker"></td>
</tr>
{% endifchanged %}
{% endif %}
{% if item.timeslot.type.slug == 'break' or item.timeslot.type.slug == 'reg' or item.timeslot.type.slug == 'other' %}
<tr id="row-{{ item.slug }}" data-filter-keywords="{{ item.filter_keywords|join:',' }}">
<tr id="row-{{ item.slug }}" data-filter-keywords="{{ item.filter_keywords|join:',' }}"
data-slot-start-ts="{{item.start_timestamp}}"
data-slot-end-ts="{{item.end_timestamp}}">
<td class="leftmarker"></td>
<td class="text-nowrap text-right">
<span class="hidden-xs">
{% include "meeting/timeslot_start_end.html" %}
@ -189,6 +188,7 @@
</div>
{% endif %}
</td>
<td class="rightmarker"></td>
</tr>
{% endif %}
@ -196,7 +196,10 @@
{% if item.session.historic_group %}
<tr id="row-{{item.slug}}"
{% if item.timeslot.type.slug == 'plenary' %}class="{{item.timeslot.type.slug}}danger"{% endif %}
data-filter-keywords="{{ item.filter_keywords|join:',' }}">
data-filter-keywords="{{ item.filter_keywords|join:',' }}"
data-slot-start-ts="{{item.start_timestamp}}"
data-slot-end-ts="{{item.end_timestamp}}">
<td class="leftmarker"></td>
{% if item.timeslot.type.slug == 'plenary' %}
<th class="text-nowrap text-right">
<span class="hidden-xs">
@ -282,11 +285,13 @@
RESCHEDULED
{% if item.session.rescheduled_to %}
TO
<span class="timetooltip reschedtimetooltip"><span class="time" data-start-time="{{item.session.rescheduled_to.utc_start_time|date:"U"}}" data-end-time="{{item.session.rescheduled_to.utc_end_time|date:"U"}}" {% if item.timeslot.time|date:"l" != item.session.rescheduled_to.time|date:"l" %} weekday="1"{% endif %}>
{% if "-utc" in request.path %}
{{ item.session.rescheduled_to.utc_start_time|date:"l G:i"|upper }}-{{ item.session.rescheduled_to.utc_end_time|date:"G:i" }}
{% else %}
{{ item.session.rescheduled_to.time|date:"l G:i"|upper }}-{{ item.session.rescheduled_to.end_time|date:"G:i" }}
{% endif %}
</span></span>
{% endif %}
</span>
{% endif %}
@ -297,6 +302,7 @@
<br><span class="text-danger">{{item.session.agenda_note}}</span>
{% endif %}
</td>
<td class="rightmarker"></td>
</tr>
{% endif %}
{% endif %}
@ -306,11 +312,25 @@
</div>
<div class="col-md-2 hidden-print bs-docs-sidebar" id="affix">
<ul class="nav nav-pills nav-stacked small" data-spy="affix">
<li><a href="#now">Now</a></li>
{% for item in filtered_assignments %}
{% ifchanged item.timeslot.time|date:"Y-m-d" %}
<li><a href="#{{item.timeslot.time|slugify}}">{{ item.timeslot.time|date:"l, F j, Y" }}</a></li>
{% endifchanged %}
{% endfor %}
<li><hr/></li>
<li class="tzselect">Select timezone:</li>
<li><a id="meeting-timezone" onclick="use_timezone(0)">Meeting Timezone</a></li>
<li><a id="local-timezone" onclick="use_timezone(1)">Local Timezone</a></li>
<li><a id="utc-timezone" onclick="use_timezone(2)">UTC</a></li>
<li id="timezone">
<select style="width: 12em;" id="timezone_select"></select>
</li>
{% if settings.DEBUG %}
<li><hr/></li>
<li><span id="current-time"></span></li>
{% endif %}
</ul>
</div>
</div>
@ -462,5 +482,43 @@
retrieve_session_modal($(this).find(".session-materials"));
});
</script>
<script src="{% static 'moment/min/moment.min.js' %}"></script>
<script src="{% static 'moment-timezone/builds/moment-timezone-with-data-10-year-range.min.js' %}"></script>
<script src="{% static 'ietf/js/agenda/timezone.js' %}"></script>
<script>
{% if settings.DEBUG %}
speedup = +$.urlParam('speedup');
if (speedup < 1) {
speedup = 1;
}
start_time = moment().utc();
if ($.urlParam('date')) {
offset_time = moment.tz(decodeURIComponent($.urlParam('date')), "UTC");
} else {
offset_time = start_time;
}
if (speedup > 1 || offset_time != start_time) {
moment.now = function () {
return (+new Date() - start_time) * speedup + offset_time;
}
}
{% else %}
speedup = 1;
{% endif %}
// Get meeting and local times, initialize timezone system
meeting_timezone = "{{timezone}}";
local_timezone = moment.tz.guess();
current_timezone = 'UTC';
{% if "-utc" in request.path %}
timezone_init('UTC');
{% else %}
timezone_init(meeting_timezone);
{% endif %}
init_timers();
</script>
{% endblock %}

View file

@ -1,5 +1,7 @@
<span class="timetooltip"><span class="time" data-start-time="{{item.start_timestamp}}" data-end-time="{{item.end_timestamp}}">
{% if "-utc" in request.path %}
{{item.timeslot.utc_start_time|date:"G:i"}}-{{item.timeslot.utc_end_time|date:"G:i"}}
{{item.timeslot.utc_start_time|date:"H:i"}}-{{item.timeslot.utc_end_time|date:"H:i"}}
{% else %}
{{item.timeslot.time|date:"G:i"}}-{{item.timeslot.end_time|date:"G:i"}}
{{item.timeslot.time|date:"H:i"}}-{{item.timeslot.end_time|date:"H:i"}}
{% endif %}
</span></span>