Add timezone selector to upcoming meetings page. Separate general timezone handling from parts only relevant to main agenda page. Speed up agenda timezone javascript tests. Fixes #3184. Commit ready for merge.
- Legacy-Id: 18970
This commit is contained in:
parent
b08110b838
commit
a5604992f2
|
@ -13,7 +13,8 @@ import django
|
|||
#from django.urls import reverse as urlreverse
|
||||
from django.utils.text import slugify
|
||||
from django.db.models import F
|
||||
from pytz import timezone
|
||||
import pytz
|
||||
|
||||
#from django.test.utils import override_settings
|
||||
|
||||
import debug # pyflakes:ignore
|
||||
|
@ -37,7 +38,7 @@ from ietf import settings
|
|||
if selenium_enabled():
|
||||
from selenium.webdriver.common.action_chains import ActionChains
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.support.ui import Select, WebDriverWait
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
from selenium.webdriver.support import expected_conditions
|
||||
from selenium.common.exceptions import NoSuchElementException
|
||||
|
||||
|
@ -957,25 +958,22 @@ class AgendaTests(IetfSeleniumTestCase):
|
|||
with self.assertRaises(NoSuchElementException):
|
||||
self.driver.find_element_by_xpath('//a[text()="%s"]' % slide.title)
|
||||
|
||||
def _wait_for_tz_change_from(self, old_tz):
|
||||
"""Helper to wait for tz displays to change from their old value"""
|
||||
match = 'text()!="%s"' % old_tz
|
||||
WebDriverWait(self.driver, 2).until(
|
||||
expected_conditions.presence_of_element_located((By.XPATH, '//*[@class="current-tz"][%s]' % match))
|
||||
)
|
||||
|
||||
def test_agenda_time_zone_selection(self):
|
||||
self.assertNotEqual(self.meeting.time_zone, 'UTC', 'Meeting time zone must not be UTC')
|
||||
|
||||
wait = WebDriverWait(self.driver, 2)
|
||||
self.driver.get(self.absreverse('ietf.meeting.views.agenda'))
|
||||
|
||||
# wait for the select box to be updated - look for an arbitrary time zone to be in
|
||||
# its options list to detect this
|
||||
WebDriverWait(self.driver, 2).until(
|
||||
expected_conditions.presence_of_element_located((By.XPATH, '//option[@value="America/Halifax"]'))
|
||||
arbitrary_tz = 'America/Halifax'
|
||||
arbitrary_tz_opt = WebDriverWait(self.driver, 2).until(
|
||||
expected_conditions.presence_of_element_located(
|
||||
(By.CSS_SELECTOR, '#timezone-select > option[value="%s"]' % arbitrary_tz)
|
||||
)
|
||||
)
|
||||
|
||||
tz_select_input = Select(self.driver.find_element_by_id('timezone_select'))
|
||||
tz_select_input = self.driver.find_element_by_id('timezone-select')
|
||||
meeting_tz_link = self.driver.find_element_by_id('meeting-timezone')
|
||||
local_tz_link = self.driver.find_element_by_id('local-timezone')
|
||||
utc_tz_link = self.driver.find_element_by_id('utc-timezone')
|
||||
|
@ -991,73 +989,110 @@ class AgendaTests(IetfSeleniumTestCase):
|
|||
self.assertNotEqual(self.meeting.time_zone, local_tz, 'Meeting time zone must not be local time zone')
|
||||
self.assertNotEqual(local_tz, 'UTC', 'Local time zone must not be UTC')
|
||||
|
||||
meeting_tz_opt = tz_select_input.find_element_by_css_selector('option[value="%s"]' % self.meeting.time_zone)
|
||||
local_tz_opt = tz_select_input.find_element_by_css_selector('option[value="%s"]' % local_tz)
|
||||
utc_tz_opt = tz_select_input.find_element_by_css_selector('option[value="UTC"]')
|
||||
|
||||
# Should start off in meeting time zone
|
||||
self.assertEqual(tz_select_input.first_selected_option.get_attribute('value'), self.meeting.time_zone)
|
||||
self.assertTrue(meeting_tz_opt.is_selected())
|
||||
# don't yet know local_tz, so can't check that it's deselected here
|
||||
self.assertFalse(arbitrary_tz_opt.is_selected())
|
||||
self.assertFalse(utc_tz_opt.is_selected())
|
||||
for disp in tz_displays:
|
||||
self.assertEqual(disp.text.strip(), self.meeting.time_zone)
|
||||
|
||||
# Click 'local' button
|
||||
local_tz_link.click()
|
||||
self._wait_for_tz_change_from(self.meeting.time_zone)
|
||||
self.assertEqual(tz_select_input.first_selected_option.get_attribute('value'), local_tz)
|
||||
wait.until(expected_conditions.element_selection_state_to_be(meeting_tz_opt, False))
|
||||
self.assertFalse(meeting_tz_opt.is_selected())
|
||||
# just identified the local_tz_opt as being selected, so no check here, either
|
||||
self.assertFalse(arbitrary_tz_opt.is_selected())
|
||||
self.assertFalse(utc_tz_opt.is_selected())
|
||||
for disp in tz_displays:
|
||||
self.assertEqual(disp.text.strip(), local_tz)
|
||||
|
||||
# click 'utc' button
|
||||
utc_tz_link.click()
|
||||
self._wait_for_tz_change_from(local_tz)
|
||||
self.assertEqual(tz_select_input.first_selected_option.get_attribute('value'), 'UTC')
|
||||
wait.until(expected_conditions.element_to_be_selected(utc_tz_opt))
|
||||
self.assertFalse(meeting_tz_opt.is_selected())
|
||||
self.assertFalse(local_tz_opt.is_selected()) # finally!
|
||||
self.assertFalse(arbitrary_tz_opt.is_selected())
|
||||
self.assertTrue(utc_tz_opt.is_selected())
|
||||
for disp in tz_displays:
|
||||
self.assertEqual(disp.text.strip(), 'UTC')
|
||||
|
||||
# click back to meeting
|
||||
meeting_tz_link.click()
|
||||
self._wait_for_tz_change_from('UTC')
|
||||
self.assertEqual(tz_select_input.first_selected_option.get_attribute('value'), self.meeting.time_zone)
|
||||
wait.until(expected_conditions.element_to_be_selected(meeting_tz_opt))
|
||||
self.assertTrue(meeting_tz_opt.is_selected())
|
||||
self.assertFalse(local_tz_opt.is_selected())
|
||||
self.assertFalse(arbitrary_tz_opt.is_selected())
|
||||
self.assertFalse(utc_tz_opt.is_selected())
|
||||
for disp in tz_displays:
|
||||
self.assertEqual(disp.text.strip(), self.meeting.time_zone)
|
||||
|
||||
# and then back to UTC...
|
||||
utc_tz_link.click()
|
||||
self._wait_for_tz_change_from(self.meeting.time_zone)
|
||||
self.assertEqual(tz_select_input.first_selected_option.get_attribute('value'), 'UTC')
|
||||
wait.until(expected_conditions.element_to_be_selected(utc_tz_opt))
|
||||
self.assertFalse(meeting_tz_opt.is_selected())
|
||||
self.assertFalse(local_tz_opt.is_selected())
|
||||
self.assertFalse(arbitrary_tz_opt.is_selected())
|
||||
self.assertTrue(utc_tz_opt.is_selected())
|
||||
for disp in tz_displays:
|
||||
self.assertEqual(disp.text.strip(), 'UTC')
|
||||
|
||||
# ... and test the switch from UTC to local
|
||||
local_tz_link.click()
|
||||
self._wait_for_tz_change_from('UTC')
|
||||
self.assertEqual(tz_select_input.first_selected_option.get_attribute('value'), local_tz)
|
||||
wait.until(expected_conditions.element_to_be_selected(local_tz_opt))
|
||||
self.assertFalse(meeting_tz_opt.is_selected())
|
||||
self.assertTrue(local_tz_opt.is_selected())
|
||||
self.assertFalse(arbitrary_tz_opt.is_selected())
|
||||
self.assertFalse(utc_tz_opt.is_selected())
|
||||
for disp in tz_displays:
|
||||
self.assertEqual(disp.text.strip(), local_tz)
|
||||
|
||||
# Now select a different item from the select input
|
||||
tz_select_input.select_by_value('America/Halifax')
|
||||
self._wait_for_tz_change_from(self.meeting.time_zone)
|
||||
self.assertEqual(tz_select_input.first_selected_option.get_attribute('value'), 'America/Halifax')
|
||||
arbitrary_tz_opt.click()
|
||||
wait.until(expected_conditions.element_to_be_selected(arbitrary_tz_opt))
|
||||
self.assertFalse(meeting_tz_opt.is_selected())
|
||||
self.assertFalse(local_tz_opt.is_selected())
|
||||
self.assertTrue(arbitrary_tz_opt.is_selected())
|
||||
self.assertFalse(utc_tz_opt.is_selected())
|
||||
for disp in tz_displays:
|
||||
self.assertEqual(disp.text.strip(), 'America/Halifax')
|
||||
|
||||
self.assertEqual(disp.text.strip(), arbitrary_tz)
|
||||
|
||||
def test_agenda_time_zone_selection_updates_weekview(self):
|
||||
"""Changing the time zone should update the weekview to match"""
|
||||
class in_iframe_href:
|
||||
"""Condition class for use with WebDriverWait"""
|
||||
def __init__(self, fragment, iframe):
|
||||
self.fragment = fragment
|
||||
self.iframe = iframe
|
||||
|
||||
def __call__(self, driver):
|
||||
driver.switch_to.frame(self.iframe)
|
||||
current_href= driver.execute_script(
|
||||
'return document.location.href'
|
||||
)
|
||||
driver.switch_to.default_content()
|
||||
return self.fragment in current_href
|
||||
|
||||
# enable a filter so the weekview iframe is visible
|
||||
self.driver.get(self.absreverse('ietf.meeting.views.agenda') + '?show=mars')
|
||||
# wait for the select box to be updated - look for an arbitrary time zone to be in
|
||||
# its options list to detect this
|
||||
WebDriverWait(self.driver, 2).until(
|
||||
expected_conditions.presence_of_element_located((By.XPATH, '//option[@value="America/Halifax"]'))
|
||||
wait = WebDriverWait(self.driver, 2)
|
||||
option = wait.until(
|
||||
expected_conditions.presence_of_element_located(
|
||||
(By.CSS_SELECTOR, '#timezone-select > option[value="America/Halifax"]'))
|
||||
)
|
||||
|
||||
tz_select_input = Select(self.driver.find_element_by_id('timezone_select'))
|
||||
|
||||
# Now select a different item from the select input
|
||||
tz_select_input.select_by_value('America/Halifax')
|
||||
self._wait_for_tz_change_from(self.meeting.time_zone)
|
||||
self.assertEqual(tz_select_input.first_selected_option.get_attribute('value'), 'America/Halifax')
|
||||
self.driver.switch_to.frame('weekview')
|
||||
wv_url = self.driver.execute_script('return document.location.href')
|
||||
self.assertIn('tz=america/halifax', wv_url)
|
||||
|
||||
option.click()
|
||||
try:
|
||||
wait.until(in_iframe_href('tz=america/halifax', 'weekview'))
|
||||
except:
|
||||
self.fail('iframe href not updated to contain selected time zone')
|
||||
|
||||
|
||||
@ifSeleniumEnabled
|
||||
class WeekviewTests(IetfSeleniumTestCase):
|
||||
|
@ -1099,7 +1134,7 @@ class WeekviewTests(IetfSeleniumTestCase):
|
|||
zones_to_test = ['utc', 'America/Halifax', 'Asia/Bangkok', 'Africa/Dakar', 'Europe/Dublin']
|
||||
self.login()
|
||||
for zone_name in zones_to_test:
|
||||
zone = timezone(zone_name)
|
||||
zone = pytz.timezone(zone_name)
|
||||
self.driver.get(self.absreverse('ietf.meeting.views.week_view') + '?tz=' + zone_name)
|
||||
for item in self.get_expected_items():
|
||||
if item.session.name:
|
||||
|
@ -1155,10 +1190,10 @@ class WeekviewTests(IetfSeleniumTestCase):
|
|||
|
||||
# Session during a single day in meeting local time but multi-day UTC
|
||||
# Compute a time that overlaps midnight, UTC, but won't when shifted to a local time zone
|
||||
start_time_utc = timezone('UTC').localize(
|
||||
start_time_utc = pytz.timezone('UTC').localize(
|
||||
datetime.datetime.combine(self.meeting.date, datetime.time(23,0))
|
||||
)
|
||||
start_time_local = start_time_utc.astimezone(timezone(self.meeting.time_zone))
|
||||
start_time_local = start_time_utc.astimezone(pytz.timezone(self.meeting.time_zone))
|
||||
|
||||
daytime_session = SessionFactory(
|
||||
meeting=self.meeting,
|
||||
|
@ -1551,6 +1586,152 @@ class InterimTests(IetfSeleniumTestCase):
|
|||
meetings.update(self.displayed_interims(groups=['mars']))
|
||||
self.do_upcoming_view_filter_test('?show=mars , ames &hide= ames', meetings)
|
||||
|
||||
def test_upcoming_view_time_zone_selection(self):
|
||||
wait = WebDriverWait(self.driver, 2)
|
||||
|
||||
def _assert_interim_tz_correct(sessions, tz):
|
||||
zone = pytz.timezone(tz)
|
||||
for session in sessions:
|
||||
ts = session.official_timeslotassignment().timeslot
|
||||
start = ts.utc_start_time().astimezone(zone).strftime('%Y-%m-%d %H:%M')
|
||||
end = ts.utc_end_time().astimezone(zone).strftime('%H:%M')
|
||||
meeting_link = self.driver.find_element_by_link_text(session.meeting.number)
|
||||
time_td = meeting_link.find_element_by_xpath('../../td[@class="session-time"]')
|
||||
self.assertIn('%s - %s' % (start, end), time_td.text)
|
||||
|
||||
def _assert_ietf_tz_correct(meetings, tz):
|
||||
zone = pytz.timezone(tz)
|
||||
for meeting in meetings:
|
||||
meeting_zone = pytz.timezone(meeting.time_zone)
|
||||
start_dt = meeting_zone.localize(datetime.datetime.combine(
|
||||
meeting.date,
|
||||
datetime.time.min
|
||||
))
|
||||
end_dt = meeting_zone.localize(datetime.datetime.combine(
|
||||
start_dt + datetime.timedelta(days=meeting.days - 1),
|
||||
datetime.time.max
|
||||
))
|
||||
|
||||
start = start_dt.astimezone(zone).strftime('%Y-%m-%d')
|
||||
end = end_dt.astimezone(zone).strftime('%Y-%m-%d')
|
||||
meeting_link = self.driver.find_element_by_link_text("IETF " + meeting.number)
|
||||
time_td = meeting_link.find_element_by_xpath('../../td[@class="meeting-time"]')
|
||||
self.assertIn('%s - %s' % (start, end), time_td.text)
|
||||
|
||||
sessions = [m.session_set.first() for m in self.displayed_interims()]
|
||||
self.assertGreater(len(sessions), 0)
|
||||
ietf_meetings = self.all_ietf_meetings()
|
||||
self.assertGreater(len(ietf_meetings), 0)
|
||||
|
||||
self.driver.get(self.absreverse('ietf.meeting.views.upcoming'))
|
||||
tz_select_input = self.driver.find_element_by_id('timezone-select')
|
||||
tz_select_bottom_input = self.driver.find_element_by_id('timezone-select-bottom')
|
||||
local_tz_link = self.driver.find_element_by_id('local-timezone')
|
||||
utc_tz_link = self.driver.find_element_by_id('utc-timezone')
|
||||
local_tz_bottom_link = self.driver.find_element_by_id('local-timezone-bottom')
|
||||
utc_tz_bottom_link = self.driver.find_element_by_id('utc-timezone-bottom')
|
||||
|
||||
# wait for the select box to be updated - look for an arbitrary time zone to be in
|
||||
# its options list to detect this
|
||||
arbitrary_tz = 'America/Halifax'
|
||||
arbitrary_tz_opt = wait.until(
|
||||
expected_conditions.presence_of_element_located(
|
||||
(By.CSS_SELECTOR, '#timezone-select > option[value="%s"]' % arbitrary_tz)
|
||||
)
|
||||
)
|
||||
arbitrary_tz_bottom_opt = tz_select_bottom_input.find_element_by_css_selector(
|
||||
'option[value="%s"]' % arbitrary_tz)
|
||||
|
||||
utc_tz_opt = tz_select_input.find_element_by_css_selector('option[value="UTC"]')
|
||||
utc_tz_bottom_opt= tz_select_bottom_input.find_element_by_css_selector('option[value="UTC"]')
|
||||
|
||||
# Moment.js guesses local time zone based on the behavior of Selenium's web client. This seems
|
||||
# to inherit Django's settings.TIME_ZONE but I don't know whether that's guaranteed to be consistent.
|
||||
# To avoid test fragility, ask Moment what it considers local and expect that.
|
||||
local_tz = self.driver.execute_script('return moment.tz.guess();')
|
||||
local_tz_opt = tz_select_input.find_element_by_css_selector('option[value=%s]' % local_tz)
|
||||
local_tz_bottom_opt = tz_select_bottom_input.find_element_by_css_selector('option[value="%s"]' % local_tz)
|
||||
|
||||
# Should start off in local time zone
|
||||
self.assertTrue(local_tz_opt.is_selected())
|
||||
self.assertTrue(local_tz_bottom_opt.is_selected())
|
||||
_assert_interim_tz_correct(sessions, local_tz)
|
||||
_assert_ietf_tz_correct(ietf_meetings, local_tz)
|
||||
|
||||
# click 'utc' button
|
||||
utc_tz_link.click()
|
||||
wait.until(expected_conditions.element_to_be_selected(utc_tz_opt))
|
||||
self.assertFalse(local_tz_opt.is_selected())
|
||||
self.assertFalse(local_tz_bottom_opt.is_selected())
|
||||
self.assertFalse(arbitrary_tz_opt.is_selected())
|
||||
self.assertFalse(arbitrary_tz_bottom_opt.is_selected())
|
||||
self.assertTrue(utc_tz_opt.is_selected())
|
||||
self.assertTrue(utc_tz_bottom_opt.is_selected())
|
||||
_assert_interim_tz_correct(sessions, 'UTC')
|
||||
_assert_ietf_tz_correct(ietf_meetings, 'UTC')
|
||||
|
||||
# click back to 'local'
|
||||
local_tz_link.click()
|
||||
wait.until(expected_conditions.element_to_be_selected(local_tz_opt))
|
||||
self.assertTrue(local_tz_opt.is_selected())
|
||||
self.assertTrue(local_tz_bottom_opt.is_selected())
|
||||
self.assertFalse(arbitrary_tz_opt.is_selected())
|
||||
self.assertFalse(arbitrary_tz_bottom_opt.is_selected())
|
||||
self.assertFalse(utc_tz_opt.is_selected())
|
||||
self.assertFalse(utc_tz_bottom_opt.is_selected())
|
||||
_assert_interim_tz_correct(sessions, local_tz)
|
||||
_assert_ietf_tz_correct(ietf_meetings, local_tz)
|
||||
|
||||
# Now select a different item from the select input
|
||||
arbitrary_tz_opt.click()
|
||||
wait.until(expected_conditions.element_to_be_selected(arbitrary_tz_opt))
|
||||
self.assertFalse(local_tz_opt.is_selected())
|
||||
self.assertFalse(local_tz_bottom_opt.is_selected())
|
||||
self.assertTrue(arbitrary_tz_opt.is_selected())
|
||||
self.assertTrue(arbitrary_tz_bottom_opt.is_selected())
|
||||
self.assertFalse(utc_tz_opt.is_selected())
|
||||
self.assertFalse(utc_tz_bottom_opt.is_selected())
|
||||
_assert_interim_tz_correct(sessions, arbitrary_tz)
|
||||
_assert_ietf_tz_correct(ietf_meetings, arbitrary_tz)
|
||||
|
||||
# Now repeat those tests using the widgets at the bottom of the page
|
||||
# click 'utc' button
|
||||
utc_tz_bottom_link.click()
|
||||
wait.until(expected_conditions.element_to_be_selected(utc_tz_opt))
|
||||
self.assertFalse(local_tz_opt.is_selected())
|
||||
self.assertFalse(local_tz_bottom_opt.is_selected())
|
||||
self.assertFalse(arbitrary_tz_opt.is_selected())
|
||||
self.assertFalse(arbitrary_tz_bottom_opt.is_selected())
|
||||
self.assertTrue(utc_tz_opt.is_selected())
|
||||
self.assertTrue(utc_tz_bottom_opt.is_selected())
|
||||
_assert_interim_tz_correct(sessions, 'UTC')
|
||||
_assert_ietf_tz_correct(ietf_meetings, 'UTC')
|
||||
|
||||
# click back to 'local'
|
||||
local_tz_bottom_link.click()
|
||||
wait.until(expected_conditions.element_to_be_selected(local_tz_opt))
|
||||
self.assertTrue(local_tz_opt.is_selected())
|
||||
self.assertTrue(local_tz_bottom_opt.is_selected())
|
||||
self.assertFalse(arbitrary_tz_opt.is_selected())
|
||||
self.assertFalse(arbitrary_tz_bottom_opt.is_selected())
|
||||
self.assertFalse(utc_tz_opt.is_selected())
|
||||
self.assertFalse(utc_tz_bottom_opt.is_selected())
|
||||
_assert_interim_tz_correct(sessions, local_tz)
|
||||
_assert_ietf_tz_correct(ietf_meetings, local_tz)
|
||||
|
||||
# Now select a different item from the select input
|
||||
arbitrary_tz_bottom_opt.click()
|
||||
wait.until(expected_conditions.element_to_be_selected(arbitrary_tz_opt))
|
||||
self.assertFalse(local_tz_opt.is_selected())
|
||||
self.assertFalse(local_tz_bottom_opt.is_selected())
|
||||
self.assertTrue(arbitrary_tz_opt.is_selected())
|
||||
self.assertTrue(arbitrary_tz_bottom_opt.is_selected())
|
||||
self.assertFalse(utc_tz_opt.is_selected())
|
||||
self.assertFalse(utc_tz_bottom_opt.is_selected())
|
||||
_assert_interim_tz_correct(sessions, arbitrary_tz)
|
||||
_assert_ietf_tz_correct(ietf_meetings, arbitrary_tz)
|
||||
|
||||
|
||||
# The following are useful debugging tools
|
||||
|
||||
# If you add this to a LiveServerTestCase and run just this test, you can browse
|
||||
|
|
|
@ -3363,7 +3363,7 @@ def upcoming(request):
|
|||
# Get ietf meetings starting 7 days ago, and interim meetings starting today
|
||||
ietf_meetings = Meeting.objects.filter(type_id='ietf', date__gte=today-datetime.timedelta(days=7))
|
||||
for m in ietf_meetings:
|
||||
m.end = m.date+datetime.timedelta(days=m.days)
|
||||
m.end = m.date + datetime.timedelta(days=m.days-1) # subtract 1 to avoid counting an extra day
|
||||
|
||||
interim_sessions = add_event_info_to_session_qs(
|
||||
Session.objects.filter(
|
||||
|
@ -3411,13 +3411,11 @@ def upcoming(request):
|
|||
|
||||
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())
|
||||
o.start_timestamp = int(pytz.utc.localize(datetime.datetime.combine(o.date, datetime.time.min)).timestamp())
|
||||
o.end_timestamp = int(pytz.utc.localize(datetime.datetime.combine(o.end, datetime.time.max)).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());
|
||||
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)
|
||||
|
|
|
@ -232,6 +232,7 @@ var agenda_filter_for_testing; // methods to be accessed for automated testing
|
|||
* button UI. Do not call if you only want to use the parameter parsing routines.
|
||||
*/
|
||||
function enable () {
|
||||
// ready handler fires immediately if document is already "ready"
|
||||
$(document).ready(function () {
|
||||
register_handlers();
|
||||
update_view();
|
||||
|
|
229
ietf/static/ietf/js/agenda/agenda_timezone.js
Normal file
229
ietf/static/ietf/js/agenda/agenda_timezone.js
Normal file
|
@ -0,0 +1,229 @@
|
|||
// Copyright The IETF Trust 2021, All Rights Reserved
|
||||
|
||||
/*
|
||||
Timezone support specific to the agenda page
|
||||
|
||||
To properly handle timezones other than local, needs a method to retrieve
|
||||
the current timezone. Set this by passing a method taking no parameters and
|
||||
returning the current timezone to the set_current_tz_cb() method.
|
||||
This should be done before calling anything else in the file.
|
||||
*/
|
||||
|
||||
var meeting_timezone;
|
||||
var local_timezone = moment.tz.guess();
|
||||
|
||||
// get_current_tz_cb must be overwritten using set_current_tz_cb
|
||||
var get_current_tz_cb = function () {
|
||||
throw new Error('Tried to get current timezone before callback registered. Use set_current_tz_cb().')
|
||||
};
|
||||
|
||||
// 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();
|
||||
});
|
||||
}
|
||||
|
||||
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 current_timezone = get_current_tz_cb();
|
||||
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) {
|
||||
$('span.current-tz').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();
|
||||
}
|
||||
|
||||
// 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(), get_current_tz_cb(), 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);
|
||||
}
|
||||
|
||||
// set method used to find current time zone
|
||||
function set_current_tz_cb(fn) {
|
||||
get_current_tz_cb = fn;
|
||||
}
|
|
@ -1,265 +1,74 @@
|
|||
// Callback for timezone change - called after current_timezone is updated
|
||||
var timezone_change_callback;
|
||||
// Copyright The IETF Trust 2021, All Rights Reserved
|
||||
|
||||
// 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();
|
||||
});
|
||||
}
|
||||
/*
|
||||
Timezone selection handling. Relies on the moment.js library.
|
||||
|
||||
// Initialize timezone system
|
||||
function timezone_init(current) {
|
||||
var tz_names = moment.tz.names();
|
||||
var select = $('#timezone_select');
|
||||
|
||||
select.empty();
|
||||
$.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();
|
||||
}
|
||||
To use, create one (or more) select inputs with class "tz-select". When the initialize()
|
||||
method is called, the options in the select will be replaced with the recognized time zone
|
||||
names. Time zone can be changed via the select input or by calling the use() method with
|
||||
the name of a time zone (or 'local' to guess the user's local timezone).
|
||||
*/
|
||||
var ietf_timezone; // public interface
|
||||
|
||||
// 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;
|
||||
(function () {
|
||||
'use strict';
|
||||
// Callback for timezone change - called after current_timezone is updated
|
||||
var timezone_change_callback;
|
||||
var current_timezone;
|
||||
|
||||
// Select timezone to use. Arg is name of a timezone or 'local' to guess local tz.
|
||||
function use_timezone (newtz) {
|
||||
// Guess local timezone if necessary
|
||||
if (newtz.toLowerCase() === 'local') {
|
||||
newtz = moment.tz.guess()
|
||||
}
|
||||
|
||||
if (current_timezone !== newtz) {
|
||||
current_timezone = newtz
|
||||
// Update values of tz-select inputs but do not trigger change event
|
||||
$('select.tz-select').val(newtz)
|
||||
if (timezone_change_callback) {
|
||||
timezone_change_callback(newtz)
|
||||
}
|
||||
}
|
||||
}
|
||||
$('#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".
|
||||
/* Initialize timezone system
|
||||
*
|
||||
* This will set the timezone to the value of 'current'. Set up the tz_change callback
|
||||
* before initializing.
|
||||
*/
|
||||
function timezone_init (current) {
|
||||
var tz_names = moment.tz.names()
|
||||
var select = $('select.tz-select')
|
||||
|
||||
function format_time(t, tz, fmt) {
|
||||
var out;
|
||||
var mtz = meeting_timezone;
|
||||
if (mtz == "") {
|
||||
mtz = "UTC";
|
||||
select.empty()
|
||||
$.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
|
||||
}))
|
||||
}
|
||||
})
|
||||
select.change(function () {use_timezone(this.value)});
|
||||
/* When navigating back/forward, the browser may change the select input's
|
||||
* value after the window load event. It does not fire the change event on
|
||||
* the input when it does this. The pageshow event occurs after such an update,
|
||||
* so trigger the change event ourselves to be sure the UI stays consistent
|
||||
* with the timezone select input. */
|
||||
window.addEventListener('pageshow', function(){select.change()})
|
||||
use_timezone(current);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
// Expose public interface
|
||||
ietf_timezone = {
|
||||
get_current_tz: function() {return current_timezone},
|
||||
initialize: timezone_init,
|
||||
set_tz_change_callback: function(cb) {timezone_change_callback=cb},
|
||||
use: use_timezone
|
||||
}
|
||||
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;
|
||||
$('span.current-tz').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();
|
||||
if (timezone_change_callback) {
|
||||
timezone_change_callback(newtz);
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// Register a callback for timezone change
|
||||
function set_tz_change_callback(cb) {
|
||||
timezone_change_callback = cb;
|
||||
}
|
||||
})();
|
|
@ -40,14 +40,14 @@
|
|||
font-weight: normal;
|
||||
}
|
||||
.tz-display select {
|
||||
min-width: 15em;
|
||||
min-width: 15em;
|
||||
}
|
||||
#affix .nav li.tz-display {
|
||||
padding: 4px 20px;
|
||||
padding: 4px 20px;
|
||||
}
|
||||
#affix .nav li.tz-display a {
|
||||
display: inline;
|
||||
padding: 0;
|
||||
display: inline;
|
||||
padding: 0;
|
||||
}
|
||||
{% endblock %}
|
||||
|
||||
|
@ -76,12 +76,12 @@
|
|||
<div class="col-xs-6">
|
||||
<div class="tz-display">
|
||||
<div><small>
|
||||
<label for="timezone_select">Time zone:</label>
|
||||
<a id="meeting-timezone" onclick="use_timezone(0)">Meeting</a> |
|
||||
<a id="local-timezone" onclick="use_timezone(1)">Local</a> |
|
||||
<a id="utc-timezone" onclick="use_timezone(2)">UTC</a>
|
||||
<label for="timezone-select">Time zone:</label>
|
||||
<a id="meeting-timezone" onclick="ietf_timezone.use('{{ timezone }}')">Meeting</a> |
|
||||
<a id="local-timezone" onclick="ietf_timezone.use('local')">Local</a> |
|
||||
<a id="utc-timezone" onclick="ietf_timezone.use('UTC')">UTC</a>
|
||||
</small></div>
|
||||
<select id="timezone_select">
|
||||
<select id="timezone-select" class="tz-select">
|
||||
{# Avoid blank while loading. JavaScript replaces the option list after init. #}
|
||||
<option selected>{{ timezone }}</option>
|
||||
</select>
|
||||
|
@ -348,23 +348,23 @@
|
|||
</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="tz-display">Showing <span class="current-tz">{{ timezone }}</span> time</li>
|
||||
<li class="tz-display"><span> {# span avoids applying nav link styling to these shortcuts #}
|
||||
<a onclick="use_timezone(0)">Meeting time</a> |
|
||||
<a onclick="use_timezone(1)">Local time</a> |
|
||||
<a onclick="use_timezone(2)">UTC</a></span>
|
||||
</li>
|
||||
{% if settings.DEBUG and settings.DEBUG_AGENDA %}
|
||||
<li><hr/></li>
|
||||
<li><span id="current-time"></span></li>
|
||||
{% endif %}
|
||||
<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="tz-display">Showing <span class="current-tz">{{ timezone }}</span> time</li>
|
||||
<li class="tz-display"><span> {# span avoids applying nav link styling to these shortcuts #}
|
||||
<a onclick="ietf_timezone.use('{{ timezone }}')">Meeting time</a> |
|
||||
<a onclick="ietf_timezone.use('local')">Local time</a> |
|
||||
<a onclick="ietf_timezone.use('UTC')">UTC</a></span>
|
||||
</li>
|
||||
{% if settings.DEBUG and settings.DEBUG_AGENDA %}
|
||||
<li><hr/></li>
|
||||
<li><span id="current-time"></span></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -376,8 +376,6 @@
|
|||
|
||||
<script src="{% static 'ietf/js/agenda/agenda_filter.js' %}"></script>
|
||||
<script>
|
||||
var current_timezone = 'UTC';
|
||||
|
||||
// Update the agenda display with specified filters
|
||||
function update_agenda_display(filter_params) {
|
||||
var agenda_rows=$('[id^="row-"]')
|
||||
|
@ -402,7 +400,7 @@
|
|||
// this is a "negative" item by wg: when present, hide these rows
|
||||
agenda_filter.rows_matching_filter_keyword(agenda_rows, v).hide();
|
||||
});
|
||||
|
||||
|
||||
// Now hide any session label rows with no visible sessions. Identify
|
||||
// by matching on start/end timestamps.
|
||||
$('tr.session-label-row').each(function(i, e) {
|
||||
|
@ -442,15 +440,15 @@
|
|||
}
|
||||
update_weekview_display();
|
||||
}
|
||||
|
||||
|
||||
function update_weekview_display() {
|
||||
var weekview = $("#weekview");
|
||||
if (!weekview.hasClass('hidden')) {
|
||||
var queryparams = window.location.search;
|
||||
if (queryparams) {
|
||||
queryparams += '&tz=' + current_timezone.toLowerCase();
|
||||
queryparams += '&tz=' + ietf_timezone.get_current_tz().toLowerCase();
|
||||
} else {
|
||||
queryparams = '?tz=' + current_timezone.toLowerCase();
|
||||
queryparams = '?tz=' + ietf_timezone.get_current_tz().toLowerCase();
|
||||
}
|
||||
var new_url = 'week-view.html' + queryparams;
|
||||
var wv_iframe = document.getElementById('weekview');
|
||||
|
@ -545,6 +543,7 @@
|
|||
<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 src="{% static 'ietf/js/agenda/agenda_timezone.js' %}"></script>
|
||||
<script>
|
||||
|
||||
{% if settings.DEBUG and settings.DEBUG_AGENDA %}
|
||||
|
@ -567,20 +566,40 @@
|
|||
speedup = 1;
|
||||
{% endif %}
|
||||
|
||||
// Get meeting and local times, initialize timezone system
|
||||
meeting_timezone = "{{timezone}}";
|
||||
local_timezone = moment.tz.guess();
|
||||
{% if "-utc" in request.path %}
|
||||
timezone_init('UTC');
|
||||
{% else %}
|
||||
timezone_init(meeting_timezone);
|
||||
{% endif %}
|
||||
|
||||
init_timers();
|
||||
$(document).ready(function() {
|
||||
// Methods/variables here that are not in ietf_timezone or agenda_filter are from agenda_timezone.js
|
||||
meeting_timezone = '{{ timezone }}';
|
||||
|
||||
set_tz_change_callback(update_weekview_display);
|
||||
agenda_filter.set_update_callback(update_view);
|
||||
agenda_filter.enable();
|
||||
// First, initialize_moments(). This must be done before calling any of the update methods.
|
||||
// It does not need timezone info, so safe to call before initializing ietf_timezone.
|
||||
initialize_moments(); // fills in moments in the agenda data
|
||||
|
||||
// Now set up callbacks related to ietf_timezone. This must happen before calling initialize().
|
||||
// In particular, set_current_tz_cb() must be called before the update methods are called.
|
||||
set_current_tz_cb(ietf_timezone.get_current_tz); // give agenda_timezone access to this method
|
||||
ietf_timezone.set_tz_change_callback(function(newtz) {
|
||||
update_times(newtz);
|
||||
update_weekview_display();
|
||||
}
|
||||
);
|
||||
|
||||
// With callbacks in place, call ietf_timezone.initialize(). This will call the tz_change callback
|
||||
// after setting things up.
|
||||
{% if "-utc" in request.path %}
|
||||
ietf_timezone.initialize('UTC');
|
||||
{% else %}
|
||||
ietf_timezone.initialize(meeting_timezone);
|
||||
{% endif %}
|
||||
|
||||
// Now make other setup calls from agenda_timezone.js
|
||||
add_tooltips();
|
||||
init_timers();
|
||||
|
||||
// Finally, set up the agenda filter UI. This does not depend on the timezone.
|
||||
agenda_filter.set_update_callback(update_view);
|
||||
agenda_filter.enable();
|
||||
}
|
||||
);
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
|
@ -15,20 +15,56 @@
|
|||
|
||||
{% block title %}Upcoming Meetings{% endblock %}
|
||||
|
||||
{% block morecss %}
|
||||
div.title-buttons {
|
||||
margin-bottom: 0.5em;
|
||||
margin-top: 1em;
|
||||
text-align: right;
|
||||
}
|
||||
.tz-display {
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
.tz-display label {
|
||||
font-weight: normal;
|
||||
}
|
||||
.tz-display a {
|
||||
cursor: pointer;
|
||||
}
|
||||
select.tz-select {
|
||||
min-width: 15em;
|
||||
margin-bottom: 0.3em;
|
||||
}
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
{% origin %}
|
||||
<div class="row">
|
||||
<div class="col-md-10">
|
||||
<div class="row">
|
||||
<div class="col-xs-6">
|
||||
<h1>Upcoming Meetings</h1>
|
||||
</div>
|
||||
<div class="title-buttons col-xs-6">
|
||||
<div>
|
||||
<a title="iCalendar subscription for upcoming meetings" href="webcal://{{request.get_host}}{% url 'ietf.meeting.views.upcoming_ical' %}">
|
||||
<span class="fa fa-stack-1"><i class="fa fa-fw fa-calendar-o fa-stack-1x"></i><i class="fa fa-fw fa-repeat fa-stack-xs"></i></span>
|
||||
</a>
|
||||
<a title="iCalendar entry for upcoming meetings" href="{% url 'ietf.meeting.views.upcoming_ical' %}"><span class="fa fa-calendar"></span></a>
|
||||
</div>
|
||||
<div class="tz-display">
|
||||
<label for="timezone-select">Time zone:</label>
|
||||
<small>
|
||||
<a id="local-timezone" onclick="ietf_timezone.use('local')">Local</a> |
|
||||
<a id="utc-timezone" onclick="ietf_timezone.use('UTC')">UTC</a>
|
||||
</small>
|
||||
<select class="tz-select" id="timezone-select" autocomplete="off">
|
||||
<!-- Avoid blank while loading. Needs to agree with native times in the table
|
||||
so the display is correct if JS is not enabled -->
|
||||
<option selected>UTC</option>
|
||||
</select>
|
||||
|
||||
<h1>Upcoming Meetings
|
||||
<span class="regular pull-right">
|
||||
<a title="iCalendar subscription for upcoming meetings" href="webcal://{{request.get_host}}{% url 'ietf.meeting.views.upcoming_ical' %}">
|
||||
<span class="fa fa-stack-1"><i class="fa fa-fw fa-calendar-o fa-stack-1x"></i><i class="fa fa-fw fa-repeat fa-stack-xs"></i></span>
|
||||
</a>
|
||||
<a title="iCalendar entry for upcoming meetings" href="{% url 'ietf.meeting.views.upcoming_ical' %}"><span class="fa fa-calendar"></span></a>
|
||||
</span>
|
||||
</h1>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p>For more on regular IETF meetings see <a href="https://www.ietf.org/meeting/upcoming.html">here</a></p>
|
||||
<p>Meeting important dates are not included in upcoming meeting calendars. They have <a href="{% url 'ietf.meeting.views.important_dates' %}">their own calendar</a></p>
|
||||
|
||||
|
@ -67,18 +103,27 @@
|
|||
</thead>
|
||||
<tbody>
|
||||
{% for entry in entries %}
|
||||
<tr class="entry"
|
||||
<tr class="entry"
|
||||
{% if entry|classname == 'Session' %}data-filter-keywords="{{ entry.filter_keywords|join:',' }}"{% endif %}>
|
||||
{% if entry|classname == 'Meeting' %}
|
||||
{% with meeting=entry %}
|
||||
<td>{{ meeting.date }} - {{ meeting.end }}</td>
|
||||
<td class="meeting-time"
|
||||
data-start-date="{{ meeting.date }}" {# dates local to meeting #}
|
||||
data-end-date="{{ meeting.end }}"
|
||||
data-time-zone="{{ meeting.time_zone }}">
|
||||
{{ meeting.date }} - {{ meeting.end }}
|
||||
</td>
|
||||
<td>ietf</td>
|
||||
<td><a class="ietf-meeting-link" href="{% url 'ietf.meeting.views.agenda' num=meeting.number %}">IETF {{ meeting.number }}</a></td>
|
||||
<td></td>
|
||||
{% endwith %}
|
||||
{% elif entry|classname == 'Session' %}
|
||||
{% with session=entry group=entry.group meeting=entry.meeting%}
|
||||
<td>{{ session.official_timeslotassignment.timeslot.utc_start_time | date:"Y-m-d H:i"}} - {{ session.official_timeslotassignment.timeslot.utc_end_time | date:"H:i e" }}</td>
|
||||
<td class="session-time"
|
||||
data-start-utc="{{ session.official_timeslotassignment.timeslot.utc_start_time | date:'Y-m-d H:i' }}Z"
|
||||
data-end-utc="{{ session.official_timeslotassignment.timeslot.utc_end_time | date:'Y-m-d H:i' }}Z">
|
||||
{{ session.official_timeslotassignment.timeslot.utc_start_time | date:"Y-m-d H:i"}} - {{ session.official_timeslotassignment.timeslot.utc_end_time | date:"H:i" }}
|
||||
</td>
|
||||
<td><a href="{% url 'ietf.group.views.group_home' acronym=group.acronym %}">{{ group.acronym }}</a></td>
|
||||
<td>
|
||||
<a class="interim-meeting-link" href="{% url 'ietf.meeting.views.session_details' num=meeting.number acronym=group.acronym %}"> {{ meeting.number }}</a>
|
||||
|
@ -109,34 +154,52 @@
|
|||
{% endcache %}
|
||||
</div>
|
||||
</div>
|
||||
<div id="calendar" class="col-md-10" ></div>
|
||||
<div class="row">
|
||||
<div class="col-md-10">
|
||||
<div class="tz-display text-right">
|
||||
<label for="timezone-select-bottom">Time zone: </label>
|
||||
<small>
|
||||
<a id="local-timezone-bottom" onclick="ietf_timezone.use('local')">Local</a> |
|
||||
<a id="utc-timezone-bottom" onclick="ietf_timezone.use('UTC')">UTC</a>
|
||||
</small>
|
||||
<select class="tz-select" id="timezone-select-bottom"></select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-10">
|
||||
<div id="calendar"></div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
<script src="{% static "jquery.tablesorter/js/jquery.tablesorter.combined.min.js" %}"></script>
|
||||
<script src="{% static 'fullcalendar/core/main.js' %}"></script>
|
||||
<script src="{% static 'fullcalendar/daygrid/main.js' %}"></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/agenda_filter.js' %}"></script>
|
||||
<script src="{% static 'ietf/js/agenda/timezone.js' %}"></script>
|
||||
<script>
|
||||
// List of all events with meta-info needed for filtering
|
||||
var all_event_list = [{% for entry in entries %}
|
||||
{% if entry|classname == 'Meeting' %}
|
||||
{% with meeting=entry %}
|
||||
{
|
||||
title: 'IETF {{ meeting.number }}',
|
||||
start: '{{meeting.date}}',
|
||||
end: '{{meeting.end}}',
|
||||
ietf_meeting_number: '{{ meeting.number }}',
|
||||
start_moment: moment.tz('{{meeting.date}}', '{{ meeting.time_zone }}').startOf('day'),
|
||||
end_moment: moment.tz('{{meeting.end}}', '{{ meeting.time_zone }}').endOf('day'),
|
||||
url: '{% url 'ietf.meeting.views.agenda' num=meeting.number %}'
|
||||
}{% if not forloop.last %}, {% endif %}
|
||||
{% endwith %}
|
||||
{% else %} {# if it's not a Meeting, it's a Session #}
|
||||
{% with session=entry %}
|
||||
{
|
||||
title: '{{session.official_timeslotassignment.timeslot.utc_start_time|date:"H:i"}}-{{session.official_timeslotassignment.timeslot.utc_end_time|date:"H:i"}}',
|
||||
group: '{% if session.group %}{{session.group.acronym}}{% endif %}',
|
||||
filter_keywords: ["{{ session.filter_keywords|join:'","' }}"],
|
||||
start: '{{session.official_timeslotassignment.timeslot.utc_start_time | date:"Y-m-d H:i"}}',
|
||||
end: '{{session.official_timeslotassignment.timeslot.utc_end_time | date:"Y-m-d H:i"}}',
|
||||
start_moment: moment.utc('{{session.official_timeslotassignment.timeslot.utc_start_time | date:"Y-m-d H:i"}}'),
|
||||
end_moment: moment.utc('{{session.official_timeslotassignment.timeslot.utc_end_time | date:"Y-m-d H:i"}}'),
|
||||
url: '{% url 'ietf.meeting.views.session_details' num=session.meeting.number acronym=session.group.acronym %}'
|
||||
}
|
||||
{% endwith %}
|
||||
|
@ -144,7 +207,9 @@
|
|||
{% endif %}
|
||||
{% endfor %}];
|
||||
var filtered_event_list = []; // currently visible list
|
||||
var display_events = []; // filtered events, processed for calendar display
|
||||
var event_calendar; // handle on the calendar object
|
||||
var current_tz = 'UTC';
|
||||
|
||||
// Test whether an event should be visible given a set of filter parameters
|
||||
function calendar_event_visible(filter_params, event) {
|
||||
|
@ -158,28 +223,55 @@
|
|||
&& agenda_filter.keyword_match(filter_params.show, event.filter_keywords));
|
||||
}
|
||||
|
||||
// Apply filter_params to the event list and format data for the calendar
|
||||
/* Apply filter_params to the event list */
|
||||
function filter_calendar_events(filter_params, event_list) {
|
||||
var calendarEl = document.getElementById('calendar');
|
||||
var glue = calendarEl.clientWidth > 720 ? ' ' : '\n';
|
||||
var filtered_output = [];
|
||||
for (var ii = 0; ii < event_list.length; ii++) {
|
||||
var this_event = event_list[ii];
|
||||
if (calendar_event_visible(filter_params, this_event)) {
|
||||
filtered_output.push({
|
||||
title: this_event.title + (this_event.group ? (glue + this_event.group) : ''),
|
||||
start: this_event.start,
|
||||
end: this_event.end,
|
||||
url: this_event.url
|
||||
})
|
||||
filtered_output.push(this_event);
|
||||
}
|
||||
}
|
||||
return filtered_output;
|
||||
}
|
||||
|
||||
// Initialize or update the calendar, updating the filtered event list
|
||||
function update_calendar(filter_params) {
|
||||
filtered_event_list = filter_calendar_events(filter_params, all_event_list);
|
||||
// format a moment in a tz
|
||||
var moment_formats = {time: 'HH:mm', date: 'YYYY-MM-DD', datetime: 'YYYY-MM-DD HH:mm'};
|
||||
function format_moment(t_moment, tz, fmt_type) {
|
||||
return t_moment.tz(tz).format(moment_formats[fmt_type]);
|
||||
}
|
||||
|
||||
function make_display_events(event_data, tz) {
|
||||
var output = [];
|
||||
var calendarEl = document.getElementById('calendar');
|
||||
var glue = calendarEl.clientWidth > 720 ? ' ' : '\n';
|
||||
return $.map(event_data, function(src_event) {
|
||||
var title;
|
||||
// Render IETF meetings with meeting dates, sessions with actual times
|
||||
if (src_event.ietf_meeting_number) {
|
||||
title = 'IETF ' + src_event.ietf_meeting_number;
|
||||
} else {
|
||||
title = (format_moment(src_event.start_moment, tz, 'time') + '-'
|
||||
+ format_moment(src_event.end_moment, tz, 'time')
|
||||
+ glue + (src_event.group || 'Invalid event'));
|
||||
}
|
||||
return {
|
||||
title: title,
|
||||
start: format_moment(src_event.start_moment, tz, 'datetime'),
|
||||
end: format_moment(src_event.end_moment, tz, 'datetime'),
|
||||
url: src_event.url
|
||||
}; // all events have the URL
|
||||
});
|
||||
}
|
||||
|
||||
// Initialize or update the calendar, updating the filtered event list and/or timezone
|
||||
function update_calendar(tz, filter_params) {
|
||||
if (filter_params) {
|
||||
// Update event list if we were called with filter params
|
||||
filtered_event_list = filter_calendar_events(filter_params, all_event_list);
|
||||
}
|
||||
display_events = make_display_events(filtered_event_list, tz);
|
||||
|
||||
if (event_calendar) {
|
||||
event_calendar.refetchEvents()
|
||||
} else {
|
||||
|
@ -191,7 +283,7 @@
|
|||
event_calendar = new FullCalendar.Calendar(calendarEl, {
|
||||
plugins: ['dayGrid'],
|
||||
displayEventTime: false,
|
||||
events: function (fInfo, success) {success(filtered_event_list)},
|
||||
events: function (fInfo, success) {success(display_events)},
|
||||
eventRender: function (info) {
|
||||
$(info.el).tooltip({ title: info.event.title })
|
||||
},
|
||||
|
@ -239,7 +331,7 @@
|
|||
function update_view(filter_params) {
|
||||
update_meeting_display(filter_params);
|
||||
update_links(filter_params);
|
||||
update_calendar(filter_params);
|
||||
update_calendar(current_tz, filter_params);
|
||||
}
|
||||
|
||||
// Set up the filtering - the callback will be called when the page loads and on any filter changes
|
||||
|
@ -276,5 +368,38 @@
|
|||
});
|
||||
}
|
||||
});
|
||||
|
||||
function format_session_time(session_elt, tz) {
|
||||
var start = moment.utc($(session_elt).attr('data-start-utc'));
|
||||
var end = moment.utc($(session_elt).attr('data-end-utc'));
|
||||
return format_moment(start, tz, 'datetime') + ' - ' + format_moment(end, tz, 'time');
|
||||
}
|
||||
|
||||
function format_meeting_time(meeting_elt, tz) {
|
||||
var meeting_tz = $(meeting_elt).attr('data-time-zone');
|
||||
var start = moment.tz($(meeting_elt).attr('data-start-date'), meeting_tz).startOf('day');
|
||||
var end = moment.tz($(meeting_elt).attr('data-end-date'), meeting_tz).endOf('day');
|
||||
return format_moment(start, tz, 'date') + ' - ' + format_moment(end, tz, 'date');
|
||||
}
|
||||
|
||||
function timezone_changed(newtz) {
|
||||
// update times for events in the table
|
||||
if (current_tz !== newtz) {
|
||||
current_tz = newtz;
|
||||
$('.session-time').each(function () {
|
||||
$(this).html(format_session_time(this, newtz));
|
||||
});
|
||||
$('.meeting-time').each(function () {
|
||||
$(this).html(format_meeting_time(this, newtz));
|
||||
});
|
||||
}
|
||||
|
||||
update_calendar(newtz);
|
||||
}
|
||||
|
||||
|
||||
// Init with best guess at local timezone.
|
||||
ietf_timezone.set_tz_change_callback(timezone_changed);
|
||||
ietf_timezone.initialize('local');
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
Loading…
Reference in a new issue