showoff 0.16.1 → 0.16.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/bin/showoff +17 -8
- data/lib/showoff.rb +250 -56
- data/lib/showoff/version.rb +1 -1
- data/public/css/presenter.css +7 -0
- data/public/css/showoff.css +93 -1
- data/public/js/presenter.js +21 -2
- data/public/js/showoff.js +67 -15
- data/views/header.erb +2 -0
- data/views/header_mini.erb +2 -0
- data/views/stats.erb +45 -54
- metadata +16 -3
- data/public/zoomline-0.0.1.html +0 -2085
data/lib/showoff/version.rb
CHANGED
data/public/css/presenter.css
CHANGED
@@ -78,6 +78,9 @@
|
|
78
78
|
#links a.enabled {
|
79
79
|
background-color: #003d96;
|
80
80
|
}
|
81
|
+
#links a.warning {
|
82
|
+
background-color: #8e030a;
|
83
|
+
}
|
81
84
|
|
82
85
|
#links .mobile {
|
83
86
|
display: none;
|
@@ -109,6 +112,10 @@
|
|
109
112
|
color: #ffffff;
|
110
113
|
}
|
111
114
|
|
115
|
+
#presenterPopup #stats .zoomline {
|
116
|
+
color: #000;
|
117
|
+
}
|
118
|
+
|
112
119
|
#nextWindowConfirmation {
|
113
120
|
right: 0.25em;
|
114
121
|
left: auto;
|
data/public/css/showoff.css
CHANGED
@@ -843,6 +843,7 @@ form .element {
|
|
843
843
|
-webkit-border-radius: 5px;
|
844
844
|
-khtml-border-radius: 5px;
|
845
845
|
border-radius: 5px;
|
846
|
+
cursor: default;
|
846
847
|
}
|
847
848
|
/* and to the bar inside the row */
|
848
849
|
#stats div.bar {
|
@@ -864,6 +865,68 @@ form .element {
|
|
864
865
|
right: 0.25em;
|
865
866
|
}
|
866
867
|
|
868
|
+
/* Add the disclosure triangles */
|
869
|
+
#stats div.row:before {
|
870
|
+
font-family: FontAwesome;
|
871
|
+
font-style: normal;
|
872
|
+
font-weight: normal;
|
873
|
+
font-size: 1.25em;
|
874
|
+
text-decoration: inherit;
|
875
|
+
}
|
876
|
+
#stats div.row.top:hover:before {
|
877
|
+
position: absolute;
|
878
|
+
content: "\f0da"; /* fa-caret-right */
|
879
|
+
margin-left: -0.5em;
|
880
|
+
}
|
881
|
+
#stats div.row.top.active:before {
|
882
|
+
position: absolute;
|
883
|
+
content: "\f0d7"; /* fa-caret-down */
|
884
|
+
margin-left: -0.75em;
|
885
|
+
}
|
886
|
+
|
887
|
+
#stats h2 {
|
888
|
+
border-bottom: 1px solid #eee;
|
889
|
+
}
|
890
|
+
#stats #viewers,
|
891
|
+
#stats #elapsed {
|
892
|
+
text-align: center;
|
893
|
+
min-height: 2em;
|
894
|
+
}
|
895
|
+
#stats #viewers.zoomline,
|
896
|
+
#stats #elapsed.zoomline {
|
897
|
+
text-align: left;
|
898
|
+
}
|
899
|
+
#stats .zoomline .col.current,
|
900
|
+
#stats .zoomline .col.current .inner {
|
901
|
+
background-color: #ffb105;
|
902
|
+
min-width: 5px;
|
903
|
+
}
|
904
|
+
#stats .zoomline .col.current:hover {
|
905
|
+
min-width: 10px;
|
906
|
+
background: #ffb105;
|
907
|
+
background: linear-gradient(#efefec, #ffb105);
|
908
|
+
}
|
909
|
+
#stats .zoomline .col.current:hover .inner,
|
910
|
+
#stats .zoomline .col.current .inner:hover {
|
911
|
+
background-color: #f3708d;
|
912
|
+
}
|
913
|
+
|
914
|
+
|
915
|
+
#stats #stray,
|
916
|
+
#stats #idle {
|
917
|
+
display: none;
|
918
|
+
width: 75%;
|
919
|
+
margin: 1em auto;
|
920
|
+
padding: 0.5em;
|
921
|
+
border: 1px solid #ccc;
|
922
|
+
}
|
923
|
+
#stats #stray {
|
924
|
+
background-color: #8e030a;
|
925
|
+
}
|
926
|
+
#stats #idle {
|
927
|
+
background-color: #3a64c1;
|
928
|
+
}
|
929
|
+
|
867
930
|
/* style all three boxes */
|
868
931
|
#stats div#least,
|
869
932
|
#stats div#most,
|
@@ -903,6 +966,7 @@ form .element {
|
|
903
966
|
}
|
904
967
|
#stats div#all div.bar {
|
905
968
|
background: #66b366;
|
969
|
+
border-color: #000;
|
906
970
|
}
|
907
971
|
|
908
972
|
/* detail view */
|
@@ -944,7 +1008,8 @@ form .element {
|
|
944
1008
|
.callout.exercise,
|
945
1009
|
.callout.stop,
|
946
1010
|
.callout.thumbsup,
|
947
|
-
.callout.conversation
|
1011
|
+
.callout.conversation,
|
1012
|
+
.callout.glossary {
|
948
1013
|
padding-left: 3em;
|
949
1014
|
}
|
950
1015
|
|
@@ -955,6 +1020,7 @@ form .element {
|
|
955
1020
|
.callout.stop:before { content: "\f05e"; } /* fa-ban */
|
956
1021
|
.callout.thumbsup:before { content: "\f164"; } /* fa-thumbs-up */
|
957
1022
|
.callout.conversation:before { content: "\f0e5"; } /* fa-comment */
|
1023
|
+
.callout.glossary:before { content: "\f02d"; } /* fa-book */
|
958
1024
|
|
959
1025
|
/* callouts used on error pages */
|
960
1026
|
.error .callout {
|
@@ -970,6 +1036,32 @@ form .element {
|
|
970
1036
|
/**********************
|
971
1037
|
*** end callouts ***
|
972
1038
|
**********************/
|
1039
|
+
.callout.glossary a.label,
|
1040
|
+
ul.glossary.terms a.label {
|
1041
|
+
display: block;
|
1042
|
+
font-weight: 600;
|
1043
|
+
text-decoration: none;
|
1044
|
+
}
|
1045
|
+
ul.glossary.terms a.return {
|
1046
|
+
text-decoration: none;
|
1047
|
+
padding-right: 3em;
|
1048
|
+
}
|
1049
|
+
a.term {
|
1050
|
+
text-decoration: none;
|
1051
|
+
}
|
1052
|
+
a.term:after {
|
1053
|
+
font-family: FontAwesome;
|
1054
|
+
font-size: 0.8em;
|
1055
|
+
vertical-align: super;
|
1056
|
+
content: "\f27b";
|
1057
|
+
text-decoration: none;/* fa-commenting-o */
|
1058
|
+
}
|
1059
|
+
|
1060
|
+
|
1061
|
+
/**********************
|
1062
|
+
*** glossary ***
|
1063
|
+
**********************/
|
1064
|
+
|
973
1065
|
|
974
1066
|
/* Render hidden headlines so that when we print with wkhtmltopdf, we can use section
|
975
1067
|
titles for page headers. it would make sense to put this in the print section, but
|
data/public/js/presenter.js
CHANGED
@@ -126,6 +126,27 @@ $(document).ready(function(){
|
|
126
126
|
|
127
127
|
setInterval(function() { updatePace() }, 1000);
|
128
128
|
|
129
|
+
setInterval(function() {
|
130
|
+
$.getJSON("/stats_data", function( json ) {
|
131
|
+
var percent = json['stray_p'];
|
132
|
+
if(percent > 25) {
|
133
|
+
$('#topbar #statslink').addClass('warning');
|
134
|
+
$('#topbar #statslink').attr('title', percent + "% of your audience is not viewing the same slide you are.");
|
135
|
+
}
|
136
|
+
else {
|
137
|
+
$('#stray').hide(); // in case the popup is open
|
138
|
+
$('#topbar #statslink').removeClass('warning');
|
139
|
+
$('#topbar #statslink').attr('title', "");
|
140
|
+
}
|
141
|
+
|
142
|
+
if( $('#presenterPopup #stats').is(':visible') ) {
|
143
|
+
setupStats(json);
|
144
|
+
}
|
145
|
+
});
|
146
|
+
|
147
|
+
}, 30000);
|
148
|
+
|
149
|
+
|
129
150
|
// Tell the showoff server that we're a presenter
|
130
151
|
register();
|
131
152
|
|
@@ -172,8 +193,6 @@ function presenterPopupToggle(page, event) {
|
|
172
193
|
content.append($(data).siblings('#wrapper').html());
|
173
194
|
popup.append(content);
|
174
195
|
|
175
|
-
setupStats(); // this function is in showoff.js because /stats does not load presenter.js
|
176
|
-
|
177
196
|
$('body').append(popup);
|
178
197
|
popup.slideDown(200); // #presenterPopup is display: none by default
|
179
198
|
});
|
data/public/js/showoff.js
CHANGED
@@ -448,6 +448,10 @@ function currentSlideFromName(name) {
|
|
448
448
|
var count = 0;
|
449
449
|
if(name.length > 0 ) {
|
450
450
|
slides.each(function(s, slide) {
|
451
|
+
if (name == $(slide).attr("id") ) {
|
452
|
+
found = count;
|
453
|
+
return false;
|
454
|
+
}
|
451
455
|
if (name == $(slide).find(".content").attr("ref") ) {
|
452
456
|
found = count;
|
453
457
|
return false;
|
@@ -460,12 +464,13 @@ function currentSlideFromName(name) {
|
|
460
464
|
|
461
465
|
function currentSlideFromParams() {
|
462
466
|
var result;
|
463
|
-
|
464
|
-
|
467
|
+
// Match numeric slide hashes: #241
|
468
|
+
if (result = window.location.hash.match(/^#([0-9]+)$/)) {
|
469
|
+
return result[1] - 1;
|
465
470
|
}
|
466
|
-
|
467
|
-
|
468
|
-
return currentSlideFromName(
|
471
|
+
// Match slide, with optional internal mark: #slideName(+internal)
|
472
|
+
else if (result = window.location.hash.match(/^#([^+]+)\+?(.*)?$/)) {
|
473
|
+
return currentSlideFromName(result[1]);
|
469
474
|
}
|
470
475
|
}
|
471
476
|
|
@@ -1053,18 +1058,24 @@ function feedbackActivity() {
|
|
1053
1058
|
setTimeout(function() { $("#hamburger").removeClass('highlight') }, 75);
|
1054
1059
|
}
|
1055
1060
|
|
1056
|
-
function track() {
|
1061
|
+
function track(current) {
|
1057
1062
|
if (mode.track && ws.readyState == WebSocket.OPEN) {
|
1058
|
-
var slideName
|
1059
|
-
|
1060
|
-
|
1063
|
+
var slideName = $("#slideFilename").text() || $("#slideFile").text(); // yey for consistency
|
1064
|
+
|
1065
|
+
if(current) {
|
1066
|
+
ws.send(JSON.stringify({ message: 'track', slide: slideName}));
|
1067
|
+
}
|
1068
|
+
else {
|
1069
|
+
var slideEndTime = new Date().getTime();
|
1070
|
+
var elapsedTime = slideEndTime - slideStartTime;
|
1061
1071
|
|
1062
|
-
|
1063
|
-
|
1072
|
+
// reset the timer
|
1073
|
+
slideStartTime = slideEndTime;
|
1064
1074
|
|
1065
|
-
|
1066
|
-
|
1067
|
-
|
1075
|
+
if (elapsedTime > 1000) {
|
1076
|
+
elapsedTime /= 1000;
|
1077
|
+
ws.send(JSON.stringify({ message: 'track', slide: slideName, time: elapsedTime}));
|
1078
|
+
}
|
1068
1079
|
}
|
1069
1080
|
}
|
1070
1081
|
}
|
@@ -1170,6 +1181,9 @@ function postSlide() {
|
|
1170
1181
|
}
|
1171
1182
|
|
1172
1183
|
$('#notes').html(notes);
|
1184
|
+
|
1185
|
+
// tell Showoff what slide we ended up on
|
1186
|
+
track(true);
|
1173
1187
|
}
|
1174
1188
|
}
|
1175
1189
|
|
@@ -1620,12 +1634,50 @@ function togglePause() {
|
|
1620
1634
|
Stats page
|
1621
1635
|
********************/
|
1622
1636
|
|
1623
|
-
function setupStats()
|
1637
|
+
function setupStats(data)
|
1624
1638
|
{
|
1625
1639
|
$("#stats div#all div.detail").hide();
|
1626
1640
|
$("#stats div#all div.row").click(function() {
|
1641
|
+
$(this).toggleClass('active');
|
1627
1642
|
$(this).find("div.detail").slideToggle("fast");
|
1628
1643
|
});
|
1644
|
+
|
1645
|
+
['stray', 'idle'].forEach(function(stat){
|
1646
|
+
var percent = data[stat+'_p'];
|
1647
|
+
var selector = '#'+stat;
|
1648
|
+
|
1649
|
+
if(percent > 25) {
|
1650
|
+
$(selector).show();
|
1651
|
+
$(selector+' .label').text(percent+'%');
|
1652
|
+
}
|
1653
|
+
else {
|
1654
|
+
$(selector).hide();
|
1655
|
+
}
|
1656
|
+
});
|
1657
|
+
|
1658
|
+
var location = window.location.pathname == '/presenter' ? '#' : '/#';
|
1659
|
+
var viewers = data['viewers'];
|
1660
|
+
if (viewers) {
|
1661
|
+
if (viewers.length == 1 && viewers[0][3] == 'current') {
|
1662
|
+
$("#viewers").removeClass('zoomline');
|
1663
|
+
$("#viewers").text("All audience members are viewing the presenter's slide.");
|
1664
|
+
}
|
1665
|
+
else {
|
1666
|
+
$("#viewers").zoomline({
|
1667
|
+
max: data['viewmax'],
|
1668
|
+
data: viewers,
|
1669
|
+
click: function(element) { window.location = (location + element.attr("data-left")); }
|
1670
|
+
});
|
1671
|
+
}
|
1672
|
+
}
|
1673
|
+
|
1674
|
+
if (data['elapsed']) {
|
1675
|
+
$("#elapsed").zoomline({
|
1676
|
+
max: data['maxtime'],
|
1677
|
+
data: data['elapsed'],
|
1678
|
+
click: function(element) { window.location = (location + element.attr("data-left")); }
|
1679
|
+
});
|
1680
|
+
}
|
1629
1681
|
}
|
1630
1682
|
|
1631
1683
|
/* Is this a mobile device? */
|
data/views/header.erb
CHANGED
@@ -11,6 +11,7 @@
|
|
11
11
|
<link rel="stylesheet" type="text/css" href="<%= @asset_path %>/css/mermaid-6.0.0.css" />
|
12
12
|
<link rel="stylesheet" type="text/css" href="<%= @asset_path %>/css/font-awesome-4.4.0/css/font-awesome.min.css">
|
13
13
|
<link rel="stylesheet" type="text/css" href="<%= @asset_path %>/css/showoff.css?v=<%= SHOWOFF_VERSION %>" />
|
14
|
+
<link rel="stylesheet" type="text/css" href="<%= @asset_path %>/css/zoomline-0.0.1.css">
|
14
15
|
|
15
16
|
<script type="text/javascript" src="<%= @asset_path %>/js/jquery-2.1.4.min.js"></script>
|
16
17
|
|
@@ -20,6 +21,7 @@
|
|
20
21
|
<script type="text/javascript" src="<%= @asset_path %>/js/jquery.doubletap-4ff02c5.js"></script>
|
21
22
|
<script type="text/javascript" src="<%= @asset_path %>/js/jTypeWriter-1.1.js"></script>
|
22
23
|
<script type="text/javascript" src="<%= @asset_path %>/js/bigtext-0.1.8.js"></script>
|
24
|
+
<script type="text/javascript" src="<%= @asset_path %>/js/zoomline-0.0.1.js"></script>
|
23
25
|
<script type="text/javascript" src="<%= @asset_path %>/js/highlight.pack-9.2.0.js"></script>
|
24
26
|
<script type="text/javascript" src="<%= @asset_path %>/js/mermaid-6.0.0-min.js"></script>
|
25
27
|
|
data/views/header_mini.erb
CHANGED
@@ -9,8 +9,10 @@
|
|
9
9
|
|
10
10
|
<link rel="stylesheet" type="text/css" href="<%= @asset_path %>/css/showoff.css?v=<%= SHOWOFF_VERSION %>" />
|
11
11
|
<link rel="stylesheet" type="text/css" href="<%= @asset_path %>/css/font-awesome-4.4.0/css/font-awesome.min.css">
|
12
|
+
<link rel="stylesheet" type="text/css" href="<%= @asset_path %>/css/zoomline-0.0.1.css">
|
12
13
|
|
13
14
|
<script type="text/javascript" src="<%= @asset_path %>/js/jquery-2.1.4.min.js"></script>
|
15
|
+
<script type="text/javascript" src="<%= @asset_path %>/js/zoomline-0.0.1.js"></script>
|
14
16
|
|
15
17
|
<script type="text/javascript" src="<%= @asset_path %>/js/showoff.js?v=<%= SHOWOFF_VERSION %>"></script>
|
16
18
|
|
data/views/stats.erb
CHANGED
@@ -3,68 +3,59 @@
|
|
3
3
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
4
4
|
<head>
|
5
5
|
<%= erb :header_mini %>
|
6
|
-
|
7
|
-
<script type="text/javascript">
|
8
|
-
$(document).ready(function(){ setupStats(); });
|
9
|
-
</script>
|
10
6
|
</head>
|
11
7
|
|
12
8
|
<body id="stats">
|
13
9
|
<div id="wrapper">
|
14
|
-
<h1>Viewing Statistics</h1>
|
15
10
|
|
16
|
-
<
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
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>
|
11
|
+
<script type="text/javascript">
|
12
|
+
$(document).ready(function(){
|
13
|
+
$.getJSON("/stats_data", function( json ) {
|
14
|
+
setupStats(json);
|
15
|
+
});
|
16
|
+
});
|
17
|
+
</script>
|
30
18
|
|
31
|
-
<
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
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>
|
19
|
+
<h1>Viewing Statistics</h1>
|
20
|
+
<p id="stray"><span class="label"></span> of your audience is not viewing the same slide you are.</p>
|
21
|
+
<p id="idle"><span class="label"></span> of your audience is idle.</p>
|
22
|
+
<h2>Slides currently being viewed:</h2>
|
23
|
+
<div id="viewers">No data to display.</div>
|
24
|
+
<h2>Elapsed time spent on each slide:</h2>
|
25
|
+
<div id="elapsed">No data to display.</div>
|
26
|
+
</div>
|
45
27
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
28
|
+
<div id="all">
|
29
|
+
<h3>All slides</h3>
|
30
|
+
<% max = @all.sort_by {|slide, time| -time}[0][1].to_f rescue 0 %>
|
31
|
+
<% @all.sort.map do |slide, time| %>
|
32
|
+
<% next if slide.empty? %>
|
33
|
+
<% timestr = (time < 60) ? ':%S' : (time < 3599) ? '%M:%S' : '%H:%M:%S' %>
|
34
|
+
<div class="row top">
|
35
|
+
<span class="label"><%= slide %></span>
|
36
|
+
<div class="bar" style="width: <%= (time/max)*100 %>%;"> </div>
|
37
|
+
<div class="time"><%= Time.at(time).gmtime.strftime(timestr) %></div>
|
38
|
+
<% if @counter %>
|
39
|
+
<div class="detail">
|
40
|
+
<% @counter[slide].each do |host, views| %>
|
41
|
+
<% count = views.inject(0) { |sum, view| sum += view['elapsed'] } %>
|
42
|
+
<% timestr = (count < 60) ? ':%S' : (count < 3599) ? '%M:%S' : '%H:%M:%S' %>
|
43
|
+
<div class="row">
|
44
|
+
<% if host == 'presenter' %>
|
45
|
+
<% bgcolor = '' %>
|
58
46
|
<span class="label"><%= host %>:</span>
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
47
|
+
<% else %>
|
48
|
+
<% bgcolor = "background-color: ##{host[0...6]}" %>
|
49
|
+
<% end %>
|
50
|
+
<div class="bar" style="width: <%= (count/max)*100 %>%; <%= bgcolor %>"> </div>
|
51
|
+
<div class="time"><%= Time.at(count).gmtime.strftime(timestr) %></div>
|
52
|
+
</div>
|
53
|
+
<% end %>
|
54
|
+
</div>
|
55
|
+
<% end %>
|
56
|
+
</div>
|
57
|
+
<% end %>
|
68
58
|
</div>
|
59
|
+
|
69
60
|
</body>
|
70
61
|
</html>
|