showoff 0.10.2 → 0.11.0
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 +46 -13
- data/lib/showoff/version.rb +1 -1
- data/lib/showoff.rb +84 -45
- data/public/css/presenter.css +24 -0
- data/public/css/showoff.css +18 -1
- data/public/js/showoff.js +40 -20
- data/views/header.erb +0 -2
- data/views/header_mini.erb +0 -1
- data/views/onepage.erb +3 -3
- metadata +2 -3
- data/public/css/reset.css +0 -53
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b168170069c1f28da7c3fe26bff21344cf8ad6bd
|
4
|
+
data.tar.gz: b42c67ff2736fbeda95819f6c3d61b4a1ea2980b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7056bb877d7f2066f713d31e6e32c58bb6661c52c650f89aa45dc415ef5e96dbf4ff9273746b8373c8c552e9e5290229c60c6fbcefb500da5bd7c13a662b9ba2
|
7
|
+
data.tar.gz: ae64ba89fa60b8a14f64f2cc5392fedf0e26dfc60aa2eaea74c4496d5a52f63fa4f43d5f06a83d42bd43a4abe79ecdf35ad2daaa025f372b2ab5ea0b95628746
|
data/bin/showoff
CHANGED
@@ -117,25 +117,37 @@ default_value "."
|
|
117
117
|
command :serve do |c|
|
118
118
|
|
119
119
|
c.desc 'Show verbose messaging'
|
120
|
-
c.switch :verbose
|
120
|
+
c.switch [:v, :verbose]
|
121
121
|
|
122
122
|
c.desc 'Enable code review'
|
123
|
-
c.switch :review
|
123
|
+
c.switch [:r, :review]
|
124
124
|
|
125
125
|
c.desc 'Enable remote code execution'
|
126
|
-
c.switch [:x
|
126
|
+
c.switch [:x, :executecode]
|
127
|
+
|
128
|
+
c.desc 'Disable content caching'
|
129
|
+
c.switch :nocache
|
127
130
|
|
128
131
|
c.desc 'Port on which to run'
|
129
132
|
c.default_value "9090"
|
130
|
-
c.flag [:p
|
133
|
+
c.flag [:p, :port]
|
131
134
|
|
132
135
|
c.desc 'Host or ip to run on'
|
133
136
|
c.default_value "0.0.0.0"
|
134
|
-
c.flag [:h
|
137
|
+
c.flag [:h, :host]
|
138
|
+
|
139
|
+
c.desc 'Run via HTTPS'
|
140
|
+
c.switch [:s, :ssl]
|
141
|
+
|
142
|
+
c.desc 'Path to SSL certificate. Showoff will generate one automatically if unset.'
|
143
|
+
c.flag :ssl_certificate
|
144
|
+
|
145
|
+
c.desc 'Path to SSL private key. Showoff will generate one automatically if unset.'
|
146
|
+
c.flag :ssl_private_key
|
135
147
|
|
136
148
|
c.desc 'JSON file used to describe presentation'
|
137
149
|
c.default_value "showoff.json"
|
138
|
-
c.flag [:f, :pres_file]
|
150
|
+
c.flag [:f, :file, :pres_file]
|
139
151
|
|
140
152
|
c.action do |global_options,options,args|
|
141
153
|
|
@@ -145,8 +157,15 @@ command :serve do |c|
|
|
145
157
|
raise "This presentation requires Showoff version #{config['version']} or greater."
|
146
158
|
end
|
147
159
|
|
148
|
-
|
149
|
-
|
160
|
+
options[:host] ||= config['host']
|
161
|
+
options[:port] ||= config['port']
|
162
|
+
options[:ssl] ||= config['ssl']
|
163
|
+
options[:ssl_certificate] ||= config['ssl_certificate']
|
164
|
+
options[:ssl_private_key] ||= config['ssl_private_key']
|
165
|
+
|
166
|
+
protocol = options[:ssl] ? 'https' : 'http'
|
167
|
+
host = options[:host] == '0.0.0.0' ? 'localhost' : options[:host]
|
168
|
+
url = "#{protocol}://#{host}:#{options[:p].to_i}"
|
150
169
|
puts "
|
151
170
|
-------------------------
|
152
171
|
|
@@ -160,14 +179,28 @@ To run it from presenter view, go to: [ #{url}/presenter ]
|
|
160
179
|
|
161
180
|
"
|
162
181
|
|
163
|
-
ShowOff.run!
|
164
|
-
:port => options[:
|
165
|
-
:pres_file => options[:
|
182
|
+
ShowOff.run!( :host => options[:host],
|
183
|
+
:port => options[:port].to_i,
|
184
|
+
:pres_file => options[:file],
|
166
185
|
:pres_dir => args[0],
|
167
186
|
:verbose => options[:verbose],
|
168
187
|
:review => options[:review],
|
169
|
-
:execute => options[:
|
170
|
-
:
|
188
|
+
:execute => options[:executecode],
|
189
|
+
:nocache => options[:nocache],
|
190
|
+
:bind => options[:host],
|
191
|
+
) do |server|
|
192
|
+
if options[:ssl]
|
193
|
+
ssl_options = {
|
194
|
+
:cert_chain_file => options[:ssl_certificate],
|
195
|
+
:private_key_file => options[:ssl_private_key],
|
196
|
+
:verify_peer => false,
|
197
|
+
}
|
198
|
+
|
199
|
+
server.ssl = true
|
200
|
+
server.ssl_options = ssl_options
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
171
204
|
end
|
172
205
|
end
|
173
206
|
|
data/lib/showoff/version.rb
CHANGED
data/lib/showoff.rb
CHANGED
@@ -13,7 +13,7 @@ require "#{here}/commandline_parser"
|
|
13
13
|
require "#{here}/keymap"
|
14
14
|
|
15
15
|
begin
|
16
|
-
require '
|
16
|
+
require 'rmagick'
|
17
17
|
rescue LoadError
|
18
18
|
# nop
|
19
19
|
end
|
@@ -121,8 +121,13 @@ class ShowOff < Sinatra::Application
|
|
121
121
|
# Page view time accumulator. Tracks how often slides are viewed by the audience
|
122
122
|
begin
|
123
123
|
@@counter = JSON.parse(File.read("#{settings.statsdir}/#{settings.viewstats}"))
|
124
|
+
|
125
|
+
# port old format stats
|
126
|
+
unless @@counter.has_key? 'user_agents'
|
127
|
+
@@counter = { 'user_agents' => {}, 'pageviews' => @@counter }
|
128
|
+
end
|
124
129
|
rescue
|
125
|
-
@@counter =
|
130
|
+
@@counter = { 'user_agents' => {}, 'pageviews' => {} }
|
126
131
|
end
|
127
132
|
|
128
133
|
# keeps track of form responses. In memory to avoid concurrence issues.
|
@@ -135,6 +140,7 @@ class ShowOff < Sinatra::Application
|
|
135
140
|
@@downloads = Hash.new # Track downloadable files
|
136
141
|
@@cookie = nil # presenter cookie. Identifies the presenter for control messages
|
137
142
|
@@current = Hash.new # The current slide that the presenter is viewing
|
143
|
+
@@cache = nil # Cache slide content for subsequent hits
|
138
144
|
|
139
145
|
# flush stats to disk periodically
|
140
146
|
Thread.new do
|
@@ -458,9 +464,9 @@ class ShowOff < Sinatra::Application
|
|
458
464
|
end
|
459
465
|
end
|
460
466
|
|
461
|
-
# Now add a target so we open all links from notes in a new window
|
467
|
+
# Now add a target so we open all external links from notes in a new window
|
462
468
|
doc.css('a').each do |link|
|
463
|
-
link.set_attribute('target', '_blank')
|
469
|
+
link.set_attribute('target', '_blank') unless link['href'].start_with? '#'
|
464
470
|
end
|
465
471
|
|
466
472
|
doc.to_html
|
@@ -513,7 +519,7 @@ class ShowOff < Sinatra::Application
|
|
513
519
|
form = "<form id='#{title}' action='/form/#{title}' method='POST'>#{content}#{tools}</form>"
|
514
520
|
doc = Nokogiri::HTML::DocumentFragment.parse(form)
|
515
521
|
doc.css('p').each do |p|
|
516
|
-
if p.text =~ /^(\w*) ?(?:->)? ?(
|
522
|
+
if p.text =~ /^(\w*) ?(?:->)? ?(.*)? (\*?)= ?(.*)?$/
|
517
523
|
code = $1
|
518
524
|
id = "#{title}_#{code}"
|
519
525
|
name = $2.empty? ? code : $2
|
@@ -536,19 +542,19 @@ class ShowOff < Sinatra::Application
|
|
536
542
|
str = "<div class='form element #{required}' id='#{id}' data-name='#{code}'>"
|
537
543
|
str << "<label for='#{id}'>#{name}</label>"
|
538
544
|
case rhs
|
539
|
-
when /^\[\s+(\d*)\]$$/ # value = [ 5]
|
545
|
+
when /^\[\s+(\d*)\]$$/ # value = [ 5] (textarea)
|
540
546
|
str << form_element_textarea(id, code, $1)
|
541
|
-
when /^___+(?:\[(\d+)\])?$/ # value = ___[50]
|
547
|
+
when /^___+(?:\[(\d+)\])?$/ # value = ___[50] (text)
|
542
548
|
str << form_element_text(id, code, $1)
|
543
|
-
when /^\(
|
544
|
-
str << form_element_radio(id, code, rhs.scan(/\((
|
545
|
-
when /^\[
|
546
|
-
str << form_element_checkboxes(id, code, rhs.scan(/\[(
|
547
|
-
when /^\{(.*)\}$/ # value = {BOS, SFO, (NYC)}
|
548
|
-
str << form_element_select(id, code, rhs.scan(
|
549
|
-
when /^\{$/ # value = {
|
549
|
+
when /^\(.?\)/ # value = (x) option one (=) opt2 () opt3 -> option 3 (radio)
|
550
|
+
str << form_element_radio(id, code, rhs.scan(/\((.?)\)\s*([^()]+)\s*/))
|
551
|
+
when /^\[.?\]/ # value = [x] option one [=] opt2 [] opt3 -> option 3 (checkboxes)
|
552
|
+
str << form_element_checkboxes(id, code, rhs.scan(/\[(.?)\] ?([^\[\]]+)/))
|
553
|
+
when /^\{(.*)\}$/ # value = {BOS, [SFO], (NYC)} (select shorthand)
|
554
|
+
str << form_element_select(id, code, rhs.scan(/[(\[]?\w+[)\]]?/))
|
555
|
+
when /^\{$/ # value = { (select)
|
550
556
|
str << form_element_select_multiline(id, code, text)
|
551
|
-
when '' # value =
|
557
|
+
when '' # value = (radio/checkbox list)
|
552
558
|
str << form_element_multiline(id, code, text)
|
553
559
|
else
|
554
560
|
@logger.warn "Unmatched form element: #{rhs}"
|
@@ -597,9 +603,13 @@ class ShowOff < Sinatra::Application
|
|
597
603
|
case item
|
598
604
|
when /^ +\((\w+) -> (.+)\),?$/ # (NYC -> New York City)
|
599
605
|
str << "<option value='#{$1}' selected>#{$2}</option>"
|
606
|
+
when /^ +\[(\w+) -> (.+)\],?$/ # [NYC -> New York City]
|
607
|
+
str << "<option value='#{$1}' class='correct'>#{$2}</option>"
|
600
608
|
when /^ +(\w+) -> (.+),?$/ # NYC -> New, York City
|
601
609
|
str << "<option value='#{$1}'>#{$2}</option>"
|
602
|
-
when /^ +\((.+)
|
610
|
+
when /^ +\((.+)\)$/ # (Boston)
|
611
|
+
str << "<option value='#{$1}' selected>#{$1}</option>"
|
612
|
+
when /^ +\[(.+)\]$/ # [Boston]
|
603
613
|
str << "<option value='#{$1}' selected>#{$1}</option>"
|
604
614
|
when /^ +([^\(].+[^\),]),?$/ # Boston
|
605
615
|
str << "<option value='#{$1}'>#{$1}</option>"
|
@@ -613,20 +623,20 @@ class ShowOff < Sinatra::Application
|
|
613
623
|
|
614
624
|
text.split("\n")[1..-1].each do |item|
|
615
625
|
case item
|
616
|
-
when /\((
|
617
|
-
|
618
|
-
type
|
619
|
-
value
|
620
|
-
label
|
621
|
-
when /\[(
|
622
|
-
|
623
|
-
type
|
624
|
-
value
|
625
|
-
label
|
626
|
+
when /\((.?)\)\s*(\w+)\s*(?:->\s*(.*)?)?/
|
627
|
+
modifier = $1
|
628
|
+
type = 'radio'
|
629
|
+
value = $2
|
630
|
+
label = $3 || $2
|
631
|
+
when /\[(.?)\]\s*(\w+)\s*(?:->\s*(.*)?)?/
|
632
|
+
modifier = $1
|
633
|
+
type = 'checkbox'
|
634
|
+
value = $2
|
635
|
+
label = $3 || $2
|
626
636
|
end
|
627
637
|
|
628
638
|
str << '<li>'
|
629
|
-
str << form_element_check_or_radio(type, id, code, value, label,
|
639
|
+
str << form_element_check_or_radio(type, id, code, value, label, modifier)
|
630
640
|
str << '</li>'
|
631
641
|
end
|
632
642
|
str << '</ul>'
|
@@ -635,7 +645,7 @@ class ShowOff < Sinatra::Application
|
|
635
645
|
def form_element_check_or_radio_set(type, id, code, items)
|
636
646
|
str = ''
|
637
647
|
items.each do |item|
|
638
|
-
|
648
|
+
modifier = item[0]
|
639
649
|
|
640
650
|
if item[1] =~ /^(\w*) -> (.*)$/
|
641
651
|
value = $1
|
@@ -644,17 +654,31 @@ class ShowOff < Sinatra::Application
|
|
644
654
|
value = label = item[1]
|
645
655
|
end
|
646
656
|
|
647
|
-
str << form_element_check_or_radio(type, id, code, value, label,
|
657
|
+
str << form_element_check_or_radio(type, id, code, value, label, modifier)
|
648
658
|
end
|
649
659
|
str
|
650
660
|
end
|
651
661
|
|
652
|
-
def form_element_check_or_radio(type, id, code, value, label,
|
662
|
+
def form_element_check_or_radio(type, id, code, value, label, modifier)
|
653
663
|
# yes, value and id are conflated, because this is the id of the parent widget
|
664
|
+
checked = form_checked?(modifier)
|
665
|
+
classes = form_classes(modifier)
|
654
666
|
|
655
667
|
name = (type == 'checkbox') ? "#{code}[]" : code
|
656
|
-
str = "<input type='#{type}' name='#{name}' id='#{id}_#{value}' value='#{value}' #{checked} />"
|
657
|
-
str << "<label for='#{id}_#{value}'>#{label}</label>"
|
668
|
+
str = "<input type='#{type}' name='#{name}' id='#{id}_#{value}' value='#{value}' class='#{classes}' #{checked} />"
|
669
|
+
str << "<label for='#{id}_#{value}' class='#{classes}'>#{label}</label>"
|
670
|
+
end
|
671
|
+
|
672
|
+
def form_classes(modifier)
|
673
|
+
modifier.downcase!
|
674
|
+
classes = []
|
675
|
+
classes << 'correct' if modifier.include?('=')
|
676
|
+
|
677
|
+
classes.join
|
678
|
+
end
|
679
|
+
|
680
|
+
def form_checked?(modifier)
|
681
|
+
modifier.downcase.include?('x') ? "checked='checked'" : ''
|
658
682
|
end
|
659
683
|
|
660
684
|
# TODO: deprecated
|
@@ -931,7 +955,13 @@ class ShowOff < Sinatra::Application
|
|
931
955
|
end
|
932
956
|
|
933
957
|
def slides(static=false)
|
934
|
-
|
958
|
+
# if we have a cache and we're not asking to invalidate it
|
959
|
+
return @@cache if (@@cache and params['cache'] != 'clear')
|
960
|
+
content = get_slides_html(:static=>static)
|
961
|
+
|
962
|
+
# allow command line cache disabling
|
963
|
+
@@cache = content unless settings.nocache
|
964
|
+
content
|
935
965
|
end
|
936
966
|
|
937
967
|
def onepage(static=false)
|
@@ -970,12 +1000,12 @@ class ShowOff < Sinatra::Application
|
|
970
1000
|
|
971
1001
|
def stats()
|
972
1002
|
if request.env['REMOTE_HOST'] == 'localhost'
|
973
|
-
# the presenter should have full stats
|
974
|
-
@counter = @@counter
|
1003
|
+
# the presenter should have full stats in the erb
|
1004
|
+
@counter = @@counter['pageviews']
|
975
1005
|
end
|
976
1006
|
|
977
1007
|
@all = Hash.new
|
978
|
-
@@counter.each do |slide, stats|
|
1008
|
+
@@counter['pageviews'].each do |slide, stats|
|
979
1009
|
@all[slide] = 0
|
980
1010
|
stats.map do |host, visits|
|
981
1011
|
visits.each { |entry| @all[slide] += entry['elapsed'].to_f }
|
@@ -1158,16 +1188,21 @@ class ShowOff < Sinatra::Application
|
|
1158
1188
|
|
1159
1189
|
@@forms[id].each_with_object({}) do |(ip,form), sum|
|
1160
1190
|
form.each do |key, val|
|
1161
|
-
|
1191
|
+
# initialize the object with an empty response if needed
|
1192
|
+
sum[key] ||= { 'count' => 0, 'responses' => {} }
|
1193
|
+
|
1194
|
+
# increment the number of unique responses we've seen
|
1195
|
+
sum[key]['count'] += 1
|
1162
1196
|
|
1197
|
+
responses = sum[key]['responses']
|
1163
1198
|
if val.class == Array
|
1164
1199
|
val.each do |item|
|
1165
|
-
|
1166
|
-
|
1200
|
+
responses[item] ||= 0
|
1201
|
+
responses[item] += 1
|
1167
1202
|
end
|
1168
1203
|
else
|
1169
|
-
|
1170
|
-
|
1204
|
+
responses[val] ||= 0
|
1205
|
+
responses[val] += 1
|
1171
1206
|
end
|
1172
1207
|
end
|
1173
1208
|
end.to_json
|
@@ -1294,14 +1329,18 @@ class ShowOff < Sinatra::Application
|
|
1294
1329
|
slide = control['slide']
|
1295
1330
|
time = control['time'].to_f
|
1296
1331
|
|
1297
|
-
|
1332
|
+
# record the UA of the client if we haven't seen it before
|
1333
|
+
@@counter['user_agents'][remote] ||= request.user_agent
|
1298
1334
|
|
1335
|
+
views = @@counter['pageviews']
|
1299
1336
|
# a bucket for this slide
|
1300
|
-
|
1337
|
+
views[slide] ||= Hash.new
|
1301
1338
|
# a bucket of slideviews for this address
|
1302
|
-
|
1339
|
+
views[slide][remote] ||= Array.new
|
1303
1340
|
# and add this slide viewing to the bucket
|
1304
|
-
|
1341
|
+
views[slide][remote] << { 'elapsed' => time, 'timestamp' => Time.now.to_i, 'presenter' => @@current[:name] }
|
1342
|
+
|
1343
|
+
@logger.debug "Logged #{time} on slide #{slide} for #{remote}"
|
1305
1344
|
|
1306
1345
|
when 'position'
|
1307
1346
|
ws.send( { 'current' => @@current[:number] }.to_json ) unless @@cookie.nil?
|
data/public/css/presenter.css
CHANGED
@@ -175,6 +175,12 @@ div.zoomed {
|
|
175
175
|
background-color: #fff;
|
176
176
|
font-family: helvetica;
|
177
177
|
}
|
178
|
+
|
179
|
+
.menu ul {
|
180
|
+
list-style: none;
|
181
|
+
padding: 0;
|
182
|
+
}
|
183
|
+
|
178
184
|
.menu ul li {
|
179
185
|
padding: 5px;
|
180
186
|
}
|
@@ -234,6 +240,11 @@ div.zoomed {
|
|
234
240
|
display: none;
|
235
241
|
}
|
236
242
|
|
243
|
+
#preview .content form label.correct {
|
244
|
+
font-weight: 900;
|
245
|
+
border-bottom: 4px solid black;
|
246
|
+
}
|
247
|
+
|
237
248
|
img#disconnected {
|
238
249
|
margin: 0.5em 1em;
|
239
250
|
}
|
@@ -349,6 +360,19 @@ div.zoomed {
|
|
349
360
|
list-style-type: disc;
|
350
361
|
}
|
351
362
|
|
363
|
+
#notes div.rendered.form span.count {
|
364
|
+
display: inline;
|
365
|
+
position: absolute;
|
366
|
+
top: 3px;
|
367
|
+
right: 4px;
|
368
|
+
z-index: 10;
|
369
|
+
font-size: 2.5em;
|
370
|
+
background-color: #ddd;
|
371
|
+
border: 1px solid #333;
|
372
|
+
border-radius: 0 0.15em 0 0;
|
373
|
+
box-shadow: 2px 2px 2px #888;
|
374
|
+
}
|
375
|
+
|
352
376
|
#bottom #notes div.personal {
|
353
377
|
float: right;
|
354
378
|
border-left: 4px solid #999;
|
data/public/css/showoff.css
CHANGED
@@ -641,6 +641,11 @@ div.rendered.form {
|
|
641
641
|
border-radius: 0.5em;
|
642
642
|
margin: 1em;
|
643
643
|
padding: 0.25em;
|
644
|
+
min-height: 3em;
|
645
|
+
}
|
646
|
+
div.rendered.form.element {
|
647
|
+
position: relative; /* for absolutely positioning children */
|
648
|
+
padding-left: 0.5em;
|
644
649
|
}
|
645
650
|
div.rendered.form label {
|
646
651
|
display: block;
|
@@ -653,11 +658,23 @@ div.rendered.form .item {
|
|
653
658
|
/* overflow: hidden; */
|
654
659
|
height: 1.25em;
|
655
660
|
white-space: nowrap;
|
661
|
+
opacity: 0.5;
|
662
|
+
border-radius: 0 0.5em 0.5em 0;
|
663
|
+
margin: 2px 0;
|
664
|
+
}
|
665
|
+
div.rendered.form .item.correct {
|
666
|
+
font-weight: 900;
|
667
|
+
border-left: 4px solid black;
|
668
|
+
opacity: 1.0;
|
656
669
|
}
|
670
|
+
div.rendered.form span.count {
|
671
|
+
display: none;
|
672
|
+
}
|
673
|
+
|
657
674
|
div.rendered.form .item.barstyle0 { background-color: #bb73bb; }
|
658
675
|
div.rendered.form .item.barstyle1 { background-color: #59b859; }
|
659
676
|
div.rendered.form .item.barstyle2 { background-color: #e3742f; }
|
660
|
-
div.rendered.form .item.barstyle3 { background-color: #
|
677
|
+
div.rendered.form .item.barstyle3 { background-color: #48a2ee; }
|
661
678
|
div.rendered.form .item.barstyle4 { background-color: #f75d5d; }
|
662
679
|
|
663
680
|
#notes .form.wrapper {
|
data/public/js/showoff.js
CHANGED
@@ -102,10 +102,14 @@ function setupPreso(load_slides, prefix) {
|
|
102
102
|
*/
|
103
103
|
}
|
104
104
|
|
105
|
-
function loadSlides(load_slides, prefix) {
|
105
|
+
function loadSlides(load_slides, prefix, reload) {
|
106
|
+
var url = loadSlidesPrefix + "slides";
|
107
|
+
if (reload) {
|
108
|
+
url += "?cache=clear";
|
109
|
+
}
|
106
110
|
//load slides offscreen, wait for images and then initialize
|
107
111
|
if (load_slides) {
|
108
|
-
$("#slides").load(
|
112
|
+
$("#slides").load(url, false, function(){
|
109
113
|
$("#slides img").batchImageLoad({
|
110
114
|
loadingCompleteCallback: initializePresentation(prefix)
|
111
115
|
})
|
@@ -118,7 +122,7 @@ function loadSlides(load_slides, prefix) {
|
|
118
122
|
}
|
119
123
|
|
120
124
|
function loadKeyDictionaries () {
|
121
|
-
$.getJSON('
|
125
|
+
$.getJSON('js/keyDictionary.json', function(data) {
|
122
126
|
keycode_dictionary = data['keycodeDictionary'];
|
123
127
|
keycode_shifted_keys = data['shiftedKeyDictionary'];
|
124
128
|
});
|
@@ -504,7 +508,11 @@ function renderForm(form) {
|
|
504
508
|
//console.log(data);
|
505
509
|
form.children('div.form.element').each(function() {
|
506
510
|
var key = $(this).attr('data-name');
|
507
|
-
|
511
|
+
|
512
|
+
// add a counter label if we haven't already
|
513
|
+
if( $(this).has('span.count').length == 0 ) {
|
514
|
+
$(this).prepend('<span class="count"></span>');
|
515
|
+
}
|
508
516
|
|
509
517
|
$(this).find('ul > li > *').each(function() {
|
510
518
|
$(this).parent().parent().before(this);
|
@@ -529,15 +537,16 @@ function renderForm(form) {
|
|
529
537
|
case 'radio':
|
530
538
|
case 'checkbox':
|
531
539
|
// Just render these directly and migrate the label to inside the span
|
532
|
-
var value
|
533
|
-
var label
|
534
|
-
var
|
540
|
+
var value = $(this).attr('value');
|
541
|
+
var label = $(this).next('label');
|
542
|
+
var classes = $(this).attr('class');
|
543
|
+
var text = label.text();
|
535
544
|
|
536
545
|
if(text.match(/^-+$/)) {
|
537
546
|
$(this).remove();
|
538
547
|
}
|
539
548
|
else{
|
540
|
-
$(this).replaceWith('<div class="item barstyle'+style+'" data-value="'+value+'">'+text+'</div>');
|
549
|
+
$(this).replaceWith('<div class="item barstyle'+style+' '+classes+'" data-value="'+value+'">'+text+'</div>');
|
541
550
|
}
|
542
551
|
label.remove();
|
543
552
|
break;
|
@@ -548,11 +557,12 @@ function renderForm(form) {
|
|
548
557
|
parent = $(this).parent();
|
549
558
|
|
550
559
|
$(this).children('option').each(function() {
|
551
|
-
var value
|
552
|
-
var text
|
560
|
+
var value = $(this).val();
|
561
|
+
var text = $(this).text();
|
562
|
+
var classes = $(this).attr('class');
|
553
563
|
|
554
564
|
if(! text.match(/^-+$/)) {
|
555
|
-
parent.append('<div class="item barstyle'+style+'" data-value="'+value+'">'+text+'</div>');
|
565
|
+
parent.append('<div class="item barstyle'+style+' '+classes+'" data-value="'+value+'">'+text+'</div>');
|
556
566
|
|
557
567
|
// loop style counter
|
558
568
|
style++; style %= max;
|
@@ -568,37 +578,46 @@ function renderForm(form) {
|
|
568
578
|
|
569
579
|
// only start counting and sizing bars if we actually have usable data
|
570
580
|
if(data) {
|
581
|
+
// number of unique responses
|
582
|
+
var total = 0;
|
571
583
|
// double loop so we can handle re-renderings of the form
|
572
584
|
$(this).find('.item').each(function() {
|
573
585
|
var name = $(this).attr('data-value');
|
574
586
|
|
575
587
|
if(key in data) {
|
576
|
-
var count = data[key][name];
|
577
|
-
|
588
|
+
var count = data[key]['responses'][name];
|
589
|
+
|
590
|
+
total = data[key]['count'];
|
578
591
|
}
|
579
592
|
});
|
580
593
|
|
594
|
+
// insert the total into the counter label
|
595
|
+
$(this).find('span.count').each(function() {
|
596
|
+
$(this).text(total);
|
597
|
+
});
|
581
598
|
|
599
|
+
var oldTotal = $(this).attr('data-total');
|
582
600
|
$(this).find('.item').each(function() {
|
583
601
|
var name = $(this).attr('data-value');
|
584
602
|
var oldCount = $(this).attr('data-count');
|
585
|
-
var oldSum = $(this).attr('data-sum');
|
586
603
|
|
587
604
|
if(key in data) {
|
588
|
-
var count = data[key][name] || 0;
|
605
|
+
var count = data[key]['responses'][name] || 0;
|
589
606
|
}
|
590
607
|
else {
|
591
608
|
var count = 0;
|
592
609
|
}
|
593
610
|
|
594
|
-
if(count != oldCount ||
|
595
|
-
var percent = (
|
611
|
+
if(count != oldCount || total != oldTotal) {
|
612
|
+
var percent = (total) ? ((count/total)*100)+'%' : '0%';
|
596
613
|
|
597
614
|
$(this).attr('data-count', count);
|
598
|
-
$(this).attr('data-sum', sum);
|
599
615
|
$(this).animate({width: percent});
|
600
616
|
}
|
601
617
|
});
|
618
|
+
|
619
|
+
// record the old total value so we only animate when it changes
|
620
|
+
$(this).attr('data-total', total);
|
602
621
|
}
|
603
622
|
|
604
623
|
$(this).addClass('rendered');
|
@@ -608,7 +627,8 @@ function renderForm(form) {
|
|
608
627
|
}
|
609
628
|
|
610
629
|
function connectControlChannel() {
|
611
|
-
|
630
|
+
protocol = (location.protocol === 'https:') ? 'wss://' : 'ws://';
|
631
|
+
ws = new WebSocket(protocol + location.host + '/control');
|
612
632
|
ws.onopen = function() { connected(); };
|
613
633
|
ws.onclose = function() { disconnected(); }
|
614
634
|
ws.onmessage = function(m) { parseMessage(m.data); };
|
@@ -911,7 +931,7 @@ function toggleDebug () {
|
|
911
931
|
|
912
932
|
function reloadSlides () {
|
913
933
|
if (confirm('Are you sure you want to reload the slides?')) {
|
914
|
-
loadSlides(loadSlidesBool, loadSlidesPrefix);
|
934
|
+
loadSlides(loadSlidesBool, loadSlidesPrefix, true);
|
915
935
|
showSlide();
|
916
936
|
}
|
917
937
|
}
|
data/views/header.erb
CHANGED
@@ -3,8 +3,6 @@
|
|
3
3
|
|
4
4
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0"/>
|
5
5
|
|
6
|
-
<link rel="stylesheet" href="<%= @asset_path %>/css/reset.css" type="text/css"/>
|
7
|
-
|
8
6
|
<% if @favicon %>
|
9
7
|
<link rel="icon" href="<%= @favicon %>"/>
|
10
8
|
<% end %>
|
data/views/header_mini.erb
CHANGED
@@ -7,7 +7,6 @@
|
|
7
7
|
<link rel="icon" href="<%= @favicon %>"/>
|
8
8
|
<% end %>
|
9
9
|
|
10
|
-
<link rel="stylesheet" href="<%= @asset_path %>/css/reset.css" type="text/css"/>
|
11
10
|
<link rel="stylesheet" href="<%= @asset_path %>/css/showoff.css" type="text/css"/>
|
12
11
|
|
13
12
|
<link type="text/css" href="<%= @asset_path %>/css/fg.menu.css" media="screen" rel="stylesheet" />
|
data/views/onepage.erb
CHANGED
@@ -11,11 +11,11 @@
|
|
11
11
|
|
12
12
|
<% if @inline %>
|
13
13
|
|
14
|
-
<%= inline_css(['
|
14
|
+
<%= inline_css(['showoff.css', 'theme/ui.all.css', 'onepage.css', "highlight/#{@highlightStyle}.css"], 'public/css') %>
|
15
15
|
<%= inline_css(css_files) %>
|
16
16
|
|
17
17
|
<!--[if lte IE 8]>
|
18
|
-
<%= inline_css('ie8.css') %>
|
18
|
+
<%= inline_css(['ie8.css'], 'public/css') %>
|
19
19
|
<![endif]-->
|
20
20
|
|
21
21
|
<%= inline_js(['jquery-2.1.4.min.js', 'jquery-print.js', 'showoff.js', 'onepage.js', 'highlight.pack.js'], 'public/js') %>
|
@@ -23,7 +23,7 @@
|
|
23
23
|
<%= inline_js(js_files) %>
|
24
24
|
|
25
25
|
<% else %>
|
26
|
-
<% ['
|
26
|
+
<% ['showoff.css', 'theme/ui.all.css', 'onepage.css', "highlight/#{@highlightStyle}.css"].each do |css_file| %>
|
27
27
|
<link rel="stylesheet" href="<%= @asset_path %>/css/<%= css_file %>" type="text/css"/>
|
28
28
|
<% end %>
|
29
29
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: showoff
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Scott Chacon
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-11-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sinatra
|
@@ -268,7 +268,6 @@ files:
|
|
268
268
|
- public/css/popdown.png
|
269
269
|
- public/css/popout.png
|
270
270
|
- public/css/presenter.css
|
271
|
-
- public/css/reset.css
|
272
271
|
- public/css/run_code-dim.png
|
273
272
|
- public/css/run_code.png
|
274
273
|
- public/css/showoff.css
|
data/public/css/reset.css
DELETED
@@ -1,53 +0,0 @@
|
|
1
|
-
/* http://meyerweb.com/eric/tools/css/reset/ */
|
2
|
-
/* v1.0 | 20080212 */
|
3
|
-
|
4
|
-
html, body, div, span, applet, object, iframe,
|
5
|
-
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
6
|
-
a, abbr, acronym, address, big, cite, code,
|
7
|
-
del, dfn, em, font, img, ins, kbd, q, s, samp,
|
8
|
-
small, strike, strong, sub, sup, tt, var,
|
9
|
-
b, u, i, center,
|
10
|
-
dl, dt, dd, ol, ul, li,
|
11
|
-
fieldset, form, label, legend,
|
12
|
-
table, caption, tbody, tfoot, thead, tr, th, td {
|
13
|
-
margin: 0;
|
14
|
-
padding: 0;
|
15
|
-
border: 0;
|
16
|
-
outline: 0;
|
17
|
-
font-size: 100%;
|
18
|
-
vertical-align: baseline;
|
19
|
-
background: transparent;
|
20
|
-
}
|
21
|
-
body {
|
22
|
-
line-height: 1;
|
23
|
-
}
|
24
|
-
ol, ul {
|
25
|
-
list-style: none;
|
26
|
-
}
|
27
|
-
blockquote, q {
|
28
|
-
quotes: none;
|
29
|
-
}
|
30
|
-
blockquote:before, blockquote:after,
|
31
|
-
q:before, q:after {
|
32
|
-
content: '';
|
33
|
-
content: none;
|
34
|
-
}
|
35
|
-
|
36
|
-
/* remember to define focus styles! */
|
37
|
-
:focus {
|
38
|
-
outline: 0;
|
39
|
-
}
|
40
|
-
|
41
|
-
/* remember to highlight inserts somehow! */
|
42
|
-
ins {
|
43
|
-
text-decoration: none;
|
44
|
-
}
|
45
|
-
del {
|
46
|
-
text-decoration: line-through;
|
47
|
-
}
|
48
|
-
|
49
|
-
/* tables still need 'cellspacing="0"' in the markup */
|
50
|
-
table {
|
51
|
-
border-collapse: collapse;
|
52
|
-
border-spacing: 0;
|
53
|
-
}
|