showoff 0.17.1 → 0.17.2
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/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>
|