showoff 0.17.1 → 0.17.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +3 -2
- data/lib/showoff.rb +36 -4
- data/lib/showoff/version.rb +1 -1
- data/lib/showoff_utils.rb +5 -4
- data/public/css/images/ui-icons_444444_256x240.png +0 -0
- data/public/css/images/ui-icons_555555_256x240.png +0 -0
- data/public/css/images/ui-icons_777620_256x240.png +0 -0
- data/public/css/images/ui-icons_777777_256x240.png +0 -0
- data/public/css/images/ui-icons_cc0000_256x240.png +0 -0
- data/public/css/images/ui-icons_ffffff_256x240.png +0 -0
- data/public/css/presenter.css +63 -23
- data/public/css/showoff.css +46 -10
- data/public/js/presenter.js +21 -1
- data/public/js/showoff.js +72 -10
- data/views/presenter.erb +11 -0
- metadata +22 -4
- data/public/css/pace.png +0 -0
- data/views/stats copy.erb +0 -70
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e79f2890ed22c967bf20c13e7086bdd7e6fa441c
|
4
|
+
data.tar.gz: 5c328c3d8979191809a404b984202d2fca92a8cd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 281b88028b291de836db367604aaadead708eb79a34f060e1231defc51b188f5dc0cf01f1ceb8ca17e1662528d6f0d168929410f75a880a7c1ca5b24f95c0071
|
7
|
+
data.tar.gz: 2408b4d8909d3f84df131a7af5069cbc5029d8f768b67d6508d83899ccafec0d549e863554ef4dbfafe921be89395e2013fe505ee1bd77eb900bba1888d66cbe
|
data/README.md
CHANGED
@@ -64,9 +64,10 @@ you'll need to install both Ruby and the Ruby DevKit for compiling native extens
|
|
64
64
|
|
65
65
|
## Documentation
|
66
66
|
|
67
|
-
Please see the
|
67
|
+
Please see the user manual on the [Showoff homepage](http://puppetlabs.github.io/showoff)
|
68
|
+
for further information.
|
68
69
|
|
69
|
-
You can generate a nice & pretty local copy of the
|
70
|
+
You can also generate a nice & pretty local copy of the user manual by running
|
70
71
|
`rake doc` in your clone of the repository. The generated HTML will be saved in
|
71
72
|
the `docs` directory.
|
72
73
|
|
data/lib/showoff.rb
CHANGED
@@ -185,6 +185,7 @@ class ShowOff < Sinatra::Application
|
|
185
185
|
@@cookie = nil # presenter cookie. Identifies the presenter for control messages
|
186
186
|
@@current = Hash.new # The current slide that the presenter is viewing
|
187
187
|
@@cache = nil # Cache slide content for subsequent hits
|
188
|
+
@@activity = [] # keep track of completion for activity slides
|
188
189
|
|
189
190
|
if @interactive
|
190
191
|
# flush stats to disk periodically
|
@@ -415,6 +416,12 @@ class ShowOff < Sinatra::Application
|
|
415
416
|
|
416
417
|
content += sl
|
417
418
|
content += "</div>\n"
|
419
|
+
if content_classes.include? 'activity'
|
420
|
+
content += '<span class="activityToggle">'
|
421
|
+
content += " <label for=\"activity-#{ref}\">Activity complete</label>"
|
422
|
+
content += " <input type=\"checkbox\" class=\"activity\" name=\"activity-#{ref}\" id=\"activity-#{ref}\">"
|
423
|
+
content += '</span>'
|
424
|
+
end
|
418
425
|
content += "<canvas class=\"annotations\"></canvas>\n"
|
419
426
|
content += "</div>\n"
|
420
427
|
|
@@ -465,6 +472,9 @@ class ShowOff < Sinatra::Application
|
|
465
472
|
result.gsub!(match[0], "<pre class=\"highlight\"><code class=\"#{css}\">#{file}</code></pre>")
|
466
473
|
end
|
467
474
|
|
475
|
+
result.gsub!(/\[(fa-.*)\]/, '<i class="fa \1"></i>')
|
476
|
+
|
477
|
+
|
468
478
|
result
|
469
479
|
end
|
470
480
|
|
@@ -1111,6 +1121,7 @@ class ShowOff < Sinatra::Application
|
|
1111
1121
|
@favicon = settings.showoff_config['favicon']
|
1112
1122
|
@issues = settings.showoff_config['issues']
|
1113
1123
|
@edit = settings.showoff_config['edit'] if @review
|
1124
|
+
@feedback = settings.showoff_config['feedback']
|
1114
1125
|
@@cookie ||= guid()
|
1115
1126
|
response.set_cookie('presenter', @@cookie)
|
1116
1127
|
erb :presenter
|
@@ -1429,10 +1440,10 @@ class ShowOff < Sinatra::Application
|
|
1429
1440
|
classes = code.attr('class').split rescue []
|
1430
1441
|
lang = classes.shift =~ /language-(\S*)/ ? $1 : nil
|
1431
1442
|
|
1432
|
-
[lang, code.text, classes]
|
1443
|
+
[lang, code.text.gsub(/^\* /, ' '), classes]
|
1433
1444
|
end
|
1434
1445
|
else
|
1435
|
-
doc.css(classes)[index.to_i].text rescue 'Invalid code block index'
|
1446
|
+
doc.css(classes)[index.to_i].text.gsub(/^\* /, ' ') rescue 'Invalid code block index'
|
1436
1447
|
end
|
1437
1448
|
end
|
1438
1449
|
|
@@ -1577,6 +1588,10 @@ class ShowOff < Sinatra::Application
|
|
1577
1588
|
# Docs suggest that old versions of Sinatra might provide an array here, so just make sure.
|
1578
1589
|
filename = path.class == Array ? path.first : path
|
1579
1590
|
@logger.debug "Editing #{filename}"
|
1591
|
+
|
1592
|
+
# When a relative path is used, it's sometimes fully expanded. But then when
|
1593
|
+
# it's passed via URL, the initial slash is lost. Here we try to get it back.
|
1594
|
+
filename = "/#{filename}" unless File.exist? filename
|
1580
1595
|
return unless File.exist? filename
|
1581
1596
|
|
1582
1597
|
if request.host != 'localhost'
|
@@ -1682,9 +1697,26 @@ class ShowOff < Sinatra::Application
|
|
1682
1697
|
@logger.debug "Recorded current slide #{slide} for #{remote}"
|
1683
1698
|
end
|
1684
1699
|
|
1685
|
-
|
1686
1700
|
when 'position'
|
1687
|
-
ws.send( { 'current' => @@current[:number] }.to_json ) unless @@cookie.nil?
|
1701
|
+
ws.send( { 'message' => 'current', 'current' => @@current[:number] }.to_json ) unless @@cookie.nil?
|
1702
|
+
|
1703
|
+
when 'activity'
|
1704
|
+
next if valid_presenter_cookie?
|
1705
|
+
remote = request.cookies['client_id']
|
1706
|
+
slide = control['slide']
|
1707
|
+
status = control['status']
|
1708
|
+
@@activity[slide] ||= {}
|
1709
|
+
@@activity[slide][remote] = status
|
1710
|
+
|
1711
|
+
current = @@current[:number]
|
1712
|
+
activity = @@activity[current] rescue nil
|
1713
|
+
|
1714
|
+
@logger.debug "Current activity status: #{activity.inspect}"
|
1715
|
+
if activity
|
1716
|
+
# select all activity on this slide where completion status is false
|
1717
|
+
count = activity.select {|viewer, status| status == false }.size
|
1718
|
+
EM.next_tick { settings.presenters.each{|s| s.send({ 'message' => 'activity', 'count' => count }.to_json) } }
|
1719
|
+
end
|
1688
1720
|
|
1689
1721
|
when 'pace', 'question', 'cancel'
|
1690
1722
|
# just forward to the presenter(s) along with a debounce in case a presenter is registered twice
|
data/lib/showoff/version.rb
CHANGED
data/lib/showoff_utils.rb
CHANGED
@@ -470,14 +470,15 @@ class ShowOffUtils
|
|
470
470
|
end.flatten.compact.each do |filename|
|
471
471
|
# We do this in two passes simply because most of it was already done
|
472
472
|
# and I don't want to waste time on legacy functionality.
|
473
|
-
path = File.dirname(filename)
|
474
|
-
sections[path] ||= []
|
475
|
-
|
476
473
|
if File.directory? filename
|
474
|
+
path = filename
|
475
|
+
sections[path] ||= []
|
477
476
|
Dir.glob("#{filename}/**/*.md").sort.each do |slidefile|
|
478
|
-
sections[path]
|
477
|
+
sections[path] << slidefile
|
479
478
|
end
|
480
479
|
else
|
480
|
+
path = File.dirname(filename)
|
481
|
+
sections[path] ||= []
|
481
482
|
sections[path] << filename
|
482
483
|
end
|
483
484
|
end
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
data/public/css/presenter.css
CHANGED
@@ -83,7 +83,7 @@
|
|
83
83
|
border: none;
|
84
84
|
border-radius: 0;
|
85
85
|
line-height: 3em;
|
86
|
-
|
86
|
+
vertical-align: inherit; /* overrides a jquery-ui style */
|
87
87
|
}
|
88
88
|
|
89
89
|
#links a.enabled,
|
@@ -208,30 +208,62 @@
|
|
208
208
|
height: 40px;
|
209
209
|
min-height: 40px;
|
210
210
|
position: relative;
|
211
|
-
|
212
|
-
}
|
213
|
-
#paceFast,
|
214
|
-
#paceSlow {
|
215
|
-
display: none;
|
216
|
-
}
|
217
|
-
#paceFast {
|
218
|
-
float: left;
|
219
|
-
margin-left: 1em;
|
220
|
-
}
|
221
|
-
#paceSlow {
|
222
|
-
float: right;
|
223
|
-
margin-right: 1em;
|
211
|
+
border-bottom: 1px solid #ccc;
|
224
212
|
}
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
213
|
+
#feedbackPace .gradient {
|
214
|
+
position: absolute;
|
215
|
+
bottom: 0;
|
216
|
+
width: 100%;
|
217
|
+
height: 20px;
|
218
|
+
background-color:#ff0000;
|
219
|
+
filter:progid:DXImageTransform.Microsoft.gradient(GradientType=1,startColorstr=#ff0000, endColorstr=#eeff00);
|
220
|
+
background-image:-moz-linear-gradient(left, #ff0000 10%, #eeff00 30%,#00ff00 50%,#eeff00 70%,#ff0000 90%);
|
221
|
+
background-image:linear-gradient(left, #ff0000 10%, #eeff00 30%,#00ff00 50%,#eeff00 70%,#ff0000 90%);
|
222
|
+
background-image:-webkit-linear-gradient(left, #ff0000 10%, #eeff00 30%,#00ff00 50%,#eeff00 70%,#ff0000 90%);
|
223
|
+
background-image:-o-linear-gradient(left, #ff0000 10%, #eeff00 30%,#00ff00 50%,#eeff00 70%,#ff0000 90%);
|
224
|
+
background-image:-ms-linear-gradient(left, #ff0000 10%, #eeff00 30%,#00ff00 50%,#eeff00 70%,#ff0000 90%);
|
225
|
+
background-image:-webkit-gradient(linear, left bottom, right bottom, color-stop(10%,#ff0000), color-stop(30%,#eeff00),color-stop(50%,#00ff00),color-stop(70%,#eeff00),color-stop(90%,#ff0000));
|
226
|
+
}
|
227
|
+
#feedbackPace .obscure {
|
228
|
+
position: absolute;
|
229
|
+
background-color: #f0f0f0;
|
230
|
+
bottom: 0;
|
231
|
+
height: 20px;
|
232
|
+
transition: width 0.25s;
|
233
|
+
}
|
234
|
+
#feedbackPace .obscure.left {
|
235
|
+
left: 0;
|
236
|
+
width: 50%;
|
237
|
+
border-right: 1px solid black;
|
238
|
+
}
|
239
|
+
#feedbackPace .obscure.right {
|
240
|
+
right: 0;
|
241
|
+
width: 50%;
|
242
|
+
border-left: 1px solid black;
|
243
|
+
}
|
233
244
|
|
234
|
-
|
245
|
+
#paceFast,
|
246
|
+
#paceSlow {
|
247
|
+
display: none;
|
248
|
+
}
|
249
|
+
#paceFast {
|
250
|
+
float: left;
|
251
|
+
margin-left: 1em;
|
252
|
+
}
|
253
|
+
#paceSlow {
|
254
|
+
float: right;
|
255
|
+
margin-right: 1em;
|
256
|
+
}
|
257
|
+
#paceMarker {
|
258
|
+
left: 50%;
|
259
|
+
position: absolute;
|
260
|
+
transform: translate(-50%, -50%);
|
261
|
+
-webkit-transform: translate(-50%, 0);
|
262
|
+
-moz-transform: translate(-50%, 0);
|
263
|
+
-ms-transform: translate(-50%, 0);
|
264
|
+
-o-transform: translate(-50%, 0);
|
265
|
+
transition: left 0.25s;
|
266
|
+
}
|
235
267
|
|
236
268
|
.submenu {
|
237
269
|
flex-grow: 10;
|
@@ -574,6 +606,7 @@
|
|
574
606
|
width: 90%;
|
575
607
|
}
|
576
608
|
|
609
|
+
.slide.activity .count,
|
577
610
|
#notes .count {
|
578
611
|
display: inline-block;
|
579
612
|
background-color: #2e2e2e;
|
@@ -604,6 +637,13 @@
|
|
604
637
|
display: none;
|
605
638
|
}
|
606
639
|
|
640
|
+
.slide.activity .count {
|
641
|
+
position: absolute;
|
642
|
+
bottom: .25em;
|
643
|
+
right: .5em;
|
644
|
+
padding: 0 0.25em;
|
645
|
+
}
|
646
|
+
|
607
647
|
|
608
648
|
a.controls {
|
609
649
|
text-decoration: none;
|
data/public/css/showoff.css
CHANGED
@@ -382,6 +382,10 @@ img#disconnected {
|
|
382
382
|
margin-right: 8px;
|
383
383
|
}
|
384
384
|
|
385
|
+
.sideMenu hr {
|
386
|
+
clear: both;
|
387
|
+
}
|
388
|
+
|
385
389
|
.buttonWrapper {
|
386
390
|
line-height: 48px;
|
387
391
|
padding: 0 16px;
|
@@ -577,6 +581,12 @@ pre.highlight code.language-powershellconsole.nochrome {
|
|
577
581
|
border-radius: 4px;
|
578
582
|
}
|
579
583
|
|
584
|
+
pre.highlight code .highlightedLine {
|
585
|
+
background-color: #f5e2e2;
|
586
|
+
display: inline-block;
|
587
|
+
width: 100%;
|
588
|
+
}
|
589
|
+
|
580
590
|
/* to avoid breaking changes */
|
581
591
|
.highlight .language-shell {
|
582
592
|
display: block;
|
@@ -747,14 +757,6 @@ form .element {
|
|
747
757
|
/*****************
|
748
758
|
*** modals ***
|
749
759
|
*****************/
|
750
|
-
#help-modal {
|
751
|
-
font-size: .9em;
|
752
|
-
}
|
753
|
-
|
754
|
-
#help-modal .ui-dialog .ui-dialog-title {
|
755
|
-
font-size: 1em;
|
756
|
-
}
|
757
|
-
|
758
760
|
.ui-dialog.ui-widget-content {
|
759
761
|
border: none;
|
760
762
|
box-shadow:0 0 25px rgba(0,0,0,0.35);
|
@@ -812,6 +814,15 @@ form .element {
|
|
812
814
|
display: none;
|
813
815
|
}
|
814
816
|
|
817
|
+
#help-modal {
|
818
|
+
display: none;
|
819
|
+
font-size: .9em;
|
820
|
+
}
|
821
|
+
|
822
|
+
#help-modal .ui-dialog .ui-dialog-title {
|
823
|
+
font-size: 1em;
|
824
|
+
}
|
825
|
+
|
815
826
|
#help div {
|
816
827
|
padding: 6px 0;
|
817
828
|
}
|
@@ -1041,6 +1052,7 @@ form .element {
|
|
1041
1052
|
|
1042
1053
|
.callout {
|
1043
1054
|
padding: 1em;
|
1055
|
+
position: relative;
|
1044
1056
|
line-height: 1.2em;
|
1045
1057
|
border: 1px solid #222;
|
1046
1058
|
border-radius: 4px;
|
@@ -1054,7 +1066,18 @@ form .element {
|
|
1054
1066
|
font-size: 2em;
|
1055
1067
|
text-decoration: inherit;
|
1056
1068
|
position: absolute;
|
1057
|
-
left:
|
1069
|
+
left: 0.3em;
|
1070
|
+
}
|
1071
|
+
.callout:after {
|
1072
|
+
font-family: FontAwesome;
|
1073
|
+
font-style: normal;
|
1074
|
+
font-weight: normal;
|
1075
|
+
font-size: 1.5em;
|
1076
|
+
text-decoration: inherit;
|
1077
|
+
position: absolute;
|
1078
|
+
left: 0.5em;
|
1079
|
+
top: 0.65em;
|
1080
|
+
color: #fff;
|
1058
1081
|
}
|
1059
1082
|
|
1060
1083
|
.callout.info,
|
@@ -1064,7 +1087,8 @@ form .element {
|
|
1064
1087
|
.callout.stop,
|
1065
1088
|
.callout.thumbsup,
|
1066
1089
|
.callout.conversation,
|
1067
|
-
.callout.glossary
|
1090
|
+
.callout.glossary,
|
1091
|
+
.callout.terminal {
|
1068
1092
|
padding-left: 3em;
|
1069
1093
|
}
|
1070
1094
|
|
@@ -1077,6 +1101,10 @@ form .element {
|
|
1077
1101
|
.callout.conversation:before { content: "\f0e5"; } /* fa-comment */
|
1078
1102
|
.callout.glossary:before { content: "\f02d"; } /* fa-book */
|
1079
1103
|
|
1104
|
+
.callout.terminal:before { content: "\f0c8"; } /* fa-square */
|
1105
|
+
.callout.terminal:after { content: "\f120"; } /* fa-terminal */
|
1106
|
+
|
1107
|
+
|
1080
1108
|
/* callouts used on error pages */
|
1081
1109
|
.error .callout {
|
1082
1110
|
padding-left: 4em;
|
@@ -1117,6 +1145,14 @@ a.term:after {
|
|
1117
1145
|
*** glossary ***
|
1118
1146
|
**********************/
|
1119
1147
|
|
1148
|
+
/**************************
|
1149
|
+
*** activity indicators ***
|
1150
|
+
***************************/
|
1151
|
+
.slide.activity .activityToggle {
|
1152
|
+
position: absolute;
|
1153
|
+
bottom: 1em;
|
1154
|
+
right: 1em;
|
1155
|
+
}
|
1120
1156
|
|
1121
1157
|
/* Render hidden headlines so that when we print with wkhtmltopdf, we can use section
|
1122
1158
|
titles for page headers. it would make sense to put this in the print section, but
|
data/public/js/presenter.js
CHANGED
@@ -30,7 +30,10 @@ $(document).ready(function(){
|
|
30
30
|
$("#settings").click( function() { $("#settings-modal").dialog("open"); });
|
31
31
|
$("#slideSource a").click( function() { openEditor() });
|
32
32
|
$("#notesToggle").click( function() { toggleNotes() });
|
33
|
-
$("#clearCookies").click( function() {
|
33
|
+
$("#clearCookies").click( function() {
|
34
|
+
clearCookies();
|
35
|
+
location.reload(false);
|
36
|
+
});
|
34
37
|
$("#nextWinCancel").click( function() { chooseLayout('default') });
|
35
38
|
$("#openNextWindow").click(function() { openNext() });
|
36
39
|
|
@@ -514,6 +517,15 @@ function updatePace() {
|
|
514
517
|
var position = Math.max(Math.min(sum, 90), 10); // between 10 and 90
|
515
518
|
$("#paceMarker").css({ left: position+"%" });
|
516
519
|
|
520
|
+
if (position > 50) {
|
521
|
+
$("#feedbackPace .obscure.left").css({ width: "50%" });
|
522
|
+
$("#feedbackPace .obscure.right").css({ width: (100-position)+"%" });
|
523
|
+
}
|
524
|
+
else {
|
525
|
+
$("#feedbackPace .obscure.right").css({ width: "50%" });
|
526
|
+
$("#feedbackPace .obscure.left").css({ width: position+"%" });
|
527
|
+
}
|
528
|
+
|
517
529
|
if(position > 75) {
|
518
530
|
$("#paceFast").show();
|
519
531
|
} else {
|
@@ -526,6 +538,10 @@ function updatePace() {
|
|
526
538
|
}
|
527
539
|
}
|
528
540
|
|
541
|
+
function updateActivityCompletion(count) {
|
542
|
+
currentSlide.children('.count').text(count);
|
543
|
+
}
|
544
|
+
|
529
545
|
// extend this function to add presenter bits
|
530
546
|
var origGotoSlide = gotoSlide;
|
531
547
|
gotoSlide = function (slideNum)
|
@@ -663,6 +679,10 @@ function postSlide() {
|
|
663
679
|
$("#notes div.form.wrapper").each(function(e) {
|
664
680
|
renderFormInterval = renderFormWatcher($(this));
|
665
681
|
});
|
682
|
+
|
683
|
+
if(currentSlide.hasClass('activity')) {
|
684
|
+
currentSlide.children('.activityToggle').replaceWith('<span class="count">0</span>');
|
685
|
+
}
|
666
686
|
}
|
667
687
|
}
|
668
688
|
|
data/public/js/showoff.js
CHANGED
@@ -19,6 +19,7 @@ var lastMessageGuid = 0
|
|
19
19
|
var query;
|
20
20
|
var section = 'handouts'; // default to showing handout notes for display view
|
21
21
|
var slideStartTime = new Date().getTime()
|
22
|
+
var activityIncomplete = false; // slides won't advance when this is on
|
22
23
|
|
23
24
|
var loadSlidesBool
|
24
25
|
var loadSlidesPrefix
|
@@ -119,9 +120,8 @@ function setupPreso(load_slides, prefix) {
|
|
119
120
|
});
|
120
121
|
|
121
122
|
// Open up our control socket
|
122
|
-
|
123
|
-
|
124
|
-
}
|
123
|
+
connectControlChannel();
|
124
|
+
|
125
125
|
}
|
126
126
|
|
127
127
|
function loadSlides(load_slides, prefix, reload, hard) {
|
@@ -183,6 +183,16 @@ function initializePresentation(prefix) {
|
|
183
183
|
$('pre.highlight code').each(function(i, block) {
|
184
184
|
try {
|
185
185
|
hljs.highlightBlock(block);
|
186
|
+
|
187
|
+
// Highlight requested lines
|
188
|
+
block.innerHTML = block.innerHTML.split(/\r?\n/).map(function (line, i) {
|
189
|
+
if (line.indexOf('* ') === 0) {
|
190
|
+
return line.replace(/^\*(.*)$/, '<div class="highlightedLine">$1</div>');
|
191
|
+
}
|
192
|
+
|
193
|
+
return line;
|
194
|
+
}).join('\n');
|
195
|
+
|
186
196
|
} catch(e) {
|
187
197
|
console.log('Syntax highlighting failed on ' + $(this).closest('div.slide').attr('id'));
|
188
198
|
console.log('Syntax highlighting failed for ' + $(this).attr('class'));
|
@@ -218,6 +228,13 @@ function initializePresentation(prefix) {
|
|
218
228
|
}
|
219
229
|
});
|
220
230
|
|
231
|
+
// The display window doesn't need the extra chrome
|
232
|
+
if(typeof(presenterView) != 'undefined') {
|
233
|
+
$('.slide.activity').removeClass('activity').children('.activityToggle').remove();
|
234
|
+
}
|
235
|
+
$('.slide.activity .activityToggle input.activity').checkboxradio();
|
236
|
+
$('.slide.activity .activityToggle input.activity').change(toggleComplete);
|
237
|
+
|
221
238
|
// initialize mermaid, but don't render yet since the slide sizes are indeterminate
|
222
239
|
mermaid.initialize({startOnLoad:false});
|
223
240
|
|
@@ -240,15 +257,15 @@ function zoom(presenter) {
|
|
240
257
|
}
|
241
258
|
|
242
259
|
// Calculate margins to center the thing *before* scaling
|
243
|
-
//
|
244
|
-
if(
|
260
|
+
// Vertically center on presenter, top align everywhere else
|
261
|
+
if(presenter) {
|
262
|
+
var hMargin = (hBody - hSlide) /2;
|
263
|
+
}
|
264
|
+
else {
|
245
265
|
// (center of slide to top) - (half of the zoomed slide)
|
246
266
|
//var hMargin = (hSlide/2 * newZoom) - (hSlide / 2);
|
247
267
|
var hMargin = (hSlide * newZoom - hSlide) / 2;
|
248
268
|
}
|
249
|
-
else {
|
250
|
-
var hMargin = (hBody - hSlide) /2;
|
251
|
-
}
|
252
269
|
var wMargin = (wBody - wSlide) /2;
|
253
270
|
|
254
271
|
preso.css("margin", hMargin + "px " + wMargin + "px");
|
@@ -642,6 +659,21 @@ function showSlide(back_step, updatepv) {
|
|
642
659
|
// copy notes to the notes field for mobile.
|
643
660
|
postSlide();
|
644
661
|
|
662
|
+
// is this an activity slide that has not yet been marked complete?
|
663
|
+
if (currentSlide.hasClass('activity')) {
|
664
|
+
if (currentSlide.find('input.activity').is(":checked")) {
|
665
|
+
activityIncomplete = false;
|
666
|
+
sendActivityStatus(true);
|
667
|
+
}
|
668
|
+
else {
|
669
|
+
activityIncomplete = true;
|
670
|
+
sendActivityStatus(false);
|
671
|
+
}
|
672
|
+
}
|
673
|
+
else {
|
674
|
+
activityIncomplete = false;
|
675
|
+
}
|
676
|
+
|
645
677
|
// make all bigly text tremendous
|
646
678
|
currentSlide.children('.content.bigtext').bigtext();
|
647
679
|
|
@@ -730,6 +762,10 @@ function submitForm(form) {
|
|
730
762
|
var submit = form.find("input[type=submit]")
|
731
763
|
submit.attr("disabled", "disabled");
|
732
764
|
submit.removeClass("dirty");
|
765
|
+
|
766
|
+
// stop blocking follow mode
|
767
|
+
activityIncomplete = false;
|
768
|
+
getPosition();
|
733
769
|
});
|
734
770
|
}
|
735
771
|
}
|
@@ -758,7 +794,10 @@ function validateForm(form) {
|
|
758
794
|
function enableForm(element) {
|
759
795
|
var submit = element.closest('form').find(':submit')
|
760
796
|
submit.removeAttr("disabled");
|
761
|
-
submit.addClass("dirty")
|
797
|
+
submit.addClass("dirty");
|
798
|
+
|
799
|
+
// once a form is started, stop following the presenter
|
800
|
+
activityIncomplete = true;
|
762
801
|
}
|
763
802
|
|
764
803
|
function renderFormWatcher(element) {
|
@@ -996,6 +1035,9 @@ function parseMessage(data) {
|
|
996
1035
|
removeQuestion(command["questionID"]);
|
997
1036
|
break;
|
998
1037
|
|
1038
|
+
case 'activity':
|
1039
|
+
updateActivityCompletion(command['count']);
|
1040
|
+
|
999
1041
|
case 'annotation':
|
1000
1042
|
invokeAnnotation(command["type"], command["x"], command["y"]);
|
1001
1043
|
break;
|
@@ -1063,6 +1105,12 @@ function sendAnnotationConfig(setting, value) {
|
|
1063
1105
|
}
|
1064
1106
|
}
|
1065
1107
|
|
1108
|
+
function sendActivityStatus(status) {
|
1109
|
+
if (ws.readyState == WebSocket.OPEN) {
|
1110
|
+
ws.send(JSON.stringify({ message: 'activity', slide: slidenum, status: status }));
|
1111
|
+
}
|
1112
|
+
}
|
1113
|
+
|
1066
1114
|
function invokeAnnotation(type, x, y) {
|
1067
1115
|
switch (type) {
|
1068
1116
|
case 'erase':
|
@@ -1114,7 +1162,7 @@ function editSlide() {
|
|
1114
1162
|
}
|
1115
1163
|
|
1116
1164
|
function follow(slide, newIncrement) {
|
1117
|
-
if (mode.follow) {
|
1165
|
+
if (mode.follow && ! activityIncomplete) {
|
1118
1166
|
var lastSlide = slidenum;
|
1119
1167
|
console.log("New slide: " + slide);
|
1120
1168
|
gotoSlide(slide);
|
@@ -1360,6 +1408,20 @@ function getKeyName (event) {
|
|
1360
1408
|
return keyName;
|
1361
1409
|
}
|
1362
1410
|
|
1411
|
+
function toggleComplete() {
|
1412
|
+
if($(this).is(':checked')) {
|
1413
|
+
activityIncomplete = false;
|
1414
|
+
sendActivityStatus(true);
|
1415
|
+
if(mode.follow) {
|
1416
|
+
getPosition();
|
1417
|
+
}
|
1418
|
+
}
|
1419
|
+
else {
|
1420
|
+
activityIncomplete = true;
|
1421
|
+
sendActivityStatus(false);
|
1422
|
+
}
|
1423
|
+
}
|
1424
|
+
|
1363
1425
|
function toggleDebug () {
|
1364
1426
|
debugMode = !debugMode;
|
1365
1427
|
doDebugStuff();
|
data/views/presenter.erb
CHANGED
@@ -64,18 +64,29 @@
|
|
64
64
|
</span>
|
65
65
|
<div id="timerDisplay"></div>
|
66
66
|
</div>
|
67
|
+
|
68
|
+
<% if @feedback then %>
|
67
69
|
<div id="feedbackPace">
|
70
|
+
<div class="gradient"> </div>
|
71
|
+
<div class="left obscure"> </div>
|
72
|
+
<div class="right obscure"> </div>
|
68
73
|
<span id="paceSlow">Speed Up!</span>
|
69
74
|
<span id="paceFast">Slow Down!</span>
|
70
75
|
<img id="paceMarker" src="<%= @asset_path %>/css/paceMarker.png" />
|
71
76
|
</div>
|
77
|
+
<% end %>
|
78
|
+
|
72
79
|
<div id="navigation" class="submenu"></div>
|
73
80
|
<div id="navigationHover"></div>
|
81
|
+
|
82
|
+
<% if @feedback then %>
|
74
83
|
<div id="questions">
|
75
84
|
<h3>Audience Questions</h3>
|
76
85
|
<ol id="unanswered"></ol>
|
77
86
|
<ol id="answered"></ol>
|
78
87
|
</div>
|
88
|
+
<% end %>
|
89
|
+
|
79
90
|
</div>
|
80
91
|
|
81
92
|
<div id="presenter">
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: showoff
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.17.
|
4
|
+
version: 0.17.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Scott Chacon
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2017-03-
|
12
|
+
date: 2017-03-23 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: sinatra
|
@@ -165,6 +165,20 @@ dependencies:
|
|
165
165
|
- - "~>"
|
166
166
|
- !ruby/object:Gem::Version
|
167
167
|
version: '1.3'
|
168
|
+
- !ruby/object:Gem::Dependency
|
169
|
+
name: commonmarker
|
170
|
+
requirement: !ruby/object:Gem::Requirement
|
171
|
+
requirements:
|
172
|
+
- - "<="
|
173
|
+
- !ruby/object:Gem::Version
|
174
|
+
version: 0.14.4
|
175
|
+
type: :runtime
|
176
|
+
prerelease: false
|
177
|
+
version_requirements: !ruby/object:Gem::Requirement
|
178
|
+
requirements:
|
179
|
+
- - "<="
|
180
|
+
- !ruby/object:Gem::Version
|
181
|
+
version: 0.14.4
|
168
182
|
- !ruby/object:Gem::Dependency
|
169
183
|
name: mg
|
170
184
|
requirement: !ruby/object:Gem::Requirement
|
@@ -212,7 +226,6 @@ files:
|
|
212
226
|
- views/index.erb
|
213
227
|
- views/onepage.erb
|
214
228
|
- views/presenter.erb
|
215
|
-
- views/stats copy.erb
|
216
229
|
- views/stats.erb
|
217
230
|
- public/css/TimeCircles-89ac5ae.css
|
218
231
|
- public/css/disconnected-large.png
|
@@ -328,10 +341,15 @@ files:
|
|
328
341
|
- public/css/highlight/vs.css
|
329
342
|
- public/css/highlight/xcode.css
|
330
343
|
- public/css/highlight/zenburn.css
|
344
|
+
- public/css/images/ui-icons_444444_256x240.png
|
345
|
+
- public/css/images/ui-icons_555555_256x240.png
|
346
|
+
- public/css/images/ui-icons_777620_256x240.png
|
347
|
+
- public/css/images/ui-icons_777777_256x240.png
|
348
|
+
- public/css/images/ui-icons_cc0000_256x240.png
|
349
|
+
- public/css/images/ui-icons_ffffff_256x240.png
|
331
350
|
- public/css/jquery-ui-1.12.1.css
|
332
351
|
- public/css/mermaid-6.0.0.css
|
333
352
|
- public/css/onepage.css
|
334
|
-
- public/css/pace.png
|
335
353
|
- public/css/paceMarker.png
|
336
354
|
- public/css/presenter.css
|
337
355
|
- public/css/showoff.css
|
data/public/css/pace.png
DELETED
Binary file
|
data/views/stats copy.erb
DELETED
@@ -1,70 +0,0 @@
|
|
1
|
-
<!DOCTYPE html>
|
2
|
-
|
3
|
-
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
4
|
-
<head>
|
5
|
-
<%= erb :header_mini %>
|
6
|
-
|
7
|
-
<script type="text/javascript">
|
8
|
-
$(document).ready(function(){ setupStats(); });
|
9
|
-
</script>
|
10
|
-
</head>
|
11
|
-
|
12
|
-
<body id="stats">
|
13
|
-
<div id="wrapper">
|
14
|
-
<h1>Viewing Statistics</h1>
|
15
|
-
|
16
|
-
<div id="least">
|
17
|
-
<h3>Least viewed slides</h3>
|
18
|
-
<% if @least.size > 0 %>
|
19
|
-
<% max = @least.sort_by {|slide, time| -time}[0][1].to_f %>
|
20
|
-
<% timestr = (max > 3599) ? '%H:%M:%S' : '%M:%S' %>
|
21
|
-
<% @least.each do |slide, time| %>
|
22
|
-
<div class="row">
|
23
|
-
<span class="label"><%= slide %></span>
|
24
|
-
<div class="bar" style="width: <%= (time/max)*100 %>%;"> </div>
|
25
|
-
<div class="time"><%= Time.at(time).gmtime.strftime(timestr) %></div>
|
26
|
-
</div>
|
27
|
-
<% end %>
|
28
|
-
<% end %>
|
29
|
-
</div>
|
30
|
-
|
31
|
-
<div id="most">
|
32
|
-
<h3>Most viewed slides</h3>
|
33
|
-
<% if @least.size > 0 %>
|
34
|
-
<% max = @most[0][1].to_f %>
|
35
|
-
<% timestr = (max > 3599) ? '%H:%M:%S' : '%M:%S' %>
|
36
|
-
<% @most.each do |slide, time| %>
|
37
|
-
<div class="row">
|
38
|
-
<span class="label"><%= slide %></span>
|
39
|
-
<div class="bar" style="width: <%= (time/max)*100 %>%;"> </div>
|
40
|
-
<div class="time"><%= Time.at(time).gmtime.strftime(timestr) %></div>
|
41
|
-
</div>
|
42
|
-
<% end %>
|
43
|
-
<% end %>
|
44
|
-
</div>
|
45
|
-
|
46
|
-
<div id="all">
|
47
|
-
<h3>All slides</h3>
|
48
|
-
<%# We reuse the max value calculated from the above step. %>
|
49
|
-
<% @all.sort.map do |slide, time| %>
|
50
|
-
<div class="row">
|
51
|
-
<span class="label"><%= slide %></span>
|
52
|
-
<div class="bar" style="width: <%= (time/max)*100 %>%;"> </div>
|
53
|
-
<div class="time"><%= Time.at(time).gmtime.strftime(timestr) %></div>
|
54
|
-
<% if @counter %>
|
55
|
-
<div class="detail">
|
56
|
-
<% @counter[slide].each do |host, count| %>
|
57
|
-
<div class="row">
|
58
|
-
<span class="label"><%= host %>:</span>
|
59
|
-
<div class="bar" style="width: <%= (count/max)*100 %>%;"> </div>
|
60
|
-
<div class="time"><%= Time.at(count).gmtime.strftime(timestr) %></div>
|
61
|
-
</div>
|
62
|
-
<% end %>
|
63
|
-
</div>
|
64
|
-
<% end %>
|
65
|
-
</div>
|
66
|
-
<% end %>
|
67
|
-
</div>
|
68
|
-
</div>
|
69
|
-
</body>
|
70
|
-
</html>
|