├── README.md ├── ghc.js └── scheduler.PNG /README.md: -------------------------------------------------------------------------------- 1 | # ghc-scheduler 2 | Helps with visualising the schedule for GHC 2018. 3 | 4 | 5 | 6 | ## Goals 7 | * Easy to install. 8 | * Make sure we are always operating on current data - If the schedule changes, this script will pick up the changes. 9 | 10 | ## Install 11 | 1. Go to the [raw js](https://raw.githubusercontent.com/charlottetan/ghc-scheduler/master/ghc.js) and copy it (ctrl-c). 12 | 2. Go to the [GHC sessions page](http://www.cvent.com/events/grace-hopper-celebration/agenda-6083a0df738343e2ad8b262237e56423.aspx?p=13). 13 | 3. Open Developer Tools and paste the js into the console. 14 | 15 | ## Usage 16 | * **Dates:** Click to toggle between the dates 17 | * **Audience | Track | Off:** Choose to colour the sessions by audience, track, or turn it off. 18 | * **Show all sessions:** Toggle to exclude sessions outside of the main session block. List of excluded sessions is in the code. 19 | * Schedule controls: 20 | * **Show my schedule:** While viewing sessions, click on a session to add it to your schedule, then click this button to show only the sessions that you have added. Note: This disables the *Show all sessions* filter. 21 | * **Clear:** Remove all sessions that were in your schedule. 22 | * **Export:** Export schedule as a `|` delimited list of titles. 23 | * **Import:** Import a `|` delimited list of titles as a schedule and load it up for viewing. 24 | * Textbox: For grabbing exported schedule and importing schedule. 25 | * **Audience/Track colour blocks:** These can be toggled to show sessions that match a particular audience or track. 26 | * **Schedule container**: Has two horizontal scrollbars to help with the scrolling. 27 | * **Each session**: Hovering over a session brings up the description of the session. Clicking on a session will add it to your schedule which you can then view and edit using the schedule controls. 28 | -------------------------------------------------------------------------------- /ghc.js: -------------------------------------------------------------------------------- 1 | // For: http://www.cvent.com/events/grace-hopper-celebration/agenda-6083a0df738343e2ad8b262237e56423.aspx?p=13 2 | 3 | function parseGhcSessions() { 4 | var tracksIdToTextMap = {}; 5 | var tracksTextToIdMap = {}; 6 | var audienceIdToTextMap = {}; 7 | var audienceTextToIdMap = {}; 8 | var focusAreaSet = new Set(); 9 | var ghcDays = new Set(); 10 | 11 | var sessionContainerDivs = jQuery(".reg-matrix-header-container"); 12 | 13 | var sesArr = []; 14 | for (var i = 0; i < sessionContainerDivs.length; i++) { 15 | var oneSes = {}; 16 | oneSes.title = sessionContainerDivs[i].childNodes[1].childNodes[1].innerHTML.trim(); 17 | 18 | var sessionContentDiv = sessionContainerDivs[i].childNodes[3]; 19 | var sessionInfoDiv = sessionContentDiv.childNodes[1].childNodes[1]; 20 | 21 | var infoDivChildCounter = 0; 22 | 23 | if (sessionInfoDiv.childNodes[infoDivChildCounter].childNodes[0].nodeValue == "Audience Level: ") 24 | { 25 | oneSes.audience = sessionInfoDiv.childNodes[infoDivChildCounter].childNodes[1].innerHTML; 26 | var audienceKey = oneSes.audience.replace(/[^a-z0-9]/gi, ''); 27 | audienceIdToTextMap[audienceKey] = oneSes.audience; 28 | audienceTextToIdMap[oneSes.audience] = audienceKey; 29 | 30 | infoDivChildCounter += 2; 31 | } 32 | 33 | if (sessionInfoDiv.childNodes[infoDivChildCounter].childNodes[0].nodeValue == "Track: ") 34 | { 35 | oneSes.track = sessionInfoDiv.childNodes[infoDivChildCounter].childNodes[1].innerHTML; 36 | var trackKey = oneSes.track.replace(/[^a-z0-9]/gi, '') + "Tr"; 37 | tracksIdToTextMap[trackKey] = oneSes.track; 38 | tracksTextToIdMap[oneSes.track] = trackKey; 39 | 40 | infoDivChildCounter += 2; 41 | } 42 | 43 | if (sessionInfoDiv.childNodes[infoDivChildCounter].childNodes[0].nodeValue == "Focus Area: ") 44 | { 45 | oneSes.focusArea = sessionInfoDiv.childNodes[infoDivChildCounter].childNodes[1].innerHTML; 46 | focusAreaSet.add(oneSes.focusArea); 47 | infoDivChildCounter += 2; 48 | } 49 | 50 | oneSes.startDate = sessionInfoDiv.childNodes[infoDivChildCounter].childNodes[1].innerHTML; 51 | ghcDays.add(oneSes.startDate); 52 | infoDivChildCounter += 2; 53 | 54 | oneSes.timeStart = sessionInfoDiv.childNodes[infoDivChildCounter].childNodes[1].innerHTML; 55 | oneSes.timestampStart = Date.parse(oneSes.startDate + ' ' + oneSes.timeStart); 56 | infoDivChildCounter += 2; 57 | 58 | oneSes.timeEnd = sessionInfoDiv.childNodes[infoDivChildCounter].childNodes[1].innerHTML; 59 | oneSes.timestampEnd = Date.parse(oneSes.startDate + ' ' + oneSes.timeEnd); 60 | infoDivChildCounter += 2; 61 | 62 | if (infoDivChildCounter < sessionInfoDiv.childNodes.length && 63 | sessionInfoDiv.childNodes[infoDivChildCounter].childNodes[0].nodeValue == "Location: ") 64 | { 65 | oneSes.location = sessionInfoDiv.childNodes[infoDivChildCounter].childNodes[1].innerHTML; 66 | infoDivChildCounter += 2; 67 | } 68 | 69 | if (infoDivChildCounter <= sessionInfoDiv.childNodes.length) 70 | { 71 | var speakerDivList = sessionInfoDiv.childNodes[infoDivChildCounter].childNodes; 72 | oneSes.speakers = []; 73 | 74 | for (var j = 0; j < speakerDivList.length; j+=2) { 75 | oneSes.speakers.push(speakerDivList[j].childNodes[0].nodeValue); 76 | } 77 | 78 | infoDivChildCounter += 2; 79 | } 80 | 81 | oneSes.desc = sessionContentDiv.childNodes[3].innerHTML.trim(); 82 | 83 | sesArr.push(oneSes); 84 | } 85 | 86 | localStorage.setItem('ghcSessionArray', JSON.stringify(sesArr)); 87 | localStorage.setItem('ghcDays', [...ghcDays].join('|')); 88 | localStorage.setItem('tracksIdToTextMap', JSON.stringify(tracksIdToTextMap)); 89 | localStorage.setItem('tracksTextToIdMap', JSON.stringify(tracksTextToIdMap)); 90 | localStorage.setItem('audienceIdToTextMap', JSON.stringify(audienceIdToTextMap)); 91 | localStorage.setItem('audienceTextToIdMap', JSON.stringify(audienceTextToIdMap)); 92 | localStorage.setItem('focusAreaSet', [...focusAreaSet].join('|')); 93 | 94 | if (!localStorage.getItem('includedTracks')) { 95 | var includedTracks = Object.keys(tracksIdToTextMap); 96 | localStorage.setItem('includedTracks', [...includedTracks].join('|')); 97 | } 98 | 99 | if (!localStorage.getItem('includedAudiences')) { 100 | var includedAudiences = Object.keys(audienceIdToTextMap); 101 | localStorage.setItem('includedAudiences', [...includedAudiences].join('|')); 102 | } 103 | } 104 | 105 | function slotSessions() { 106 | // for some reason this double parse is required for arrays of objects in localStorage 107 | var sessionArray = JSON.parse(JSON.parse(localStorage.getItem('ghcSessionArray'))); 108 | var sList = {}; 109 | var mList = {}; 110 | var lList = {}; 111 | 112 | // sList: 0-2hrs 113 | // mList: 2.1-3 hrs 114 | // lList: more than 3 hours 115 | const sLimit = 1000*60*60*2; 116 | const mLimit = 1000*60*60*3; 117 | 118 | const partyName = "GHC Evening Celebration"; 119 | 120 | var dayArray = localStorage.getItem('ghcDays').split('|'); 121 | 122 | // get included list 123 | var includedTracks = localStorage.getItem('includedTracks').split('|'); 124 | var includedAudiences = localStorage.getItem('includedAudiences').split('|'); 125 | 126 | var excludedSessions = [ 127 | 'Community Volunteer Orientation', 128 | 'Wednesday Keynote: Padmasree Warrior and Jessica Matthews', 129 | 'Mentoring Circles', 130 | 'CR111: Speed Mentoring', 131 | 'Poster Session 1', 132 | 'Poster Session 2', 133 | 'Poster Session 3', 134 | 'Poster Session 4', 135 | 'Poster Session 5', 136 | 'Faculty Lounge', 137 | 'Speaker Lounge', 138 | 'Student Lounge', 139 | 'Gallery: Our Time', 140 | 'GHC Expo', 141 | 'Open Source Day Code-a-thon for Humanity', 142 | 'AnitaB.org PitcHER', 143 | 'Friday Keynote: Justine Cassell and PitcHER Winners', 144 | 'GHC Evening Celebration', 145 | 'Session Chair Orientation (Invite Only)', 146 | 'Speaker\'s Reception (Invite Only)', 147 | 'AnitaB.org Community Leadership Dinner (Invite Only)', 148 | 'Hoppers Orientation (Invite Only)', 149 | 'BRAID Welcome Reception (Invite Only)', 150 | 'SMASH Academy Networking Reception (Invite Only)', 151 | 'AnitaB.org Partner Meeting (Invite Only)', 152 | 'GHC Scholar Networking Reception (Invite Only)', 153 | 'Technical Executive Forum (Invite Only)', 154 | 'Senior Women\'s Program (Invite Only)', 155 | 'ACM-W Luncheon (Invite Only)', 156 | 'Committee\'s Lunch (Invite Only)', 157 | ]; 158 | 159 | if (localStorage.getItem('showAllSessions') === 'true') { 160 | excludedSessions = []; 161 | } 162 | 163 | //showSchedule overrides excludedSessions 164 | var mySchedule = []; 165 | var showingSchedule = localStorage.getItem('showSchedule') === 'true'; 166 | if (showingSchedule) { 167 | var storageSched = localStorage.getItem('mySchedule'); 168 | if (storageSched) { 169 | mySchedule = storageSched.split('|'); 170 | } 171 | excludedSessions = []; 172 | } 173 | 174 | for (var i = 0; i < sessionArray.length; i++) { 175 | var sesDay = sessionArray[i].startDate; 176 | 177 | var aDay = []; 178 | var aTrack = {}; 179 | var trackSessions = []; 180 | var trackId = 0; 181 | var days; 182 | 183 | // only proceed to slot if included 184 | var trackKey = undefined, audienceKey = undefined; 185 | if (sessionArray[i].track) { 186 | trackKey = sessionArray[i].track.replace(/[^a-z0-9]/gi, '') + "Tr"; 187 | } 188 | if (sessionArray[i].audience) { 189 | audienceKey = sessionArray[i].audience.replace(/[^a-z0-9]/gi, ''); 190 | } 191 | var trackKeyIncluded = !trackKey || (trackKey && includedTracks.includes(trackKey)); 192 | var audienceKeyIncluded = !audienceKey || (audienceKey && includedAudiences.includes(audienceKey)); 193 | var isNotAnExcludedSession = (!showingSchedule && !excludedSessions.includes(sessionArray[i].title)) || 194 | (showingSchedule && mySchedule.includes(sessionArray[i].title)); 195 | 196 | if (trackKeyIncluded && audienceKeyIncluded && isNotAnExcludedSession) { 197 | // get tshirt size 198 | var duration = sessionArray[i].timestampEnd - sessionArray[i].timestampStart; 199 | switch (true) { 200 | case (duration <= sLimit): 201 | days = sList; 202 | break; 203 | case (duration <= mLimit): 204 | days = mList; 205 | break; 206 | default: 207 | days = lList; 208 | } 209 | 210 | // Exception for GHC Evening Celebration 211 | if (sessionArray[i].title == partyName) { 212 | days = sList; 213 | } 214 | 215 | if (days[sesDay]) { 216 | aDay = days[sesDay]; 217 | 218 | var foundTrack = false; 219 | // find a suitable track 220 | for (var j = 0; j < aDay.length; j++) { 221 | var candidateTrack = aDay[j]; 222 | if (candidateTrack.latest < sessionArray[i].timestampStart) { 223 | // we found our track 224 | foundTrack = true; 225 | trackId = j; 226 | aTrack = candidateTrack; 227 | trackSessions = candidateTrack.sessions; 228 | break; 229 | } 230 | } 231 | 232 | // if none found, add to end 233 | if (!foundTrack) { 234 | trackId = aDay.length; 235 | } 236 | } 237 | 238 | // add session to track 239 | trackSessions.push(sessionArray[i]); 240 | aTrack.latest = sessionArray[i].timestampEnd; 241 | aTrack.sessions = trackSessions; 242 | 243 | // add track to day 244 | aDay[trackId] = aTrack; 245 | 246 | // add day to days 247 | // Exception for GHC Evening Celebration 248 | if (sessionArray[i].title == partyName) { 249 | sList[sesDay] = aDay; 250 | } else { 251 | switch (true) { 252 | case (duration <= sLimit): 253 | sList[sesDay] = aDay; 254 | break; 255 | case (duration <= mLimit): 256 | mList[sesDay] = aDay; 257 | break; 258 | default: 259 | lList[sesDay] = aDay; 260 | } 261 | } 262 | } 263 | } 264 | 265 | // combine days 266 | var schedule = {}; 267 | for (let dayItem of dayArray) { 268 | schedule[dayItem] = []; 269 | if (sList[dayItem]) { 270 | schedule[dayItem].push(...sList[dayItem]); 271 | } 272 | if (mList[dayItem]) { 273 | schedule[dayItem].push(...mList[dayItem]); 274 | } 275 | if (lList[dayItem]) { 276 | schedule[dayItem].push(...lList[dayItem]); 277 | } 278 | }; 279 | 280 | return schedule; 281 | } 282 | 283 | function toggleTabs(eventObj) { 284 | var timestampButton = eventObj.target.id; 285 | var timestamp = eventObj.target.id.replace("button", ""); 286 | 287 | var newWidth = jQuery('#' + timestamp).width(); 288 | jQuery('#dayDummyContent').width(newWidth); 289 | 290 | jQuery('.dayDiv').hide(); 291 | jQuery('#dayTabs button').removeClass('selected'); 292 | 293 | jQuery('#' + timestampButton).addClass('selected'); 294 | jQuery('#' + timestamp).css('display', 'flex'); 295 | 296 | var bufferSoThatDivWillNotScroll = 250; 297 | jQuery('#dayContent').height(jQuery(`#${timestamp}`)[0].scrollHeight+bufferSoThatDivWillNotScroll + 'px'); 298 | 299 | localStorage.setItem('selectedTimestampButton', timestampButton); 300 | } 301 | 302 | function toggleAudienceTrackOff(eventObj) { 303 | jQuery(".toggleOptions").removeClass("selected"); 304 | 305 | switch (eventObj.target.id) { 306 | case "toggleAudienceDiv": 307 | jQuery("#toggleAudienceDiv").addClass("selected"); 308 | 309 | jQuery("#audienceLegend").show(); 310 | jQuery("#trackLegend").hide(); 311 | jQuery(".sessionDiv").addClass("audienceColours").removeClass("trackColours"); 312 | break; 313 | case "toggleTrackDiv": 314 | jQuery("#toggleTrackDiv").addClass("selected"); 315 | 316 | jQuery("#audienceLegend").hide(); 317 | jQuery("#trackLegend").show(); 318 | jQuery(".sessionDiv").removeClass("audienceColours").addClass("trackColours"); 319 | break; 320 | default: 321 | jQuery("#toggleOffDiv").addClass("selected"); 322 | 323 | jQuery("#audienceLegend").hide(); 324 | jQuery("#trackLegend").hide(); 325 | jQuery(".sessionDiv").removeClass("audienceColours").removeClass("trackColours"); 326 | } 327 | 328 | localStorage.setItem('selectedColouring', eventObj.target.id); 329 | } 330 | 331 | function toggleAudienceCategories(eventObj) { 332 | // update localstorage 333 | var included = localStorage.getItem('includedAudiences').split('|'); 334 | if (included.includes(eventObj.target.id)) { 335 | var index = included.indexOf(eventObj.target.id); 336 | included.splice(index, 1); 337 | } else { 338 | included.push(eventObj.target.id); 339 | } 340 | localStorage.setItem('includedAudiences', [...included].join('|')); 341 | 342 | jQuery(`#${eventObj.target.id}`).toggleClass("selected"); 343 | 344 | reflow(); 345 | } 346 | 347 | function toggleTrackCategories(eventObj) { 348 | // update localstorage 349 | var included = localStorage.getItem('includedTracks').split('|'); 350 | if (included.includes(eventObj.target.id)) { 351 | var index = included.indexOf(eventObj.target.id); 352 | included.splice(index, 1); 353 | } else { 354 | included.push(eventObj.target.id); 355 | } 356 | localStorage.setItem('includedTracks', [...included].join('|')); 357 | 358 | jQuery(`#${eventObj.target.id}`).toggleClass("selected"); 359 | 360 | reflow(); 361 | } 362 | 363 | function toggleMainSessions(eventObj) { 364 | var jQele = jQuery(`#${eventObj.target.id}`); 365 | 366 | if (!jQele.hasClass('disabled')) { 367 | var newValue = !(localStorage.getItem('showAllSessions') === 'true'); 368 | localStorage.setItem('showAllSessions', newValue); 369 | 370 | if ((newValue && !jQele.hasClass("selected")) || 371 | (!newValue && jQele.hasClass("selected"))) { 372 | jQele.toggleClass("selected"); 373 | } 374 | 375 | reflow(); 376 | } 377 | } 378 | 379 | function sessionClick(eventObj) { 380 | var title = ""; 381 | if (eventObj.target.tagName === "SPAN") { 382 | if (eventObj.target.className === "title") { 383 | title = eventObj.target.innerHTML; 384 | } else { 385 | title = eventObj.target.siblings()[1].innerHTML; 386 | } 387 | } else { 388 | title = eventObj.target.childNodes[2].innerHTML; 389 | } 390 | 391 | var mySched = localStorage.getItem('mySchedule'); 392 | if (mySched) { 393 | mySched += '|' + title; 394 | } else { 395 | mySched = title; 396 | } 397 | 398 | localStorage.setItem('mySchedule', mySched); 399 | } 400 | 401 | function toggleShowSchedule(eventObj) { 402 | var jQele = jQuery(`#${eventObj.target.id}`); 403 | var newValue = !(localStorage.getItem('showSchedule') === 'true'); 404 | localStorage.setItem('showSchedule', newValue); 405 | 406 | // update controls 407 | if ((newValue && !jQele.hasClass("selected")) || 408 | (!newValue && jQele.hasClass("selected"))) { 409 | jQele.toggleClass("selected"); 410 | } 411 | 412 | if (newValue) { 413 | // disable mainSessionsFilter 414 | jQuery("#mainSessionsFilter").removeClass("selected"); 415 | jQuery("#mainSessionsFilter").addClass("disabled"); 416 | } else { 417 | // reenable 418 | jQuery("#mainSessionsFilter").removeClass("disabled"); 419 | 420 | var showingAll = (localStorage.getItem('showAllSessions') === 'true'); 421 | localStorage.setItem('showAllSessions', !showingAll); 422 | jQuery("#mainSessionsFilter").click(); 423 | } 424 | 425 | reflow(); 426 | } 427 | 428 | function clearSchedule() { 429 | localStorage.removeItem('mySchedule'); 430 | 431 | // update controls 432 | jQuery("#scheduleInput").val(''); 433 | jQuery("#mainSessionsFilter").removeClass("disabled"); 434 | 435 | // set it up so that we can toggle the showSchedule button 436 | localStorage.setItem('showSchedule', true); 437 | jQuery("#scheduleShowDiv").click(); 438 | 439 | // no need reflow(), toggleShowSchedule will do it 440 | } 441 | 442 | function exportSchedule() { 443 | jQuery("#scheduleInput").val(localStorage.getItem('mySchedule')); 444 | } 445 | 446 | function importSchedule() { 447 | localStorage.setItem('mySchedule', jQuery("#scheduleInput").val()); 448 | localStorage.setItem('showSchedule', 'false'); 449 | jQuery("#scheduleShowDiv").click(); 450 | } 451 | 452 | function createDiv() { 453 | jQuery('#schedCss').remove(); 454 | jQuery('#output').remove(); 455 | 456 | const trackColours = [ 457 | '#bf968f', '#f29979', '#7f5940', '#e5b073', '#fff780', 458 | '#f2eeb6', '#798060', '#8fcc66', '#53a674', '#79f2da', 459 | '#53a0a6', '#80d5ff', '#7c98a6', '#80a2ff', '#99a0cc', 460 | '#8959b3', '#f279da', '#80406a', '#e6accb', '#ff8091', 461 | '#994d57' 462 | ]; 463 | 464 | const audienceColours = [ 465 | '#d97b6c', '#807560', '#ffe680', '#73e6a1', '#bff2ff', 466 | '#80c4ff', '#46628c', '#9c66cc', '#f2b6de', '#804059' 467 | ]; 468 | 469 | // hard to see 470 | var flipColors = [ 471 | '#80406a', '#994d57', '#46628c', '#804059', '#807560' 472 | ]; 473 | 474 | var dayLegend = jQuery('
', {id: 'dayLegend'}); 475 | 476 | var dayControls = jQuery('
', {id: 'dayControls'}); 477 | 478 | // colour controls 479 | var toggleColoursDiv = jQuery('
', {id: 'toggleColoursDiv'}); 480 | var toggleAudienceDiv = jQuery('
', {id: 'toggleAudienceDiv', class: 'toggleOptions', text: 'Audience'}).click(toggleAudienceTrackOff); 481 | var toggleTrackDiv = jQuery('
', {id: 'toggleTrackDiv', class: 'toggleOptions', text: 'Track'}).click(toggleAudienceTrackOff); 482 | var toggleOffDiv = jQuery('
', {id: 'toggleOffDiv', class: 'toggleOptions', text: 'Off'}).click(toggleAudienceTrackOff); 483 | toggleColoursDiv.append(toggleAudienceDiv).append(toggleTrackDiv).append(toggleOffDiv); 484 | 485 | // show only main sessions 486 | var mainSessionsFilter = jQuery('
', { 487 | id: 'mainSessionsFilter', 488 | text: 'Show all sessions', 489 | title: 'Toggle to exclude sessions that occur outside of the main session block' 490 | }).click(toggleMainSessions); 491 | 492 | if (localStorage.getItem('showSchedule') === 'true') { 493 | mainSessionsFilter.addClass("disabled"); 494 | } else if (localStorage.getItem('showAllSessions') === 'true') { 495 | mainSessionsFilter.addClass("selected"); 496 | } 497 | 498 | // mySchedule controls 499 | var scheduleControlsDiv = jQuery('
', {id: 'scheduleControlsDiv'}); 500 | var scheduleShowDiv = jQuery('
', {id: 'scheduleShowDiv', class: 'scheduleControls', text: 'Show my schedule'}).click(toggleShowSchedule); 501 | var schduleClearDiv = jQuery('
', {id: 'schduleClearDiv', class: 'scheduleControls', text: 'Clear'}).click(clearSchedule); 502 | var schduleExportDiv = jQuery('
', {id: 'schduleExportDiv', class: 'scheduleControls', text: 'Export'}).click(exportSchedule); 503 | var schduleImportDiv = jQuery('
', {id: 'schduleImportDiv', class: 'scheduleControls', text: 'Import'}).click(importSchedule); 504 | var scheduleInput = jQuery('', {id: 'scheduleInput', type: 'text', size: '100'}); 505 | scheduleControlsDiv.append(scheduleShowDiv).append(schduleClearDiv).append(schduleExportDiv).append(schduleImportDiv).append(scheduleInput); 506 | 507 | if (localStorage.getItem('showSchedule') === 'true') { 508 | scheduleShowDiv.addClass("selected"); 509 | } 510 | 511 | // legends 512 | var trackLegend = jQuery('
', {id: 'trackLegend'}); 513 | var trackCss = ''; 514 | var trackColourCounter = 0; 515 | var tracksIdToTextMap = JSON.parse(localStorage.getItem('tracksIdToTextMap')); 516 | var trackKeys = Object.keys(tracksIdToTextMap); 517 | trackKeys.sort(); 518 | var includedTracks = localStorage.getItem('includedTracks').split('|'); 519 | for (var trackKey of trackKeys) { 520 | trackCss += `.sessionDiv.trackColours.${trackKey}, #trackLegend .trackColours.${trackKey}.selected {background-color: ${trackColours[trackColourCounter]};}`; 521 | trackColourCounter++; 522 | 523 | var swatch = jQuery('
', {id: trackKey, class: 'trackColours ' + trackKey}).text(tracksIdToTextMap[trackKey]).click(toggleTrackCategories); 524 | if (includedTracks.includes(trackKey)) { 525 | swatch.addClass("selected"); 526 | } 527 | trackLegend.append(swatch); 528 | }; 529 | 530 | var audienceLegend = jQuery('
', {id: 'audienceLegend'}); 531 | var audienceCss = ''; 532 | var audienceColourCounter = 0; 533 | 534 | //manual sort 535 | var audienceKeys = [ 536 | "Student", "Faculty", 537 | "EarlyCareer", "MidCareer", "SeniorExecutive", 538 | "BeginnerTech", "IntermediateTech", "AdvancedTech", 539 | "All", "InviteOnly"]; 540 | 541 | var audienceIdToTextMap = JSON.parse(localStorage.getItem('audienceIdToTextMap')); 542 | var dataAudienceKeys = Object.keys(audienceIdToTextMap); 543 | 544 | //sanity check to make sure the keys are what we expect, otherwise just use unsorted array 545 | var manualAudienceStr = audienceKeys.concat().sort().join(','); 546 | var dataAudienceStr = dataAudienceKeys.concat().sort().join(','); 547 | if (manualAudienceStr !== dataAudienceStr) { 548 | audienceKeys = dataAudienceKeys; 549 | } 550 | 551 | var includedAudiences = localStorage.getItem('includedAudiences').split('|'); 552 | for (var audienceKey of audienceKeys) { 553 | audienceCss += `.sessionDiv.audienceColours.${audienceKey}, #audienceLegend .audienceColours.${audienceKey}.selected {background-color: ${audienceColours[audienceColourCounter]};}`; 554 | audienceColourCounter++; 555 | 556 | var swatch = jQuery('
', {id: audienceKey, class: 'audienceColours ' + audienceKey}).text(audienceIdToTextMap[audienceKey]).click(toggleAudienceCategories); 557 | if (includedAudiences.includes(audienceKey)) { 558 | swatch.addClass("selected"); 559 | } 560 | audienceLegend.append(swatch); 561 | }; 562 | 563 | dayControls.append(toggleColoursDiv); 564 | dayControls.append(mainSessionsFilter); 565 | dayControls.append(scheduleControlsDiv); 566 | dayLegend.append(trackLegend); 567 | dayLegend.append(audienceLegend); 568 | 569 | // colours: http://phrogz.net/css/distinct-colors.html, sat 50-25, val 570 | var cssStylesHtml = ` 571 | #output { 572 | font-family: arial, sans-serif; 573 | } 574 | 575 | #dayContent { 576 | display: flex; 577 | flex-direction: column; 578 | overflow: auto; 579 | } 580 | 581 | #dayDummyWrapper { 582 | overflow-x: scroll; 583 | overflow-y:hidden; 584 | height: 20px; 585 | } 586 | 587 | #dayDummyContent { 588 | height: 20px; 589 | } 590 | 591 | .dayDiv { 592 | display: flex; 593 | } 594 | 595 | .trackDiv { 596 | display: flex; 597 | flex-direction: column; 598 | min-width: 80px; 599 | } 600 | 601 | .sessionDiv { 602 | border: solid black 1px; 603 | } 604 | 605 | .sessionDiv > span { 606 | font-size: 1.25vh; 607 | } 608 | 609 | #dayTabs button { 610 | background-color: #54BCEB; 611 | color: #fff; 612 | border: none; 613 | outline: none; 614 | cursor: pointer; 615 | padding: 10px 12px; 616 | transition: 0.3s; 617 | } 618 | 619 | #dayTabs button:hover, #dayTabs button.selected { 620 | background-color: #005c88; 621 | } 622 | 623 | #toggleColoursDiv { 624 | display: flex; 625 | } 626 | 627 | .toggleOptions, #mainSessionsFilter, .scheduleControls { 628 | background-color: #f1f1f1; 629 | border: 1px solid grey; 630 | padding: 6px 8px; 631 | cursor: pointer; 632 | transition: 0.3s; 633 | display: flex; 634 | flex-direction: column; 635 | justify-content: center; 636 | text-align: center; 637 | } 638 | 639 | .toggleOptions:hover, #mainSessionsFilter:hover, .scheduleControls:hover { 640 | background-color: #ddd; 641 | } 642 | 643 | .toggleOptions.selected, #mainSessionsFilter.selected, .scheduleControls.selected { 644 | background-color: #ababab; 645 | } 646 | 647 | .toggleOptions.disabled, #mainSessionsFilter.disabled, .scheduleControls.disabled, #mainSessionsFilter.disabled:hover { 648 | color: #ddd; 649 | border: 1px solid #ddd; 650 | background-color: #f1f1f1; 651 | cursor: default; 652 | } 653 | 654 | .toggleOptions:first-of-type, .scheduleControls:first-of-type { 655 | border-radius: 10px 0px 0px 10px; 656 | } 657 | 658 | .toggleOptions:last-of-type, .scheduleControls:last-of-type { 659 | border-radius: 0px 10px 10px 0px; 660 | } 661 | 662 | #scheduleInput { 663 | padding: 0; 664 | } 665 | 666 | #dayLegend, #dayLegend > div, #dayControls, #dayControls > div { 667 | display: flex; 668 | } 669 | 670 | #trackLegend, #audienceLegend { 671 | flex-wrap: wrap; 672 | width: 80%; 673 | } 674 | 675 | #dayLegend .trackColours, #dayLegend .audienceColours { 676 | padding: 6px; 677 | display: flex; 678 | flex-direction: column; 679 | justify-content: center; 680 | text-align: center; 681 | cursor: pointer; 682 | } 683 | 684 | #dayLegend div, #dayControls div, #dayControls input { 685 | font-size: 13px; 686 | } 687 | 688 | #mainSessionsFilter { 689 | border-radius: 10px; 690 | } 691 | 692 | ${trackCss} 693 | ${audienceCss} 694 | `; 695 | 696 | jQuery("