showoff 0.12.0 → 0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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() {
|