diff --git a/ietf/meeting/views.py b/ietf/meeting/views.py index 72161a16b..58499a7d2 100644 --- a/ietf/meeting/views.py +++ b/ietf/meeting/views.py @@ -1530,6 +1530,7 @@ def agenda(request, num=None, name=None, base=None, ext=None, owner=None, utc="" is_current_meeting = (num is None) or (num == get_current_ietf_meeting_num()) rendered_page = render(request, "meeting/"+base+ext, { + "personalize": False, "schedule": schedule, "filtered_assignments": filtered_assignments, "updated": updated, @@ -1701,8 +1702,9 @@ def agenda_personalize(request, num): return render( request, - "meeting/agenda_personalize.html", + "meeting/agenda.html", { + 'personalize': True, 'schedule': meeting.schedule, 'updated': meeting.updated(), 'filtered_assignments': filtered_assignments, @@ -4161,4 +4163,4 @@ def approve_proposed_slides(request, slidesubmission_id, num): 'session_number': session_number, 'existing_doc' : existing_doc, 'form': form, - }) + }) \ No newline at end of file diff --git a/ietf/static/js/agenda_filter.js b/ietf/static/js/agenda_filter.js index 92da3f600..b84877960 100644 --- a/ietf/static/js/agenda_filter.js +++ b/ietf/static/js/agenda_filter.js @@ -3,7 +3,7 @@ window.agenda_filter_for_testing; // methods to be accessed for automated testin // closure to create private scope (function () { - 'use strict' + 'use strict'; /* n.b., const refers to the opts object itself, not its contents. * Use camelCase for easy translation into element.dataset keys, @@ -15,10 +15,10 @@ window.agenda_filter_for_testing; // methods to be accessed for automated testin }; /* Remove from list, if present */ - function remove_list_item (list, item) { + function remove_list_item(list, item) { var item_index = list.indexOf(item); if (item_index !== -1) { - list.splice(item_index, 1) + list.splice(item_index, 1); } } @@ -26,63 +26,68 @@ window.agenda_filter_for_testing; // methods to be accessed for automated testin * * Returns true if added to the list, otherwise false. */ - function toggle_list_item (list, item) { + function toggle_list_item(list, item) { var item_index = list.indexOf(item); if (item_index === -1) { - list.push(item) + list.push(item); return true; } else { - list.splice(item_index, 1) + list.splice(item_index, 1); return false; } } - function parse_query_params (qs) { - var params = {} - qs = decodeURI(qs).replace(/^\?/, '').toLowerCase() + function parse_query_params(qs) { + var params = {}; + qs = decodeURI(qs) + .replace(/^\?/, '') + .toLowerCase(); if (qs) { - var param_strs = qs.split('&') + var param_strs = qs.split('&'); for (var ii = 0; ii < param_strs.length; ii++) { - var toks = param_strs[ii].split('=', 2) - params[toks[0]] = toks[1] || true + var toks = param_strs[ii].split('=', 2); + params[toks[0]] = toks[1] || true; } } - return params + return params; } /* filt = 'show' or 'hide' */ - function get_filter_from_qparams (qparams, filt) { + function get_filter_from_qparams(qparams, filt) { if (!qparams[filt] || (qparams[filt] === true)) { return []; } var result = []; var qp = qparams[filt].split(','); - + for (var ii = 0; ii < qp.length; ii++) { result.push(qp[ii].trim()); } return result; } - function get_filter_params (qparams) { + function get_filter_params(qparams) { var enabled = opts.alwaysShow || qparams.show || qparams.hide; return { enabled: enabled, show: get_filter_from_qparams(qparams, 'show'), hide: get_filter_from_qparams(qparams, 'hide') - } + }; } function get_keywords(elt) { - var keywords = $(elt).attr('data-filter-keywords'); + var keywords = $(elt) + .attr('data-filter-keywords'); if (keywords) { - return keywords.toLowerCase().split(','); + return keywords.toLowerCase() + .split(','); } return []; } function get_item(elt) { - return $(elt).attr('data-filter-item'); + return $(elt) + .attr('data-filter-item'); } // utility method - is there a match between two lists of keywords? @@ -94,25 +99,27 @@ window.agenda_filter_for_testing; // methods to be accessed for automated testin } return false; } - + // Find the items corresponding to a keyword - function get_items_with_keyword (keyword) { + function get_items_with_keyword(keyword) { var items = []; - $('.view button.pickview').filter(function(index, elt) { - return keyword_match(get_keywords(elt), [keyword]); - }).each(function (index, elt) { - items.push(get_item($(elt))); - }); + $('.view button.pickview') + .filter(function (index, elt) { + return keyword_match(get_keywords(elt), [keyword]); + }) + .each(function (index, elt) { + items.push(get_item($(elt))); + }); return items; } - function filtering_is_enabled (filter_params) { + function filtering_is_enabled(filter_params) { return filter_params.enabled; } // Update the filter / customization UI to match the current filter parameters - function update_filter_ui (filter_params) { + function update_filter_ui(filter_params) { var buttons = $('.pickview'); if (!filtering_is_enabled(filter_params)) { @@ -121,12 +128,12 @@ window.agenda_filter_for_testing; // methods to be accessed for automated testin return; } - update_href_querystrings(filter_params_as_querystring(filter_params)) + update_href_querystrings(filter_params_as_querystring(filter_params)); // show the customizer - it will stay visible even if filtering is disabled const customizer = $('#customize'); if (customizer.hasClass('collapse')) { - customizer.collapse('show') + customizer.collapse('show'); } // Update button state to match visibility @@ -135,7 +142,7 @@ window.agenda_filter_for_testing; // methods to be accessed for automated testin var keywords = get_keywords(elt); keywords.push(get_item(elt)); // treat item as one of its keywords var hidden = keyword_match(filter_params.hide, keywords); - var shown = keyword_match(filter_params.show, keywords); + var shown = keyword_match(filter_params.show, keywords); if (shown && !hidden) { elt.addClass('active'); } else { @@ -149,11 +156,11 @@ window.agenda_filter_for_testing; // methods to be accessed for automated testin * Calling the individual update_* functions outside of this method will likely cause * various parts of the page to get out of sync. */ - function update_view () { - var filter_params = get_filter_params(parse_query_params(window.location.search)) - update_filter_ui(filter_params) + function update_view() { + var filter_params = get_filter_params(parse_query_params(window.location.search)); + update_filter_ui(filter_params); if (opts.updateCallback) { - opts.updateCallback(filter_params) + opts.updateCallback(filter_params); } } @@ -162,19 +169,19 @@ window.agenda_filter_for_testing; // methods to be accessed for automated testin * Updates the URL to match filter_params, then updates the history / display to match * (if supported) or loads the new URL. */ - function update_filters (filter_params) { + function update_filters(filter_params) { var new_url = replace_querystring( - window.location.href, - filter_params_as_querystring(filter_params) - ) - update_href_querystrings(filter_params_as_querystring(filter_params)) + window.location.href, + filter_params_as_querystring(filter_params) + ); + update_href_querystrings(filter_params_as_querystring(filter_params)); if (window.history && window.history.replaceState) { // Keep current origin, replace search string, no page reload - history.replaceState({}, document.title, new_url) - update_view() + history.replaceState({}, document.title, new_url); + update_view(); } else { // No window.history.replaceState support, page reload required - window.location = new_url + window.location = new_url; } } @@ -183,34 +190,35 @@ window.agenda_filter_for_testing; // methods to be accessed for automated testin */ function update_href_querystrings(querystring) { Array.from( - document.getElementsByClassName('agenda-link filterable') - ).forEach( - (elt) => elt.href = replace_querystring(elt.href, querystring) - ) + document.getElementsByClassName('agenda-link filterable') + ) + .forEach( + (elt) => elt.href = replace_querystring(elt.href, querystring) + ); } function filter_params_as_querystring(filter_params) { - var qparams = [] + var qparams = []; if (filter_params.show.length > 0) { - qparams.push('show=' + filter_params.show.join()) + qparams.push('show=' + filter_params.show.join()); } if (filter_params.hide.length > 0) { - qparams.push('hide=' + filter_params.hide.join()) + qparams.push('hide=' + filter_params.hide.join()); } if (qparams.length > 0) { - return '?' + qparams.join('&') + return '?' + qparams.join('&'); } - return '' + return ''; } function replace_querystring(url, new_querystring) { - return url.replace(/(\?.*)?(#.*)?$/, new_querystring + window.location.hash) + return url.replace(/(\?.*)?(#.*)?$/, new_querystring + window.location.hash); } /* Helper for pick group/type button handlers - toggles the appropriate parameter entry * elt - the jquery element that was clicked */ - function handle_pick_button (elt) { + function handle_pick_button(elt) { var fp = get_filter_params(parse_query_params(window.location.search)); var item = get_item(elt); @@ -232,17 +240,17 @@ window.agenda_filter_for_testing; // methods to be accessed for automated testin * an area will enable all items in the row as one would expect. */ if (just_showed_item) { var children = get_items_with_keyword(item); - $.each(children, function(index, child) { + $.each(children, function (index, child) { remove_list_item(fp.show, child); remove_list_item(fp.hide, child); }); } - + // If the show list is empty, clear the hide list because there is nothing to hide if (fp.show.length === 0) { fp.hide = []; } - + return fp; } @@ -251,23 +259,13 @@ window.agenda_filter_for_testing; // methods to be accessed for automated testin } function register_handlers() { - $('.pickview').on("click", function () { - if (is_disabled($(this))) { return; } - var fp = handle_pick_button($(this)); - update_filters(fp); - }); - } - - /** - * Read options from the template - */ - function read_template_options() { - const opts_elt = document.getElementById('agenda-filter-options'); - opts.keys().forEach((opt) => { - if (opt in opts_elt.dataset) { - opts[opt] = opts_elt.dataset[opt]; - } - }); + $('.pickview') + .on("click", function () { + console.log("pickview"); + if (is_disabled($(this))) { return; } + var fp = handle_pick_button($(this)); + update_filters(fp); + }); } /* Entry point to filtering code when page loads @@ -275,17 +273,18 @@ window.agenda_filter_for_testing; // methods to be accessed for automated testin * This must be called if you are using the HTML template to provide a customization * button UI. Do not call if you only want to use the parameter parsing routines. */ - function enable () { + function enable() { // ready handler fires immediately if document is already "ready" - $(document).ready(function () { - register_handlers(); - update_view(); - }) + $(document) + .ready(function () { + register_handlers(); + update_view(); + }) } // utility method - filter a jquery set to those matching a keyword function rows_matching_filter_keyword(rows, kw) { - return rows.filter(function(index, element) { + return rows.filter(function (index, element) { var row_kws = get_keywords(element); return keyword_match(row_kws, [kw.toLowerCase()]); }); @@ -305,6 +304,6 @@ window.agenda_filter_for_testing; // methods to be accessed for automated testin keyword_match: keyword_match, parse_query_params: parse_query_params, rows_matching_filter_keyword: rows_matching_filter_keyword, - set_update_callback: function (cb) {opts.updateCallback = cb} + set_update_callback: function (cb) { opts.updateCallback = cb } }; })(); \ No newline at end of file diff --git a/ietf/static/js/agenda_personalize.js b/ietf/static/js/agenda_personalize.js new file mode 100644 index 000000000..edb2787f3 --- /dev/null +++ b/ietf/static/js/agenda_personalize.js @@ -0,0 +1,41 @@ +// Copyright The IETF Trust 2021, All Rights Reserved + +/** + * Agenda personalization JS methods + * + * Requires agenda_timezone.js and timezone.js be included. + */ +'use strict'; + +/** + * Update the checkbox state to match the filter parameters + */ +function updateAgendaCheckboxes(filter_params) { + var selection_inputs = document.getElementsByName('selected-sessions'); + selection_inputs.forEach((inp) => { + const item_keywords = inp.dataset.filterKeywords.toLowerCase() + .split(','); + if ( + agenda_filter.keyword_match(item_keywords, filter_params.show) && + !agenda_filter.keyword_match(item_keywords, filter_params.hide) + ) { + inp.checked = true; + } else { + inp.checked = false; + } + }); +} + +window.handleFilterParamUpdate = function (filter_params) { + updateAgendaCheckboxes(filter_params); +}; + +window.handleTableClick = function (event) { + if (event.target.name === 'selected-sessions') { + // hide the tooltip after clicking on a checkbox + const jqElt = jQuery(event.target); + if (jqElt.tooltip) { + jqElt.tooltip('hide'); + } + } +}; \ No newline at end of file diff --git a/ietf/static/js/agenda_timezone.js b/ietf/static/js/agenda_timezone.js index bef58d6cb..5b8907c83 100644 --- a/ietf/static/js/agenda_timezone.js +++ b/ietf/static/js/agenda_timezone.js @@ -13,12 +13,12 @@ var local_timezone = moment.tz.guess(); // get_current_tz_cb must be overwritten using set_current_tz_cb function get_current_tz_cb() { - throw new Error('Tried to get current timezone before callback registered. Use set_current_tz_cb().') + throw new Error('Tried to get current timezone before callback registered. Use set_current_tz_cb().'); }; // Initialize moments window.initialize_moments = function () { - var times = $('div.time') + var times = $('div.time'); $.each(times, function (i, item) { item.start_ts = moment.unix(this.getAttribute("data-start-time")) .utc(); @@ -33,7 +33,7 @@ window.initialize_moments = function () { item.format = +this.getAttribute("format"); } }); - var times = $('[data-slot-start-ts]') + times = $('[data-slot-start-ts]'); $.each(times, function (i, item) { item.slot_start_ts = moment.unix(this.getAttribute("data-slot-start-ts")) .utc(); @@ -205,7 +205,7 @@ window.update_times = function (newtz) { }); update_tooltips_all(); update_clock(); -} +}; // Highlight ongoing based on the current time window.highlight_ongoing = function () { @@ -213,7 +213,7 @@ window.highlight_ongoing = function () { .remove("#now"); $('.table-warning') .removeClass("table-warning"); - var agenda_rows = $('[data-slot-start-ts]') + var agenda_rows = $('[data-slot-start-ts]'); agenda_rows = agenda_rows.filter(function () { return moment() .isBetween(this.slot_start_ts, this.slot_end_ts); @@ -245,13 +245,13 @@ window.update_tooltips_all = function () { $(this) .html(format_tooltip_table(this.start_ts, this.end_ts)); }); -} +}; // Update clock window.update_clock = function () { $('#current-time') .html(format_time(moment(), get_current_tz_cb(), 0)); -} +}; $.urlParam = function (name) { var results = new RegExp('[\?&]' + name + '=([^&#]*)') @@ -261,7 +261,7 @@ $.urlParam = function (name) { } else { return results[1] || 0; } -} +}; window.init_timers = function () { var fast_timer = 60000 / (speedup > 600 ? 600 : speedup); @@ -271,9 +271,9 @@ window.init_timers = function () { 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 window.set_current_tz_cb = function (fn) { get_current_tz_cb = fn; -} \ No newline at end of file +}; \ No newline at end of file diff --git a/ietf/static/js/timezone.js b/ietf/static/js/timezone.js index fda6f7f47..5f77dccf3 100644 --- a/ietf/static/js/timezone.js +++ b/ietf/static/js/timezone.js @@ -17,18 +17,19 @@ window.ietf_timezone; // public interface var current_timezone; // Select timezone to use. Arg is name of a timezone or 'local' to guess local tz. - function use_timezone (newtz) { + function use_timezone(newtz) { // Guess local timezone if necessary if (newtz.toLowerCase() === 'local') { - newtz = moment.tz.guess() + newtz = moment.tz.guess(); } if (current_timezone !== newtz) { - current_timezone = newtz + current_timezone = newtz; // Update values of tz-select inputs but do not trigger change event - $('select.tz-select').val(newtz) + $('select.tz-select') + .val(newtz); if (timezone_change_callback) { - timezone_change_callback(newtz) + timezone_change_callback(newtz); } } } @@ -38,37 +39,40 @@ window.ietf_timezone; // public interface * 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 timezone_init(current) { + var tz_names = moment.tz.names(); + var select = $('select.tz-select'); - select.empty() + select.empty(); $.each(tz_names, function (i, item) { if (current === item) { select.append($('