showoff 0.12.0 → 0.12.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/showoff +28 -9
- data/lib/keymap.rb +145 -1
- data/lib/showoff.rb +184 -63
- data/lib/showoff/version.rb +1 -1
- data/lib/showoff_utils.rb +76 -39
- data/public/css/TimeCircles.css +42 -0
- data/public/css/presenter.css +161 -45
- data/public/css/showoff.css +185 -75
- data/public/favicon.ico +0 -0
- data/public/js/TimeCircles.js +984 -0
- data/public/js/presenter.js +147 -64
- data/public/js/showoff.js +224 -85
- data/views/download.erb +21 -24
- data/views/header.erb +5 -2
- data/views/index.erb +20 -10
- data/views/presenter.erb +19 -11
- data/views/stats.erb +47 -49
- metadata +28 -16
- data/public/css/close.png +0 -0
- data/public/css/run_code-dim.png +0 -0
- data/public/css/run_code.png +0 -0
- data/public/js/keyDictionary.json +0 -139
data/public/js/presenter.js
CHANGED
@@ -5,6 +5,8 @@ var notesWindow = null;
|
|
5
5
|
|
6
6
|
var paceData = [];
|
7
7
|
|
8
|
+
section = 'notes'; // which section the presenter has chosen to view
|
9
|
+
|
8
10
|
$(document).ready(function(){
|
9
11
|
// set up the presenter modes
|
10
12
|
mode = { track: true, follow: true, update: true, slave: false, next: false, notes: false};
|
@@ -16,9 +18,9 @@ $(document).ready(function(){
|
|
16
18
|
// the presenter window doesn't need the reload on resize bit
|
17
19
|
$(window).unbind('resize');
|
18
20
|
|
19
|
-
$("#
|
20
|
-
$("#
|
21
|
-
$("#stopTimer").click(function()
|
21
|
+
$("#startTimer").click(function() { startTimer() });
|
22
|
+
$("#pauseTimer").click(function() { toggleTimer() });
|
23
|
+
$("#stopTimer").click(function() { stopTimer() });
|
22
24
|
|
23
25
|
/* zoom slide to match preview size, then set up resize handler. */
|
24
26
|
zoom();
|
@@ -83,7 +85,8 @@ function presenterPopupToggle(page, event) {
|
|
83
85
|
|
84
86
|
content.attr('id', page.substring(1, page.length));
|
85
87
|
content.append(link);
|
86
|
-
|
88
|
+
/* use .sibliings() because of how jquery formats $(data) */
|
89
|
+
content.append($(data).siblings('#wrapper').html());
|
87
90
|
popup.append(content);
|
88
91
|
|
89
92
|
setupStats(); // this function is in showoff.js because /stats does not load presenter.js
|
@@ -102,14 +105,14 @@ function reportIssue() {
|
|
102
105
|
|
103
106
|
// open browser to remote edit URL
|
104
107
|
function editSlide() {
|
105
|
-
var slide = $("span#slideFile").text().replace(
|
108
|
+
var slide = $("span#slideFile").text().replace(/:\d+$/, '');
|
106
109
|
var link = editUrl + slide + ".md";
|
107
110
|
window.open(link);
|
108
111
|
}
|
109
112
|
|
110
113
|
// call the edit endpoint to open up a local file editor
|
111
114
|
function openEditor() {
|
112
|
-
var slide = $("span#slideFile").text().replace(
|
115
|
+
var slide = $("span#slideFile").text().replace(/:\d+$/, '');
|
113
116
|
var link = '/edit/' + slide + ".md";
|
114
117
|
$.get(link);
|
115
118
|
}
|
@@ -245,12 +248,31 @@ function printSlides()
|
|
245
248
|
}
|
246
249
|
}
|
247
250
|
|
248
|
-
function
|
249
|
-
|
251
|
+
function postQuestion(question, questionID) {
|
252
|
+
var questionItem = $('<li/>').text(question).attr('id', questionID);
|
250
253
|
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
+
questionItem.click( function(e) {
|
255
|
+
markCompleted($(this).attr('id'));
|
256
|
+
removeQuestion(questionID);
|
257
|
+
});
|
258
|
+
|
259
|
+
$("#unanswered").append(questionItem);
|
260
|
+
updateQuestionIndicator();
|
261
|
+
}
|
262
|
+
|
263
|
+
function removeQuestion(questionID) {
|
264
|
+
var question = $("li#"+questionID);
|
265
|
+
question.toggleClass('answered')
|
266
|
+
.remove();
|
267
|
+
$('#answered').append($(question));
|
268
|
+
updateQuestionIndicator();
|
269
|
+
}
|
270
|
+
|
271
|
+
function updateQuestionIndicator() {
|
272
|
+
try {
|
273
|
+
slaveWindow.updateQuestionIndicator( $('#unanswered li').length )
|
274
|
+
}
|
275
|
+
catch (e) {}
|
254
276
|
}
|
255
277
|
|
256
278
|
function paceFeedback(pace) {
|
@@ -330,6 +352,10 @@ reconnectControlChannel = function() {
|
|
330
352
|
});
|
331
353
|
}
|
332
354
|
|
355
|
+
function markCompleted(questionID) {
|
356
|
+
ws.send(JSON.stringify({ message: 'complete', questionID: questionID}));
|
357
|
+
}
|
358
|
+
|
333
359
|
function update() {
|
334
360
|
if(mode.update) {
|
335
361
|
var slideName = $("#slideFile").text();
|
@@ -390,7 +416,25 @@ function postSlide() {
|
|
390
416
|
notes = notes.html();
|
391
417
|
}
|
392
418
|
|
393
|
-
|
419
|
+
$('#notes').html(notes);
|
420
|
+
|
421
|
+
var sections = getCurrentSections();
|
422
|
+
if(sections.size() > 1) {
|
423
|
+
var ul = $('<ul>').addClass('section-selector');
|
424
|
+
sections.each(function(idx, value){
|
425
|
+
var li = $('<li/>').appendTo(ul);
|
426
|
+
var a = $('<a/>')
|
427
|
+
.text(value)
|
428
|
+
.attr('href','javascript:setCurrentSection("'+value+'");')
|
429
|
+
.appendTo(li);
|
430
|
+
|
431
|
+
if(section == value) {
|
432
|
+
li.addClass('selected');
|
433
|
+
}
|
434
|
+
});
|
435
|
+
|
436
|
+
$('#notes').prepend(ul);
|
437
|
+
}
|
394
438
|
|
395
439
|
if (notesWindow && typeof(notesWindow) != 'undefined' && !notesWindow.closed) {
|
396
440
|
$(notesWindow.document.body).html(notes);
|
@@ -478,67 +522,106 @@ function presenterKeyDown(event){
|
|
478
522
|
|
479
523
|
//* TIMER *//
|
480
524
|
|
481
|
-
var
|
482
|
-
var
|
483
|
-
|
484
|
-
|
485
|
-
|
525
|
+
var timerRunning = false;
|
526
|
+
var timerIntervals = [];
|
527
|
+
|
528
|
+
function startTimer() {
|
529
|
+
timerRunning = true;
|
530
|
+
|
531
|
+
$("#timerLabel").hide();
|
532
|
+
$("#minStart").hide();
|
533
|
+
|
534
|
+
$('#stopTimer').val('Cancel');
|
535
|
+
$("#stopTimer").show();
|
536
|
+
$("#pauseTimer").show();
|
537
|
+
$("#timerDisplay").show();
|
538
|
+
$("#timerSection").addClass('open');
|
539
|
+
|
540
|
+
var time = parseInt( $("#timerMinutes").val() ) * 60;
|
541
|
+
if(time) {
|
542
|
+
$('#timerDisplay')
|
543
|
+
.attr('data-timer', time)
|
544
|
+
.TimeCircles({
|
545
|
+
direction: 'Counter-clockwise',
|
546
|
+
total_duration: time,
|
547
|
+
count_past_zero: false,
|
548
|
+
time: {
|
549
|
+
Days: { show: false },
|
550
|
+
Hours: { show: false },
|
551
|
+
Seconds: { show: false },
|
552
|
+
}
|
553
|
+
}).addListener(timerProgress);
|
486
554
|
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
555
|
+
// add 60 seconds to each interval because the timer works on floor()
|
556
|
+
timerIntervals = [ time/2+60, time/4+60, time/8+60, time/16+60 ]
|
557
|
+
}
|
558
|
+
}
|
559
|
+
|
560
|
+
function timerProgress(unit, value, total){
|
561
|
+
|
562
|
+
if (timerIntervals.length > 0) {
|
563
|
+
if (total < timerIntervals[0]) {
|
564
|
+
|
565
|
+
ts = $('#timerSection');
|
566
|
+
|
567
|
+
// clear all classes except for the one sizing the container
|
568
|
+
ts.attr('class', 'open');
|
569
|
+
|
570
|
+
// remove all the intervals we've already passed
|
571
|
+
timerIntervals = timerIntervals.filter(function(val) { return val < total });
|
572
|
+
|
573
|
+
switch(timerIntervals.length) {
|
574
|
+
case 3: ts.addClass('intervalHalf'); break;
|
575
|
+
case 2: ts.addClass('intervalQuarter'); break;
|
576
|
+
case 1: ts.addClass('intervalWarning'); break;
|
577
|
+
case 0:
|
578
|
+
ts.addClass('intervalCritical');
|
579
|
+
$("#timerDisplay").TimeCircles({circle_bg_color: "red"});
|
580
|
+
|
581
|
+
// when timing short durations, sometimes the last interval doesn't get triggered until we end.
|
582
|
+
if( $("#timerDisplay").TimeCircles().getTime() <= 0 ) {
|
583
|
+
endTimer();
|
584
|
+
}
|
585
|
+
break;
|
586
|
+
}
|
503
587
|
}
|
504
|
-
}
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
588
|
+
}
|
589
|
+
else {
|
590
|
+
endTimer();
|
591
|
+
}
|
592
|
+
}
|
593
|
+
|
594
|
+
function toggleTimer() {
|
595
|
+
if (!timerRunning) {
|
596
|
+
timerRunning = true;
|
597
|
+
$('#pauseTimer').val('Pause');
|
598
|
+
$('#timerDisplay').removeClass('paused');
|
599
|
+
$("#timerDisplay").TimeCircles().start();
|
600
|
+
}
|
601
|
+
else {
|
602
|
+
timerRunning = false;
|
603
|
+
$('#pauseTimer').val('Resume');
|
604
|
+
$('#timerDisplay').addClass('paused');
|
605
|
+
$("#timerDisplay").TimeCircles().stop();
|
512
606
|
}
|
513
607
|
}
|
514
608
|
|
515
|
-
function
|
516
|
-
|
517
|
-
|
518
|
-
var percent = Math.round((minutes / totalMinutes) * 100);
|
519
|
-
var progress = getSlidePercent() - percent;
|
520
|
-
setProgressColor(progress);
|
521
|
-
return minutes + '/' + left + ' - ' + percent + '%';
|
609
|
+
function endTimer() {
|
610
|
+
$('#stopTimer').val('Reset');
|
611
|
+
$("#pauseTimer").hide();
|
522
612
|
}
|
523
613
|
|
524
|
-
function
|
525
|
-
|
526
|
-
|
527
|
-
ts.removeClass('tGreen')
|
528
|
-
ts.removeClass('tYellow')
|
529
|
-
ts.removeClass('tRed')
|
614
|
+
function stopTimer() {
|
615
|
+
$("#timerDisplay").removeData('timer');
|
616
|
+
$("#timerDisplay").TimeCircles().destroy();
|
530
617
|
|
531
|
-
|
618
|
+
$("#timerLabel").show();
|
619
|
+
$("#minStart").show();
|
532
620
|
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
} else if (progress > -10) {
|
538
|
-
ts.addClass('tYellow')
|
539
|
-
} else {
|
540
|
-
ts.addClass('tRed')
|
541
|
-
}
|
621
|
+
$("#stopTimer").hide();
|
622
|
+
$("#pauseTimer").hide();
|
623
|
+
$("#timerDisplay").hide();
|
624
|
+
$('#timerSection').removeClass();
|
542
625
|
}
|
543
626
|
|
544
627
|
/********************
|
data/public/js/showoff.js
CHANGED
@@ -16,15 +16,13 @@ var incrCode = false
|
|
16
16
|
var debugMode = false
|
17
17
|
var gotoSlidenum = 0
|
18
18
|
var lastMessageGuid = 0
|
19
|
-
var query
|
19
|
+
var query;
|
20
|
+
var section = 'handouts'; // default to showing handout notes for display view
|
20
21
|
var slideStartTime = new Date().getTime()
|
21
22
|
|
22
23
|
var loadSlidesBool
|
23
24
|
var loadSlidesPrefix
|
24
25
|
|
25
|
-
var keycode_dictionary,
|
26
|
-
keycode_shifted_keys;
|
27
|
-
|
28
26
|
var mode = { track: true, follow: true };
|
29
27
|
|
30
28
|
$(document).on('click', 'code.execute', executeCode);
|
@@ -45,7 +43,6 @@ function setupPreso(load_slides, prefix) {
|
|
45
43
|
loadSlidesPrefix = prefix || '/'
|
46
44
|
loadSlides(loadSlidesBool, loadSlidesPrefix)
|
47
45
|
|
48
|
-
loadKeyDictionaries();
|
49
46
|
setupSideMenu();
|
50
47
|
|
51
48
|
doDebugStuff()
|
@@ -54,10 +51,13 @@ function setupPreso(load_slides, prefix) {
|
|
54
51
|
toggleKeybinding('on');
|
55
52
|
|
56
53
|
$('#preso').addSwipeEvents().
|
57
|
-
bind('tap', swipeLeft). // next
|
54
|
+
// bind('tap', swipeLeft). // next
|
58
55
|
bind('swipeleft', swipeLeft). // next
|
59
56
|
bind('swiperight', swipeRight); // prev
|
60
57
|
|
58
|
+
$('#buttonNav #buttonPrev').click(prevStep);
|
59
|
+
$('#buttonNav #buttonNext').click(nextStep);
|
60
|
+
|
61
61
|
// give us the ability to disable tracking via url parameter
|
62
62
|
if(query.track == 'false') mode.track = false;
|
63
63
|
|
@@ -96,13 +96,6 @@ function loadSlides(load_slides, prefix, reload) {
|
|
96
96
|
}
|
97
97
|
}
|
98
98
|
|
99
|
-
function loadKeyDictionaries () {
|
100
|
-
$.getJSON('js/keyDictionary.json', function(data) {
|
101
|
-
keycode_dictionary = data['keycodeDictionary'];
|
102
|
-
keycode_shifted_keys = data['shiftedKeyDictionary'];
|
103
|
-
});
|
104
|
-
}
|
105
|
-
|
106
99
|
function initializePresentation(prefix) {
|
107
100
|
// unhide for height to work in static mode
|
108
101
|
$("#slides").show();
|
@@ -196,11 +189,11 @@ function setupSideMenu() {
|
|
196
189
|
$("#hamburger").click(function() {
|
197
190
|
$('#feedbackSidebar, #sidebarExit').toggle();
|
198
191
|
toggleKeybinding();
|
199
|
-
|
200
192
|
});
|
201
193
|
|
202
194
|
$("#navToggle").click(function() {
|
203
|
-
|
195
|
+
$("#navigation").toggle();
|
196
|
+
updateMenuChevrons();
|
204
197
|
});
|
205
198
|
|
206
199
|
$('#fileDownloads').click(function() {
|
@@ -217,19 +210,38 @@ function setupSideMenu() {
|
|
217
210
|
});
|
218
211
|
|
219
212
|
$('#questionToggle').click(function() {
|
220
|
-
$(
|
213
|
+
if ( ! $(this).hasClass('disabled') ) {
|
214
|
+
$('#questionSubmenu').toggle();
|
215
|
+
}
|
221
216
|
});
|
222
217
|
$("#askQuestion").click(function() {
|
223
|
-
|
224
|
-
|
218
|
+
if ( ! $(this).hasClass('disabled') ) {
|
219
|
+
var question = $("#question").val()
|
220
|
+
var qid = askQuestion(question);
|
221
|
+
|
222
|
+
feedback_response(this, "Sending...");
|
223
|
+
$("#question").val('');
|
224
|
+
|
225
|
+
var questionItem = $('<li/>').text(question).attr('id', qid);
|
226
|
+
questionItem.click( function(e) {
|
227
|
+
cancelQuestion($(this).attr('id'));
|
228
|
+
$(this).remove();
|
229
|
+
});
|
230
|
+
$("#askedQuestions").append(questionItem);
|
231
|
+
}
|
225
232
|
});
|
226
233
|
|
227
234
|
$('#feedbackToggle').click(function() {
|
228
|
-
$(
|
235
|
+
if ( ! $(this).hasClass('disabled') ) {
|
236
|
+
$('#feedbackSubmenu').toggle();
|
237
|
+
}
|
229
238
|
});
|
230
239
|
$("#sendFeedback").click(function() {
|
231
|
-
|
232
|
-
|
240
|
+
if ( ! $(this).hasClass('disabled') ) {
|
241
|
+
sendFeedback($( "input:radio[name=rating]:checked" ).val(), $("#feedback").val());
|
242
|
+
feedback_response(this, "Sending...");
|
243
|
+
$("#feedback").val('');
|
244
|
+
}
|
233
245
|
});
|
234
246
|
|
235
247
|
$("#editSlide").click(function() {
|
@@ -257,6 +269,28 @@ function setupSideMenu() {
|
|
257
269
|
}
|
258
270
|
}
|
259
271
|
|
272
|
+
function updateQuestionIndicator(count) {
|
273
|
+
if(count == 0) {
|
274
|
+
$('#questionsIndicator').hide();
|
275
|
+
}
|
276
|
+
else {
|
277
|
+
$('#questionsIndicator').show();
|
278
|
+
$('#questionsIndicator').text(count);
|
279
|
+
}
|
280
|
+
}
|
281
|
+
|
282
|
+
function updateMenuChevrons() {
|
283
|
+
$(".navSection + ul:not(:visible)")
|
284
|
+
.siblings('a')
|
285
|
+
.children('i')
|
286
|
+
.attr('class', 'fa fa-angle-down');
|
287
|
+
|
288
|
+
$(".navSection + ul:visible")
|
289
|
+
.siblings('a')
|
290
|
+
.children('i')
|
291
|
+
.attr('class', 'fa fa-angle-up');
|
292
|
+
}
|
293
|
+
|
260
294
|
function setupMenu() {
|
261
295
|
var nav = $("<ul>"),
|
262
296
|
currentSection = '',
|
@@ -270,6 +304,7 @@ function setupMenu() {
|
|
270
304
|
.shift();
|
271
305
|
var headers = $(slide).children("h1, h2");
|
272
306
|
var slideTitle = '';
|
307
|
+
var content;
|
273
308
|
|
274
309
|
if (currentSection !== slidePath) {
|
275
310
|
currentSection = slidePath;
|
@@ -283,6 +318,12 @@ function setupMenu() {
|
|
283
318
|
.append(icon)
|
284
319
|
.click(function() {
|
285
320
|
$(this).next().toggle();
|
321
|
+
updateMenuChevrons();
|
322
|
+
|
323
|
+
if( $(this).parent().is(':last-child') ) {
|
324
|
+
$(this).next().children('li').first()[0].scrollIntoView();
|
325
|
+
}
|
326
|
+
|
286
327
|
return false;
|
287
328
|
});
|
288
329
|
sectionUL = $("<ul>");
|
@@ -290,13 +331,18 @@ function setupMenu() {
|
|
290
331
|
nav.append(newSection);
|
291
332
|
}
|
292
333
|
|
334
|
+
// look for first header to use as a title
|
293
335
|
if (headers.length > 0) {
|
294
336
|
slideTitle = headers.first().text();
|
295
337
|
} else {
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
338
|
+
// if no header, look at content
|
339
|
+
content = $(slide).find(".content");
|
340
|
+
slideTitle = content.text().substr(0, 20).trim();
|
341
|
+
|
342
|
+
// if no content (like photo only) fall back to slide name
|
343
|
+
if (slideTitle == "") {
|
344
|
+
slideTitle = content.attr('ref').split('/').pop();
|
345
|
+
}
|
300
346
|
}
|
301
347
|
|
302
348
|
var navLink = $("<a>")
|
@@ -460,6 +506,11 @@ function showSlide(back_step, updatepv) {
|
|
460
506
|
$(active).parent().addClass('highlighted');
|
461
507
|
$(active).parent().parent().show();
|
462
508
|
|
509
|
+
updateMenuChevrons();
|
510
|
+
|
511
|
+
// copy notes to the notes field for mobile.
|
512
|
+
postSlide();
|
513
|
+
|
463
514
|
return ret;
|
464
515
|
}
|
465
516
|
|
@@ -468,9 +519,22 @@ function getSlideProgress()
|
|
468
519
|
return (slidenum + 1) + '/' + slideTotal
|
469
520
|
}
|
470
521
|
|
522
|
+
function getCurrentSections()
|
523
|
+
{
|
524
|
+
return currentSlide.find("div.notes-section").map(function() {
|
525
|
+
return $(this).attr('class').split(' ').filter(function(x) { return x != 'notes-section'; });
|
526
|
+
});
|
527
|
+
}
|
528
|
+
|
529
|
+
function setCurrentSection(newSection)
|
530
|
+
{
|
531
|
+
section = newSection;
|
532
|
+
postSlide();
|
533
|
+
}
|
534
|
+
|
471
535
|
function getCurrentNotes()
|
472
536
|
{
|
473
|
-
var notes = currentSlide.find("div.notes");
|
537
|
+
var notes = currentSlide.find("div.notes-section."+section);
|
474
538
|
return notes;
|
475
539
|
}
|
476
540
|
|
@@ -583,50 +647,52 @@ function renderForm(form) {
|
|
583
647
|
var action = form.attr("action");
|
584
648
|
$.getJSON(action, function( data ) {
|
585
649
|
//console.log(data);
|
586
|
-
form.children('.element').each(function() {
|
587
|
-
var key = $(
|
650
|
+
form.children('.element').each(function(index, element) {
|
651
|
+
var key = $(element).attr('data-name');
|
588
652
|
|
589
653
|
// add a counter label if we haven't already
|
590
|
-
if( $(
|
591
|
-
$(
|
654
|
+
if( $(element).next('.count').length === 0 ) {
|
655
|
+
$(element).after($('<h1>').addClass('count'));
|
592
656
|
}
|
593
657
|
|
594
|
-
$(
|
658
|
+
$(element).find('ul > li > *').each(function() {
|
595
659
|
$(this).parent().parent().before(this);
|
596
660
|
});
|
597
|
-
$(
|
661
|
+
$(element).children('ul').each(function() {
|
598
662
|
$(this).remove();
|
599
663
|
});
|
600
664
|
|
601
|
-
// replace all input widgets with
|
602
|
-
$(
|
603
|
-
switch( $(
|
665
|
+
// replace all input widgets with divs for the bar chart
|
666
|
+
$(element).children(':input').each(function(index, input) {
|
667
|
+
switch( $(input).attr('type') ) {
|
604
668
|
case 'text':
|
605
669
|
case 'button':
|
606
670
|
case 'submit':
|
607
671
|
case 'textarea':
|
608
672
|
// we don't render these
|
609
|
-
$(
|
673
|
+
$(input).parent().remove();
|
610
674
|
break;
|
611
675
|
|
612
676
|
case 'radio':
|
613
677
|
case 'checkbox':
|
614
678
|
// Just render these directly and migrate the label to inside the span
|
615
|
-
var label = $(
|
679
|
+
var label = $(input).next('label');
|
616
680
|
var text = label.text();
|
617
|
-
var classes = $(
|
681
|
+
var classes = $(input).attr('class');
|
618
682
|
|
619
683
|
if(text.match(/^-+$/)) {
|
620
|
-
$(
|
684
|
+
$(input).remove();
|
621
685
|
} else {
|
622
686
|
var resultDiv = $('<div>')
|
623
687
|
.addClass('item')
|
624
|
-
.attr('data-value', $(
|
625
|
-
.text(text)
|
688
|
+
.attr('data-value', $(input).attr('value'))
|
689
|
+
.append($('<span>').addClass('answer').text(text))
|
690
|
+
.append($('<div>').addClass('bar'));
|
691
|
+
|
626
692
|
if (classes) {
|
627
693
|
resultDiv.addClass(classes);
|
628
694
|
}
|
629
|
-
$(
|
695
|
+
$(input).replaceWith(resultDiv);
|
630
696
|
}
|
631
697
|
label.remove();
|
632
698
|
break;
|
@@ -634,24 +700,25 @@ function renderForm(form) {
|
|
634
700
|
default:
|
635
701
|
// select doesn't have a type attribute... yay html
|
636
702
|
// poke inside to get options, then render each as a span and replace the select
|
637
|
-
var parent = $(
|
703
|
+
var parent = $(input).parent();
|
638
704
|
|
639
|
-
$(
|
640
|
-
var text = $(
|
641
|
-
var classes = $(
|
705
|
+
$(input).children('option').each(function() {
|
706
|
+
var text = $(input).text();
|
707
|
+
var classes = $(input).attr('class');
|
642
708
|
|
643
709
|
if(! text.match(/^-+$/)) {
|
644
710
|
var resultDiv = $('<div>')
|
645
711
|
.addClass('item')
|
646
|
-
.attr('data-value', $(
|
647
|
-
.text(text)
|
712
|
+
.attr('data-value', $(input).val())
|
713
|
+
.append($('<span>').addClass('answer').text(text))
|
714
|
+
.append($('<div>').addClass('bar'));
|
648
715
|
if (classes) {
|
649
716
|
resultDiv.addClass(classes);
|
650
717
|
}
|
651
718
|
parent.append(resultDiv);
|
652
719
|
}
|
653
720
|
});
|
654
|
-
$(
|
721
|
+
$(input).remove();
|
655
722
|
break;
|
656
723
|
}
|
657
724
|
});
|
@@ -661,8 +728,8 @@ function renderForm(form) {
|
|
661
728
|
// number of unique responses
|
662
729
|
var total = 0;
|
663
730
|
// double loop so we can handle re-renderings of the form
|
664
|
-
$(
|
665
|
-
var name = $(
|
731
|
+
$(element).find('.item').each(function(index, item) {
|
732
|
+
var name = $(item).attr('data-value');
|
666
733
|
|
667
734
|
if(key in data) {
|
668
735
|
var count = data[key]['responses'][name];
|
@@ -672,12 +739,12 @@ function renderForm(form) {
|
|
672
739
|
});
|
673
740
|
|
674
741
|
// insert the total into the counter label
|
675
|
-
$(
|
676
|
-
$(
|
742
|
+
$(element).next('.count').each(function(index, icount) {
|
743
|
+
$(icount).text(total);
|
677
744
|
});
|
678
745
|
|
679
|
-
var oldTotal = $(
|
680
|
-
$(
|
746
|
+
var oldTotal = $(element).attr('data-total');
|
747
|
+
$(element).find('.item').each(function() {
|
681
748
|
var name = $(this).attr('data-value');
|
682
749
|
var oldCount = $(this).attr('data-count');
|
683
750
|
|
@@ -689,29 +756,35 @@ function renderForm(form) {
|
|
689
756
|
}
|
690
757
|
|
691
758
|
if(count != oldCount || total != oldTotal) {
|
692
|
-
var percent = (total) ? ((count/total)*100)+'%' : '0%';
|
759
|
+
var percent = (total) ? ((count/total)*100) + '%' : '0%';
|
693
760
|
|
694
761
|
$(this).attr('data-count', count);
|
695
|
-
$(this).animate({width: percent});
|
762
|
+
$(this).find('.bar').animate({width: percent});
|
696
763
|
}
|
697
764
|
});
|
698
765
|
|
699
766
|
// record the old total value so we only animate when it changes
|
700
|
-
$(
|
767
|
+
$(element).attr('data-total', total);
|
701
768
|
}
|
702
769
|
|
703
|
-
$(
|
770
|
+
$(element).addClass('rendered');
|
704
771
|
});
|
705
772
|
|
706
773
|
});
|
707
774
|
}
|
708
775
|
|
709
776
|
function connectControlChannel() {
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
777
|
+
if (interactive) {
|
778
|
+
protocol = (location.protocol === 'https:') ? 'wss://' : 'ws://';
|
779
|
+
ws = new WebSocket(protocol + location.host + '/control');
|
780
|
+
ws.onopen = function() { connected(); };
|
781
|
+
ws.onclose = function() { disconnected(); }
|
782
|
+
ws.onmessage = function(m) { parseMessage(m.data); };
|
783
|
+
}
|
784
|
+
else {
|
785
|
+
ws = {}
|
786
|
+
ws.send = function() { /* no-op */ }
|
787
|
+
}
|
715
788
|
}
|
716
789
|
|
717
790
|
// This exists as an intermediary simply so the presenter view can override it
|
@@ -721,12 +794,11 @@ function reconnectControlChannel() {
|
|
721
794
|
|
722
795
|
function connected() {
|
723
796
|
console.log('Control socket opened');
|
724
|
-
$("#feedbackSidebar
|
797
|
+
$("#feedbackSidebar .interactive").removeClass("disabled");
|
725
798
|
$("img#disconnected").hide();
|
726
799
|
|
727
800
|
try {
|
728
|
-
// If we are a presenter, then remind the server
|
729
|
-
update();
|
801
|
+
// If we are a presenter, then remind the server who we are
|
730
802
|
register();
|
731
803
|
}
|
732
804
|
catch (e) {}
|
@@ -734,12 +806,24 @@ function connected() {
|
|
734
806
|
|
735
807
|
function disconnected() {
|
736
808
|
console.log('Control socket closed');
|
737
|
-
$("#feedbackSidebar
|
809
|
+
$("#feedbackSidebar .interactive").addClass("disabled");
|
738
810
|
$("img#disconnected").show();
|
739
811
|
|
740
812
|
setTimeout(function() { reconnectControlChannel() } , 5000);
|
741
813
|
}
|
742
814
|
|
815
|
+
function generateGuid() {
|
816
|
+
var result, i, j;
|
817
|
+
result = 'S';
|
818
|
+
for(j=0; j<32; j++) {
|
819
|
+
if( j == 8 || j == 12|| j == 16|| j == 20)
|
820
|
+
result = result + '-';
|
821
|
+
i = Math.floor(Math.random()*16).toString(16).toUpperCase();
|
822
|
+
result = result + i;
|
823
|
+
}
|
824
|
+
return result;
|
825
|
+
}
|
826
|
+
|
743
827
|
function parseMessage(data) {
|
744
828
|
var command = JSON.parse(data);
|
745
829
|
|
@@ -753,42 +837,78 @@ function parseMessage(data) {
|
|
753
837
|
}
|
754
838
|
}
|
755
839
|
|
756
|
-
if ("current" in command) { follow(command["current"]); }
|
757
|
-
|
758
|
-
// Presenter messages only, so catch errors if method doesn't exist
|
759
840
|
try {
|
760
|
-
|
761
|
-
|
841
|
+
switch (command['message']) {
|
842
|
+
case 'current':
|
843
|
+
follow(command["current"]);
|
844
|
+
break;
|
845
|
+
|
846
|
+
case 'complete':
|
847
|
+
completeQuestion(command["questionID"]);
|
848
|
+
break;
|
849
|
+
|
850
|
+
case 'pace':
|
851
|
+
paceFeedback(command["pace"]);
|
852
|
+
break;
|
853
|
+
|
854
|
+
case 'question':
|
855
|
+
postQuestion(command["question"], command["questionID"]);
|
856
|
+
break;
|
857
|
+
|
858
|
+
case 'cancel':
|
859
|
+
removeQuestion(command["questionID"]);
|
860
|
+
break;
|
861
|
+
}
|
762
862
|
}
|
763
863
|
catch(e) {
|
764
864
|
console.log("Not a presenter!");
|
765
865
|
}
|
866
|
+
|
766
867
|
}
|
767
868
|
|
768
869
|
function sendPace(pace) {
|
769
|
-
ws.
|
770
|
-
|
870
|
+
if (ws.readyState == WebSocket.OPEN) {
|
871
|
+
ws.send(JSON.stringify({ message: 'pace', pace: pace}));
|
872
|
+
}
|
771
873
|
}
|
772
874
|
|
773
875
|
function askQuestion(question) {
|
774
|
-
ws.
|
775
|
-
|
876
|
+
if (ws.readyState == WebSocket.OPEN) {
|
877
|
+
var questionID = generateGuid();
|
878
|
+
ws.send(JSON.stringify({ message: 'question', question: question, questionID: questionID}));
|
879
|
+
return questionID;
|
880
|
+
}
|
881
|
+
}
|
882
|
+
|
883
|
+
function cancelQuestion(questionID) {
|
884
|
+
if (ws.readyState == WebSocket.OPEN) {
|
885
|
+
ws.send(JSON.stringify({ message: 'cancel', questionID: questionID}));
|
886
|
+
}
|
887
|
+
}
|
888
|
+
|
889
|
+
function completeQuestion(questionID) {
|
890
|
+
var question = $("li#"+questionID)
|
891
|
+
if(question.length > 0) {
|
892
|
+
question.addClass('closed');
|
893
|
+
feedbackActivity();
|
894
|
+
}
|
776
895
|
}
|
777
896
|
|
778
897
|
function sendFeedback(rating, feedback) {
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
898
|
+
if (ws.readyState == WebSocket.OPEN) {
|
899
|
+
var slide = $("#slideFilename").text();
|
900
|
+
ws.send(JSON.stringify({ message: 'feedback', rating: rating, feedback: feedback, slide: slide}));
|
901
|
+
$("input:radio[name=rating]:checked").attr('checked', false);
|
902
|
+
}
|
783
903
|
}
|
784
904
|
|
785
905
|
function feedbackActivity() {
|
786
|
-
$(
|
787
|
-
setTimeout(function() { $("
|
906
|
+
$('#hamburger').addClass('highlight');
|
907
|
+
setTimeout(function() { $("#hamburger").removeClass('highlight') }, 75);
|
788
908
|
}
|
789
909
|
|
790
910
|
function track() {
|
791
|
-
if (mode.track) {
|
911
|
+
if (mode.track && ws.readyState == WebSocket.OPEN) {
|
792
912
|
var slideName = $("#slideFilename").text();
|
793
913
|
var slideEndTime = new Date().getTime();
|
794
914
|
var elapsedTime = slideEndTime - slideStartTime;
|
@@ -859,6 +979,23 @@ function nextStep(updatepv)
|
|
859
979
|
}
|
860
980
|
}
|
861
981
|
|
982
|
+
// carrying on our grand tradition of overwriting functions of the same name with presenter.js
|
983
|
+
function postSlide() {
|
984
|
+
if(currentSlide) {
|
985
|
+
var notes = getCurrentNotes();
|
986
|
+
// Replace notes with empty string if there are no notes
|
987
|
+
// Otherwise it fails silently and does not remove old notes
|
988
|
+
if (notes.length === 0) {
|
989
|
+
notes = "";
|
990
|
+
} else {
|
991
|
+
notes = notes.html();
|
992
|
+
}
|
993
|
+
|
994
|
+
$('#notes').html(notes);
|
995
|
+
}
|
996
|
+
}
|
997
|
+
|
998
|
+
|
862
999
|
function doDebugStuff()
|
863
1000
|
{
|
864
1001
|
if (debugMode) {
|
@@ -1025,7 +1162,9 @@ function toggleHelp () {
|
|
1025
1162
|
}
|
1026
1163
|
|
1027
1164
|
function toggleContents () {
|
1028
|
-
$('#
|
1165
|
+
$('#feedbackSidebar, #sidebarExit').toggle();
|
1166
|
+
$("#navigation").toggle();
|
1167
|
+
updateMenuChevrons();
|
1029
1168
|
}
|
1030
1169
|
|
1031
1170
|
function swipeLeft() {
|