summaryrefslogtreecommitdiff
path: root/static/fullnarp.js
diff options
context:
space:
mode:
Diffstat (limited to 'static/fullnarp.js')
-rw-r--r--static/fullnarp.js429
1 files changed, 209 insertions, 220 deletions
diff --git a/static/fullnarp.js b/static/fullnarp.js
index 8b7d36d..3d60592 100644
--- a/static/fullnarp.js
+++ b/static/fullnarp.js
@@ -1,4 +1,9 @@
1let ws; // WebSocket instance 1let ws; // WebSocket instance
2let allrooms = ['1','2','3']
3let allminutes = ['00','05','10','15','20','25','30','35','40','45','50','55']
4let allhours = ['10','11','12','13','14','15','16','17','18','19','20','21','22','23','00','01','02'];
5let alldays = ['1','2','3','4'];
6let raw_votes;
2 7
3function toggle_grid(whichDay) { 8function toggle_grid(whichDay) {
4 var vclasses= [['in-list'], ['in-calendar', 'onlyday1'], ['in-calendar', 'onlyday2'], ['in-calendar', 'onlyday3'], 9 var vclasses= [['in-list'], ['in-calendar', 'onlyday1'], ['in-calendar', 'onlyday2'], ['in-calendar', 'onlyday3'],
@@ -8,6 +13,128 @@ function toggle_grid(whichDay) {
8 document.body.classList.add(...vclasses[whichDay]); 13 document.body.classList.add(...vclasses[whichDay]);
9} 14}
10 15
16function render_lectures(data) {
17 for (item of data) {
18 /* Take copy of hidden event template div and select them, if they're in
19 list of previous prereferences */
20 var t = document.getElementById('template').cloneNode(true);
21 var event_id = item.event_id.toString();
22
23 t.classList.add('event', 'duration_' + item.duration, 'lang_' + (item.language || 'en'));
24 t.setAttribute('event_id', event_id);
25 t.setAttribute('id', 'event_' + event_id);
26 t.setAttribute('fullnarp-duration', item.duration);
27 t.setAttribute('draggable', 'true');
28
29 /* Sort textual info into event div */
30 t.querySelector('.title').textContent = item.title;
31 t.querySelector('.speakers').textContent = item.speaker_names;
32 t.querySelector('.abstract').append(item.abstract);
33
34 /* Store speakers and their availabilities */
35 window.event_speakers[event_id] = item.speakers;
36 for (speaker of item.speakers) {
37 var have_avails = false;
38 for (avail of speaker.availabilities) {
39 if (avail.id ) {
40 have_avails = true;
41 break;
42 }
43 }
44 if (!have_avails)
45 t.classList.add('has_unavailable_speaker');
46 }
47
48 /* Make the event drag&droppable */
49 t.ondragstart = function( event, ui ) {
50 event.stopPropagation();
51
52 event.dataTransfer.setData('text/plain', this.id );
53 event.dataTransfer.dropEffect = 'move';
54 event.dataTransfer.effectAllowed = 'move';
55 event.target.classList.add('is-dragged');
56 }
57
58 /* While dragging make source element small enough to allow
59 dropping below its original area */
60 t.ondrag = function( event, ui ) {
61 event.stopPropagation();
62 event.target.classList.add('is-dragged');
63
64 /* When drag starts in list view, switch to calendar view */
65 if( document.body.classList.contains('in-list') ) {
66 toggle_grid(5);
67 document.body.classList.add('was-list');
68 }
69 if( document.body.classList.contains('in-drag') )
70 return;
71
72 document.body.classList.add('in-drag');
73 /* mark all possible drop points regarding to availability */
74 for (hour of allhours)
75 for (minute of allminutes)
76 for (day of alldays)
77 document.querySelectorAll('.grid.day_'+day+'.time_'+hour+minute).forEach(elem => elem.classList.toggle('possible', check_avail(event.target, day, hour+minute)));
78
79 }
80
81 t.ondragend = function( event, ui ) {
82 event.stopPropagation();
83
84 /* We removed in-list and the drop did not succeed. Go back to list view */
85 if (document.body.classList.contains('was-list'))
86 toggle_grid(0);
87
88 document.querySelectorAll('.over').forEach(elem => elem.classList.remove('over'));
89 document.querySelectorAll('.is-dragged').forEach(elem => elem.classList.remove('id-dragged'));
90 document.querySelectorAll('.possible').forEach(elem => elem.classList.remove('possible'));
91 document.body.classList.remove('in-drag', 'was-list');
92 }
93
94 /* start_time: 2014-12-29T21:15:00+01:00" */
95 var start_time = new Date(item.start_time);
96
97 var day = start_time.getDate()-26;
98 var hour = start_time.getHours();
99 var mins = start_time.getMinutes();
100
101 /* After midnight: sort into yesterday */
102 if( hour < 9 )
103 day--;
104
105 /* Fix up room for 38c3 */
106 room = (item.room_id || 'room_unknown').toString().replace('471','room1').replace('472','room2').replace('473','room3');
107
108 /* Apply attributes to sort events into calendar */
109 t.classList.add(room, 'day_' + day, 'time_' + (hour<10?'0':'') + hour + (mins<10?'0':'') + mins);
110 t.setAttribute('fullnarp-day', day);
111 t.setAttribute('fullnarp-time', (hour<10?'0':'') + hour + (mins<10?'0':'') + mins );
112 t.setAttribute('fullnarp-room', room.replace('room',''));
113
114 mark_avail(t);
115
116 t.onclick = function(event) {
117 _this = this;
118 document.body.classList.remove('in-drag');
119 if (document.body.classList.contains('correlate')) {
120 document.querySelectorAll('.selected').forEach(elem => elem.classList.remove('selected'));
121 document.querySelectorAll('.event').forEach(elem => mark_correlation(elem, _this));
122 }
123 _this.classList.toggle('selected');
124 document.querySelectorAll('.info').forEach(elem => elem.classList.add('hidden'));
125 event.stopPropagation();
126 }
127
128 /* Put new event into DOM tree. Track defaults to 'Other' */
129 var track = item.track_id.toString();
130 t.classList.add('track_' + track );
131 var d = document.getElementById(track);
132 if (!d)
133 d = document.getElementById('Other');
134 d.append(t);
135 }
136}
137
11function distribute_votes() { 138function distribute_votes() {
12 document.querySelectorAll('.event').forEach( function(element) { 139 document.querySelectorAll('.event').forEach( function(element) {
13 var eid = element.getAttribute('event_id'); 140 var eid = element.getAttribute('event_id');
@@ -37,8 +164,8 @@ function distribute_votes() {
37} 164}
38 165
39function corr_for_eventids(id1, id2) { 166function corr_for_eventids(id1, id2) {
40 var d = 0, c = 0, cd = 0, l = window.raw_votes.length; 167 var d = 0, c = 0, cd = 0, l = raw_votes.length;
41 for (item of window.raw_votes) { 168 for (item of raw_votes) {
42 var x = 0; 169 var x = 0;
43 if( item.indexOf(id1) > -1 ) { ++d; x++;} 170 if( item.indexOf(id1) > -1 ) { ++d; x++;}
44 if( item.indexOf(id2) > -1 ) { ++c; cd+=x; } 171 if( item.indexOf(id2) > -1 ) { ++c; cd+=x; }
@@ -52,42 +179,42 @@ function corr_for_eventids(id1, id2) {
52} 179}
53 180
54function show_all_correlates(el) { 181function show_all_correlates(el) {
55 /* First identify the room to see what other rooms to consider 182 /* First identify the room to see what other rooms to consider
56 correlates always grow from the top slot to the right, 183 correlates always grow from the top slot to the right,
57 unless there's an overlapping event to the left that starts earlier 184 unless there's an overlapping event to the left that starts earlier
58 */ 185 */
59 var event_room = el.getAttribute('fullnarp-room'); 186 var event_room = el.getAttribute('fullnarp-room');
60 var event_day = el.getAttribute('fullnarp-day'); 187 var event_day = el.getAttribute('fullnarp-day');
61 var event_time = el.getAttribute('fullnarp-time'); 188 var event_time = el.getAttribute('fullnarp-time');
62 189
63 if (!event_time) return; 190 if (!event_time) return;
64 191
65 var event_start; 192 var event_start;
66 try { event_start = time_to_mins(event_time); } catch(e) { return; } 193 try { event_start = time_to_mins(event_time); } catch(e) { return; }
67 var event_duration = el.getAttribute('fullnarp-duration') / 60; 194 var event_duration = el.getAttribute('fullnarp-duration') / 60;
68 195
69 /* Only test events to the right, if they start at the exact same time */ 196 /* Only test events to the right, if they start at the exact same time */
70 document.querySelectorAll('.event.day_'+event_day).forEach( function(check_el, index) { 197 document.querySelectorAll('.event.day_'+event_day).forEach( function(check_el, index) {
71 var check_room = check_el.getAttribute('fullnarp-room'); 198 var check_room = check_el.getAttribute('fullnarp-room');
72 if (event_room == check_room) return; 199 if (event_room == check_room) return;
73 200
74 var check_time = check_el.getAttribute('fullnarp-time'); 201 var check_time = check_el.getAttribute('fullnarp-time');
75 if (!check_time) return; 202 if (!check_time) return;
76 var check_start = time_to_mins(check_time); 203 var check_start = time_to_mins(check_time);
77 var check_duration = check_el.getAttribute('fullnarp-duration') / 60; 204 var check_duration = check_el.getAttribute('fullnarp-duration') / 60;
78 var dist = check_el.getAttribute('fullnarp-room') - event_room; 205 var dist = check_el.getAttribute('fullnarp-room') - event_room;
79 var overlap = check_start < event_start + event_duration && event_start < check_start + check_duration; 206 var overlap = check_start < event_start + event_duration && event_start < check_start + check_duration;
80 207
81 if (!overlap) return; 208 if (!overlap) return;
82 if (event_start == check_start && dist <= 0) return; 209 if (event_start == check_start && dist <= 0) return;
83 if (event_start < check_start) return; 210 if (event_start < check_start) return;
84 211
85 var corr = corr_for_eventids(el.getAttribute('event_id'), check_el.getAttribute('event_id')); 212 var corr = corr_for_eventids(el.getAttribute('event_id'), check_el.getAttribute('event_id'));
86 var dir = dist > 0 ? 'r' : 'l'; 213 var dir = dist > 0 ? 'r' : 'l';
87 var div = document.createElement('div'); 214 var div = document.createElement('div');
88 div.classList.add('corrweb', dir.repeat(Math.abs(dist)), 'day_' + event_day, 'room' + event_room, 'time_' + event_time, 'corr_d_' + corr); 215 div.classList.add('corrweb', dir.repeat(Math.abs(dist)), 'day_' + event_day, 'room' + event_room, 'time_' + event_time, 'corr_d_' + corr);
89 document.body.appendChild(div); 216 document.body.appendChild(div);
90 }) 217 });
91} 218}
92 219
93function display_correlation() { 220function display_correlation() {
@@ -104,8 +231,8 @@ function display_correlation() {
104function mark_correlation(dest, comp) { 231function mark_correlation(dest, comp) {
105 var id1 = dest.getAttribute('event_id'); 232 var id1 = dest.getAttribute('event_id');
106 var id2 = comp.getAttribute('event_id'); 233 var id2 = comp.getAttribute('event_id');
107 var d = 0, c = 0, cd = 0, l = window.raw_votes.length; 234 var d = 0, c = 0, cd = 0, l =raw_votes.length;
108 for (vote of window.raw_votes) { 235 for (vote of raw_votes) {
109 var x = 0; 236 var x = 0;
110 if( vote.indexOf(id1) > -1 ) { ++d; x++;} 237 if( vote.indexOf(id1) > -1 ) { ++d; x++;}
111 if( vote.indexOf(id2) > -1 ) { ++c; cd+=x; } 238 if( vote.indexOf(id2) > -1 ) { ++c; cd+=x; }
@@ -208,8 +335,9 @@ function remove_event(event_id) {
208 el.classList.add('pending'); 335 el.classList.add('pending');
209 if (ws && ws.readyState === WebSocket.OPEN) { 336 if (ws && ws.readyState === WebSocket.OPEN) {
210 var message = { 337 var message = {
338 action: "remove_event",
211 lastupdate: window.lastupdate, 339 lastupdate: window.lastupdate,
212 removeevent: event_id 340 event_id: event_id
213 } 341 }
214 ws.send(JSON.stringify(message)); 342 ws.send(JSON.stringify(message));
215 console.log('Sent:', message); 343 console.log('Sent:', message);
@@ -233,8 +361,9 @@ function set_all_attributes(event_id, day, room, time, from_server) {
233 el.classList.add('pending'); 361 el.classList.add('pending');
234 if (ws && ws.readyState === WebSocket.OPEN) { 362 if (ws && ws.readyState === WebSocket.OPEN) {
235 var message = { 363 var message = {
364 action: "set_event",
236 lastupdate: window.lastupdate, 365 lastupdate: window.lastupdate,
237 setevent: event_id, 366 event_id: event_id,
238 day: el.getAttribute('fullnarp-day'), 367 day: el.getAttribute('fullnarp-day'),
239 room: el.getAttribute('fullnarp-room'), 368 room: el.getAttribute('fullnarp-room'),
240 time: el.getAttribute('fullnarp-time') 369 time: el.getAttribute('fullnarp-time')
@@ -264,26 +393,51 @@ function signalFullnarpConnect(state) {
264 document.body.classList.add(state); 393 document.body.classList.add(state);
265} 394}
266 395
267function getFullnarpData(lastupdate) { 396function getFullnarpData() {
268 signalFullnarpConnect('fullnarp-connecting'); 397 signalFullnarpConnect('fullnarp-connecting');
269 ws = new WebSocket('wss://erdgeist.org/38C3/halfnarp/fullnarp-ws'); 398 ws = new WebSocket('wss://content.events.ccc.de/fullnarp/ws/');
270 399
271 ws.onopen = () => { 400 ws.onopen = () => {
272 console.log('Connected to WebSocket server'); 401 console.log('Connected to WebSocket server');
273 //stateElement.textContent = 'Connected'; 402 var message = {
403 action: raw_votes ? "reconnect" : "bootstrap"
404 };
405 ws.send(JSON.stringify(message));
406 console.log('Sent:', message);
274 }; 407 };
275 408
276 ws.onmessage = (event) => { 409 ws.onmessage = (event) => {
277 signalFullnarpConnect('fullnarp-connected'); 410 signalFullnarpConnect('fullnarp-connected');
278 const data = JSON.parse(event.data); 411 const data = JSON.parse(event.data);
279 console.log('Received:', data); 412 console.log('Received:', data);
280 for (const [eventid, event_new] of Object.entries(data.data)) { 413
281 if (document.getElementById(eventid)) 414 switch (data.property) {
282 set_all_attributes(eventid, 'day_'+event_new['day'], 'room'+event_new['room'], 'time_'+event_new['time'], true ) 415
416 case 'pretalx':
417 render_lectures(data.data);
418 break;
419
420 case 'halfnarp':
421 for (eventidlist of data.data)
422 for (eventid of eventidlist)
423 window.votes[eventid] = 1 + (window.votes[eventid] || 0 );
424 raw_votes = data.data;
425 distribute_votes();
426 break;
427
428 case 'fullnarp':
429 for (const [eventid, event_new] of Object.entries(data.data)) {
430 if (document.getElementById(eventid))
431 set_all_attributes(eventid, 'day_'+event_new['day'], 'room'+event_new['room'], 'time_'+event_new['time'], true )
432 }
433 window.lastupdate = data.current_version;
434 current_version_string = ('00000'+data.current_version).slice(-5);
435 document.querySelector('.version').innerHTML = '<a href="https://content.events.ccc.de/fullnarp/versions/fullnarp_'+current_version_string+'.json">Version: '+data.current_version+'</a>';
436 break;
437
438 default:
439 console.log(`Unknown property: ${data['property']}.`);
283 } 440 }
284 window.lastupdate = data.current_version;
285 current_version_string = ('00000'+data.current_version).slice(-5);
286 document.querySelector('.version').innerHTML = '<a href="https://erdgeist.org/38C3/halfnarp/versions/fullnarp_'+current_version_string+'.json">Version: '+data.current_version+'</a>';
287 }; 441 };
288 442
289 ws.onerror = (error) => { 443 ws.onerror = (error) => {
@@ -302,11 +456,6 @@ function getFullnarpData(lastupdate) {
302function do_the_fullnarp() { 456function do_the_fullnarp() {
303 var halfnarpAPI = 'talks_38C3.json'; 457 var halfnarpAPI = 'talks_38C3.json';
304 var fullnarpAPI = 'votes_38c3.json'; 458 var fullnarpAPI = 'votes_38c3.json';
305 var allrooms = ['1','2','3']
306 var allminutes = ['00','05','10','15','20','25','30','35','40','45','50','55']
307 var allhours = ['10','11','12','13','14','15','16','17','18','19','20','21','22','23','00','01','02'];
308 var alldays = ['1','2','3','4'];
309 var voted = 0;
310 window.event_speakers = {}; 459 window.event_speakers = {};
311 window.votes = {}; 460 window.votes = {};
312 461
@@ -383,8 +532,8 @@ function do_the_fullnarp() {
383 elem.classList.add('guide', 'time_' + hour + '00'); 532 elem.classList.add('guide', 'time_' + hour + '00');
384 document.body.append(elem); 533 document.body.append(elem);
385 534
386 for (minute of allminutes) { 535 for (minute of allminutes)
387 for (room of allrooms) { 536 for (room of allrooms)
388 for (day of alldays) { 537 for (day of alldays) {
389 elem = document.createElement('div'); 538 elem = document.createElement('div');
390 elem.classList.add('grid', 'time_' + hour + minute, 'day_' + day, 'room' + room ); 539 elem.classList.add('grid', 'time_' + hour + minute, 'day_' + day, 'room' + room );
@@ -408,170 +557,10 @@ function do_the_fullnarp() {
408 return false; 557 return false;
409 } 558 }
410 } 559 }
411 }
412 }
413 } 560 }
414 561
415 /* Fetch list of votes to display */ 562 window.lastupdate = 0;
416 fetch(`${fullnarpAPI}?format=json`) 563 getFullnarpData();
417 .then(response => {
418 if (!response.ok) {
419 throw new Error(`HTTP error when fetching fullnarp data! status: ${response.status}`);
420 }
421 return response.json();
422 }).then(data => {
423 window.raw_votes = data;
424 for (eventidlist of data)
425 for (eventid of eventidlist)
426 window.votes[eventid] = 1 + (window.votes[eventid] || 0 );
427 if( ++voted == 2 ) {
428 window.lastupdate = 0;
429 distribute_votes();
430 getFullnarpData(0);
431 }
432 }).catch(error => {
433 console.error('Fetch error:', error);
434 });
435
436
437 /* Fetch list of lectures to display */
438 fetch(`${halfnarpAPI}?format=json`)
439 .then(response => {
440 if (!response.ok) {
441 throw new Error(`HTTP error when fetching halfnarp data! status: ${response.status}`);
442 }
443 return response.json();
444 }).then(data => {
445 for (item of data) {
446 /* Take copy of hidden event template div and select them, if they're in
447 list of previous prereferences */
448 var t = document.getElementById('template').cloneNode(true);
449 var event_id = item.event_id.toString();
450 t.classList.add('event', 'duration_' + item.duration, 'lang_' + (item.language || 'en'));
451 t.setAttribute('event_id', event_id);
452 t.setAttribute('id', 'event_' + event_id)
453 t.setAttribute( 'fullnarp-duration', item.duration);
454
455 /* Sort textual info into event div */
456 t.querySelector('.title').textContent = item.title;
457 t.querySelector('.speakers').textContent = item.speaker_names;
458 t.querySelector('.abstract').append(item.abstract);
459
460 /* Store speakers and their availabilities */
461 window.event_speakers[event_id] = item.speakers;
462 for (speaker of item.speakers) {
463 var have_avails = false;
464 if (!speaker.availabilities)
465 console.log("Foo");
466 for (avail of speaker.availabilities) {
467 if (avail.id ) {
468 have_avails = true;
469 break;
470 }
471 }
472 if (!have_avails)
473 t.classList.add('has_unavailable_speaker');
474 }
475
476 t.setAttribute('draggable', 'true');
477
478 /* Make the event drag&droppable */
479 t.ondragstart = function( event, ui ) {
480 event.stopPropagation();
481
482 event.dataTransfer.setData('text/plain', this.id );
483 event.dataTransfer.dropEffect = 'move';
484 event.dataTransfer.effectAllowed = 'move';
485 event.target.classList.add('is-dragged');
486 }
487
488 /* While dragging make source element small enough to allow
489 dropping below its original area */
490 t.ondrag = function( event, ui ) {
491 event.stopPropagation();
492 event.target.classList.add('is-dragged');
493
494 /* When drag starts in list view, switch to calendar view */
495 if( document.body.classList.contains('in-list') ) {
496 toggle_grid(5);
497 document.body.classList.add('was-list');
498 }
499 if( document.body.classList.contains('in-drag') )
500 return;
501
502 document.body.classList.add('in-drag');
503 /* mark all possible drop points regarding to availability */
504 for (hour of allhours)
505 for (minute of allminutes)
506 for (day of alldays)
507 document.querySelectorAll('.grid.day_'+day+'.time_'+hour+minute).forEach(elem => elem.classList.toggle('possible', check_avail(event.target, day, hour+minute)));
508
509 }
510
511 t.ondragend = function( event, ui ) {
512 event.stopPropagation();
513
514 /* We removed in-list and the drop did not succeed. Go back to list view */
515 if (document.body.classList.contains('was-list'))
516 toggle_grid(0);
517
518 document.querySelectorAll('.over').forEach(elem => elem.classList.remove('over'));
519 document.querySelectorAll('.is-dragged').forEach(elem => elem.classList.remove('id-dragged'));
520 document.querySelectorAll('.possible').forEach(elem => elem.classList.remove('possible'));
521 document.body.classList.remove('in-drag', 'was-list');
522 }
523
524 /* start_time: 2014-12-29T21:15:00+01:00" */
525 var start_time = new Date(item.start_time);
526
527 var day = start_time.getDate()-26;
528 var hour = start_time.getHours();
529 var mins = start_time.getMinutes();
530
531 /* After midnight: sort into yesterday */
532 if( hour < 9 )
533 day--;
534
535 /* Fix up room for 38c3 */
536 room = (item.room_id || 'room_unknown').toString().replace('471','room1').replace('472','room2').replace('473','room3');
537
538 /* Apply attributes to sort events into calendar */
539 t.classList.add(room, 'day_' + day, 'time_' + (hour<10?'0':'') + hour + (mins<10?'0':'') + mins);
540 t.setAttribute('fullnarp-day', day);
541 t.setAttribute('fullnarp-time', (hour<10?'0':'') + hour + (mins<10?'0':'') + mins );
542 t.setAttribute('fullnarp-room', room.replace('room',''));
543
544 mark_avail(t);
545
546 t.onclick = function(event) {
547 _this = this;
548 document.body.classList.remove('in-drag');
549 if (document.body.classList.contains('correlate')) {
550 document.querySelectorAll('.selected').forEach(elem => elem.classList.remove('selected'));
551 document.querySelectorAll('.event').forEach(elem => mark_correlation(elem, _this));
552 }
553 _this.classList.toggle('selected');
554 document.querySelectorAll('.info').forEach(elem => elem.classList.add('hidden'));
555 event.stopPropagation();
556 }
557
558 /* Put new event into DOM tree. Track defaults to 'Other' */
559 var track = item.track_id.toString();
560 t.classList.add('track_' + track );
561 var d = document.getElementById(track);
562 if (!d)
563 d = document.querySelector('#Other');
564 d.append(t);
565 };
566
567 if( ++voted == 2 ) {
568 window.lastupdate = 0;
569 distribute_votes();
570 getFullnarpData(0);
571 }
572 }).catch(error => {
573 console.error('Fetch error:', error);
574 });
575 564
576 document.onkeypress = function(e) { 565 document.onkeypress = function(e) {
577 document.body.classList.remove('in-drag'); 566 document.body.classList.remove('in-drag');