showoff 0.17.2 → 0.18.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/Rakefile +37 -0
- data/bin/showoff +3 -0
- data/lib/showoff.rb +164 -43
- data/lib/showoff/version.rb +1 -1
- data/lib/showoff_utils.rb +26 -14
- data/locales/README.md +25 -0
- data/locales/de.yml +160 -0
- data/locales/en.yml +160 -0
- data/locales/es.yml +160 -0
- data/locales/fr.yml +160 -0
- data/locales/ja.yml +167 -0
- data/locales/nl.yml +160 -0
- data/locales/pt.yml +160 -0
- data/public/css/presenter.css +14 -1
- data/public/css/showoff.css +54 -4
- data/public/js/presenter.js +122 -49
- data/public/js/showoff.js +157 -25
- data/public/js/simpleStrings-0.0.1.js +96 -0
- data/views/404.erb +1 -1
- data/views/download.erb +2 -2
- data/views/header.erb +7 -2
- data/views/help.erb +16 -16
- data/views/index.erb +40 -26
- data/views/presenter.erb +86 -68
- data/views/stats.erb +7 -7
- metadata +39 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fa2018ebcc69b506515c2a88f49b446347256416
|
4
|
+
data.tar.gz: 97d7de5896a47cdfc5432315513819fdc060a9c7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 55efb2446f06b35a6cf7d0dd28144ce9aaae1dcb95de8897bacf5fd82e896750525a555762305a7f4e47ee9ba7860607b6a1ed3619b21520a77cb4f636f14a50
|
7
|
+
data.tar.gz: 420f79aea8433eb3f147600bd774a2e4c9907c3c78c78a4bcd38cb077807d14e1de24fb8ca1b3e4018fbc7ef1243b640224b239989cd03d3011608ffb173c324
|
data/Rakefile
CHANGED
@@ -69,6 +69,43 @@ task :test do
|
|
69
69
|
sh "turn test/*_test.rb #{suffix}"
|
70
70
|
end
|
71
71
|
|
72
|
+
|
73
|
+
desc 'Validate translation files'
|
74
|
+
task 'lang:check' do
|
75
|
+
require 'yaml'
|
76
|
+
|
77
|
+
def compare_keys(left, right, name, stack=nil)
|
78
|
+
left.each do |key, val|
|
79
|
+
inner = stack.nil? ? key : "#{stack}.#{key}"
|
80
|
+
compare = right[key]
|
81
|
+
|
82
|
+
case compare
|
83
|
+
when Hash
|
84
|
+
compare_keys(val, compare, name, inner)
|
85
|
+
when String
|
86
|
+
next
|
87
|
+
when NilClass
|
88
|
+
puts "Error: '#{inner}' is missing from #{name}"
|
89
|
+
else
|
90
|
+
puts "Error: '#{inner}' in #{name} is a #{compare.class}, not a Hash"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
canonical = YAML.load_file('locales/en.yml')
|
97
|
+
languages = Dir.glob('locales/*.yml').reject {|lang| lang == 'locales/en.yml' }
|
98
|
+
|
99
|
+
languages.each do |langfile|
|
100
|
+
lang = YAML.load_file(langfile)
|
101
|
+
code = File.basename(langfile, '.yml')
|
102
|
+
key = lang.keys.first
|
103
|
+
|
104
|
+
puts "Error: #{langfile} has the wrong language code (#{key})" unless code == key
|
105
|
+
compare_keys(canonical['en'], lang[key], langfile)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
72
109
|
begin
|
73
110
|
require 'mg'
|
74
111
|
MG.new("showoff.gemspec")
|
data/bin/showoff
CHANGED
@@ -304,6 +304,9 @@ module Wrapper
|
|
304
304
|
c.default_value "showoff.json"
|
305
305
|
c.flag [:f, :file, :pres_file]
|
306
306
|
|
307
|
+
c.desc 'Language code to generate.'
|
308
|
+
c.flag [:l, :lang, :language, :locale]
|
309
|
+
|
307
310
|
c.action do |global_options,options,args|
|
308
311
|
ShowOff.do_static(args, options)
|
309
312
|
end
|
data/lib/showoff.rb
CHANGED
@@ -9,6 +9,12 @@ require 'htmlentities'
|
|
9
9
|
require 'sinatra-websocket'
|
10
10
|
require 'tempfile'
|
11
11
|
|
12
|
+
require 'i18n'
|
13
|
+
require 'i18n/backend/fallbacks'
|
14
|
+
require 'rack'
|
15
|
+
require 'rack/contrib'
|
16
|
+
require 'iso-639'
|
17
|
+
|
12
18
|
here = File.expand_path(File.dirname(__FILE__))
|
13
19
|
require "#{here}/showoff_utils"
|
14
20
|
require "#{here}/commandline_parser"
|
@@ -53,6 +59,16 @@ class ShowOff < Sinatra::Application
|
|
53
59
|
set :encoding, nil
|
54
60
|
set :url, nil
|
55
61
|
|
62
|
+
# automatically select the translation based on the user's configured browser language
|
63
|
+
use Rack::Locale
|
64
|
+
|
65
|
+
configure do
|
66
|
+
I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
|
67
|
+
I18n.load_path += Dir[File.join(settings.root, '..', 'locales', '*.yml')]
|
68
|
+
I18n.backend.load_translations
|
69
|
+
I18n.enforce_available_locales = false
|
70
|
+
end
|
71
|
+
|
56
72
|
def initialize(app=nil)
|
57
73
|
super(app)
|
58
74
|
@logger = Logger.new(STDOUT)
|
@@ -62,10 +78,6 @@ class ShowOff < Sinatra::Application
|
|
62
78
|
@review = settings.review
|
63
79
|
@execute = settings.execute
|
64
80
|
|
65
|
-
dir = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
66
|
-
@logger.debug(dir)
|
67
|
-
|
68
|
-
showoff_dir = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
69
81
|
settings.pres_dir ||= Dir.pwd
|
70
82
|
@root_path = "."
|
71
83
|
|
@@ -139,7 +151,7 @@ class ShowOff < Sinatra::Application
|
|
139
151
|
@slide_count = 0
|
140
152
|
@section_major = 0
|
141
153
|
@section_minor = 0
|
142
|
-
@section_title = settings.showoff_config['name'] rescue '
|
154
|
+
@section_title = settings.showoff_config['name'] rescue I18n.t('name')
|
143
155
|
@@slide_titles = [] # a list of generated slide names, used for cross references later.
|
144
156
|
|
145
157
|
@logger.debug settings.pres_template
|
@@ -183,8 +195,9 @@ class ShowOff < Sinatra::Application
|
|
183
195
|
|
184
196
|
@@downloads = Hash.new # Track downloadable files
|
185
197
|
@@cookie = nil # presenter cookie. Identifies the presenter for control messages
|
198
|
+
@@master = nil # this holds the @client_id of the master presenter, for the cases in which multiple presenters are loaded
|
186
199
|
@@current = Hash.new # The current slide that the presenter is viewing
|
187
|
-
@@cache =
|
200
|
+
@@cache = Hash.new # Cache slide content for subsequent hits
|
188
201
|
@@activity = [] # keep track of completion for activity slides
|
189
202
|
|
190
203
|
if @interactive
|
@@ -199,8 +212,11 @@ class ShowOff < Sinatra::Application
|
|
199
212
|
|
200
213
|
# Initialize Markdown Configuration
|
201
214
|
MarkdownConfig::setup(settings.pres_dir)
|
202
|
-
end
|
203
215
|
|
216
|
+
# Process renderer config options
|
217
|
+
@engine_options = ShowOffUtils.showoff_renderer_options(settings.pres_dir)
|
218
|
+
|
219
|
+
end
|
204
220
|
# save stats to disk
|
205
221
|
def self.flush
|
206
222
|
begin
|
@@ -260,6 +276,81 @@ class ShowOff < Sinatra::Application
|
|
260
276
|
end
|
261
277
|
end
|
262
278
|
|
279
|
+
# This is just a unified lookup method that takes a full locale name
|
280
|
+
# and then resolves it to an available version of the name
|
281
|
+
def with_locale(locale)
|
282
|
+
locale = locale.to_s
|
283
|
+
until (locale.empty?) do
|
284
|
+
result = yield(locale)
|
285
|
+
return result unless result.nil?
|
286
|
+
|
287
|
+
# if not found, chop off a section and try again
|
288
|
+
locale = locale.rpartition(/[-_]/).first
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
# turns a locale code into a string name
|
293
|
+
def get_language_name(locale)
|
294
|
+
with_locale(locale) do |str|
|
295
|
+
result = ISO_639.find(str)
|
296
|
+
result[3] unless result.nil?
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
# This function returns the directory containing translated *content*, defaulting
|
301
|
+
# to cwd. This works similarly to I18n fallback, but we cannot reuse that as it's
|
302
|
+
# a different translation mechanism.
|
303
|
+
def get_locale_dir(prefix, locale)
|
304
|
+
return '.' if locale == 'disable'
|
305
|
+
|
306
|
+
with_locale(locale) do |str|
|
307
|
+
path = "#{prefix}/#{str}"
|
308
|
+
return path if File.directory?(path)
|
309
|
+
end || '.'
|
310
|
+
end
|
311
|
+
|
312
|
+
# return a hash of all language codes available and the long name description of each
|
313
|
+
def language_names
|
314
|
+
Dir.glob('locales/*').inject({}) do |memo, entry|
|
315
|
+
next memo unless File.directory? entry
|
316
|
+
|
317
|
+
locale = File.basename(entry)
|
318
|
+
memo.update(locale => get_language_name(locale))
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
# returns the minimized canonical version of the current selected content locale
|
323
|
+
# it assumes that if the user has specified a locale, that it's already minimized
|
324
|
+
# note: if the locale doesn't exist on disk, it will just default to no translation
|
325
|
+
def locale(user_locale)
|
326
|
+
if [nil, '', 'auto'].include? user_locale
|
327
|
+
languages = I18n.available_locales
|
328
|
+
I18n.fallbacks[I18n.locale].select { |f| languages.include? f }.first
|
329
|
+
else
|
330
|
+
user_locale
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
# returns a hash of all translations for the current language. This is used
|
335
|
+
# for the javascript half of the translations
|
336
|
+
def get_translations
|
337
|
+
languages = I18n.backend.send(:translations)
|
338
|
+
fallback = I18n.fallbacks[I18n.locale].select { |f| languages.keys.include? f }.first
|
339
|
+
languages[fallback]
|
340
|
+
end
|
341
|
+
|
342
|
+
# Finds the language key from strings.json and returns the strings hash. This is
|
343
|
+
# used for user translations in the presentation, e.g. SVG translations.
|
344
|
+
def user_translations
|
345
|
+
return {} unless File.file? 'locales/strings.json'
|
346
|
+
strings = JSON.parse(File.read('locales/strings.json')) rescue {}
|
347
|
+
|
348
|
+
with_locale(@locale) do |key|
|
349
|
+
return strings[key] if strings.include? key
|
350
|
+
end
|
351
|
+
{}
|
352
|
+
end
|
353
|
+
|
263
354
|
# todo: move more behavior into this class
|
264
355
|
class Slide
|
265
356
|
attr_reader :classes, :text, :tpl, :bg
|
@@ -291,9 +382,8 @@ class ShowOff < Sinatra::Application
|
|
291
382
|
if settings.encoding and content.respond_to?(:force_encoding)
|
292
383
|
content.force_encoding(settings.encoding)
|
293
384
|
end
|
294
|
-
engine_options = ShowOffUtils.showoff_renderer_options(settings.pres_dir)
|
295
385
|
@logger.debug "renderer: #{Tilt[:markdown].name}"
|
296
|
-
@logger.debug "render options: #{engine_options.inspect}"
|
386
|
+
@logger.debug "render options: #{@engine_options.inspect}"
|
297
387
|
|
298
388
|
# if there are no !SLIDE markers, then make every H1 define a new slide
|
299
389
|
unless content =~ /^\<?!SLIDE/m
|
@@ -407,7 +497,7 @@ class ShowOff < Sinatra::Application
|
|
407
497
|
|
408
498
|
# Apply the template to the slide and replace the key to generate the content of the slide
|
409
499
|
sl = process_content_for_replacements(template.gsub(/~~~CONTENT~~~/, slide.text))
|
410
|
-
sl = Tilt[:markdown].new(nil, nil, engine_options) { sl }.render
|
500
|
+
sl = Tilt[:markdown].new(nil, nil, @engine_options) { sl }.render
|
411
501
|
sl = build_forms(sl, content_classes)
|
412
502
|
sl = update_p_classes(sl)
|
413
503
|
sl = process_content_for_section_tags(sl, name, opts)
|
@@ -418,7 +508,7 @@ class ShowOff < Sinatra::Application
|
|
418
508
|
content += "</div>\n"
|
419
509
|
if content_classes.include? 'activity'
|
420
510
|
content += '<span class="activityToggle">'
|
421
|
-
content += " <label for=\"activity-#{ref}\"
|
511
|
+
content += " <label for=\"activity-#{ref}\">#{I18n.t('activity_complete')}</label>"
|
422
512
|
content += " <input type=\"checkbox\" class=\"activity\" name=\"activity-#{ref}\" id=\"activity-#{ref}\">"
|
423
513
|
content += '</span>'
|
424
514
|
end
|
@@ -497,14 +587,11 @@ class ShowOff < Sinatra::Application
|
|
497
587
|
filename = File.join(settings.pres_dir, '_notes', "#{name}.md")
|
498
588
|
@logger.debug "personal notes filename: #{filename}"
|
499
589
|
if [nil, 'notes'].include? opts[:section] and File.file? filename
|
500
|
-
# TODO: shouldn't have to reparse config all the time
|
501
|
-
engine_options = ShowOffUtils.showoff_renderer_options(settings.pres_dir)
|
502
|
-
|
503
590
|
# Make sure we've got a notes div to hang personal notes from
|
504
591
|
doc.add_child '<div class="notes-section notes"></div>' if doc.css('div.notes-section.notes').empty?
|
505
592
|
doc.css('div.notes-section.notes').each do |section|
|
506
|
-
text = Tilt[:markdown].new(nil, nil, engine_options) { File.read(filename) }.render
|
507
|
-
frag = "<div class=\"personal\"><h1
|
593
|
+
text = Tilt[:markdown].new(nil, nil, @engine_options) { File.read(filename) }.render
|
594
|
+
frag = "<div class=\"personal\"><h1>#{I18n.t('presenter.notes.personal')}</h1>#{text}</div>"
|
508
595
|
note = Nokogiri::HTML::DocumentFragment.parse(frag)
|
509
596
|
|
510
597
|
if section.children.size > 0
|
@@ -715,8 +802,8 @@ class ShowOff < Sinatra::Application
|
|
715
802
|
|
716
803
|
begin
|
717
804
|
tools = '<div class="tools">'
|
718
|
-
tools <<
|
719
|
-
tools <<
|
805
|
+
tools << "<input type=\"button\" class=\"display\" value=\"#{I18n.t('forms.display')}\">"
|
806
|
+
tools << "<input type=\"submit\" value=\"#{I18n.t('forms.save')}\" disabled=\"disabled\">"
|
720
807
|
tools << '</div>'
|
721
808
|
form = "<form id='#{title}' action='/form/#{title}' method='POST'>#{content}#{tools}</form>"
|
722
809
|
doc = Nokogiri::HTML::DocumentFragment.parse(form)
|
@@ -742,7 +829,7 @@ class ShowOff < Sinatra::Application
|
|
742
829
|
def form_element(id, code, name, required, rhs, text)
|
743
830
|
required = required ? 'required' : ''
|
744
831
|
str = "<div class='form element #{required}' id='#{id}' data-name='#{code}'>"
|
745
|
-
str << "<label for='#{id}'>#{name}</label>"
|
832
|
+
str << "<label class='question' for='#{id}'>#{name}</label>"
|
746
833
|
case rhs
|
747
834
|
when /^\[\s+(\d*)\]$$/ # value = [ 5] (textarea)
|
748
835
|
str << form_element_textarea(id, code, $1)
|
@@ -873,10 +960,10 @@ class ShowOff < Sinatra::Application
|
|
873
960
|
|
874
961
|
def form_classes(modifier)
|
875
962
|
modifier.downcase!
|
876
|
-
classes = []
|
963
|
+
classes = ['response']
|
877
964
|
classes << 'correct' if modifier.include?('=')
|
878
965
|
|
879
|
-
classes.join
|
966
|
+
classes.join(' ')
|
880
967
|
end
|
881
968
|
|
882
969
|
def form_checked?(modifier)
|
@@ -1016,8 +1103,11 @@ class ShowOff < Sinatra::Application
|
|
1016
1103
|
end
|
1017
1104
|
|
1018
1105
|
def get_slides_html(opts={:static=>false, :pdf=>false, :toc=>false, :supplemental=>nil, :section=>nil})
|
1106
|
+
sections = nil
|
1107
|
+
Dir.chdir(get_locale_dir('locales', @locale)) do
|
1108
|
+
sections = ShowOffUtils.showoff_sections(settings.pres_dir, settings.showoff_config, @logger)
|
1109
|
+
end
|
1019
1110
|
|
1020
|
-
sections = ShowOffUtils.showoff_sections(settings.pres_dir, @logger)
|
1021
1111
|
if sections
|
1022
1112
|
data = ''
|
1023
1113
|
sections.each do |section, slides|
|
@@ -1102,17 +1192,11 @@ class ShowOff < Sinatra::Application
|
|
1102
1192
|
# Provide a button in the sidebar for interactive editing if configured
|
1103
1193
|
@edit = settings.showoff_config['edit'] if @review
|
1104
1194
|
|
1195
|
+
# translated UI strings, according to the current locale
|
1196
|
+
@language = get_translations()
|
1197
|
+
|
1105
1198
|
# store a cookie to tell clients apart. More reliable than using IP due to proxies, etc.
|
1106
|
-
|
1107
|
-
@client_id = guid()
|
1108
|
-
else
|
1109
|
-
if request.cookies['client_id']
|
1110
|
-
@client_id = request.cookies['client_id']
|
1111
|
-
else
|
1112
|
-
@client_id = guid()
|
1113
|
-
response.set_cookie('client_id', @client_id)
|
1114
|
-
end
|
1115
|
-
end
|
1199
|
+
manage_client_cookies()
|
1116
1200
|
|
1117
1201
|
erb :index
|
1118
1202
|
end
|
@@ -1122,8 +1206,10 @@ class ShowOff < Sinatra::Application
|
|
1122
1206
|
@issues = settings.showoff_config['issues']
|
1123
1207
|
@edit = settings.showoff_config['edit'] if @review
|
1124
1208
|
@feedback = settings.showoff_config['feedback']
|
1125
|
-
|
1126
|
-
|
1209
|
+
@language = get_translations()
|
1210
|
+
|
1211
|
+
manage_client_cookies(true)
|
1212
|
+
|
1127
1213
|
erb :presenter
|
1128
1214
|
end
|
1129
1215
|
|
@@ -1165,21 +1251,26 @@ class ShowOff < Sinatra::Application
|
|
1165
1251
|
end
|
1166
1252
|
|
1167
1253
|
def slides(static=false)
|
1254
|
+
@logger.warn "Cached presentations: #{@@cache.keys}"
|
1255
|
+
|
1256
|
+
# if we have a cache and we're not asking to invalidate it
|
1257
|
+
return @@cache[@locale] if (@@cache[@locale] and params['cache'] != 'clear')
|
1258
|
+
|
1259
|
+
@logger.warn "Generating locale: #{@locale}"
|
1260
|
+
|
1168
1261
|
# If we're displaying from a repository, let's update it
|
1169
1262
|
ShowOffUtils.update(settings.verbose) if settings.url
|
1170
1263
|
|
1171
|
-
# if we have a cache and we're not asking to invalidate it
|
1172
|
-
return @@cache if (@@cache and params['cache'] != 'clear')
|
1173
1264
|
@@slide_titles = []
|
1174
1265
|
content = get_slides_html(:static=>static)
|
1175
1266
|
|
1176
1267
|
# allow command line cache disabling
|
1177
|
-
@@cache = content unless settings.nocache
|
1268
|
+
@@cache[@locale] = content unless settings.nocache
|
1178
1269
|
content
|
1179
1270
|
end
|
1180
1271
|
|
1181
|
-
def print(
|
1182
|
-
@slides = get_slides_html(:static=>
|
1272
|
+
def print(section=nil)
|
1273
|
+
@slides = get_slides_html(:static=>true, :toc=>true, :print=>true, :section=>section)
|
1183
1274
|
@favicon = settings.showoff_config['favicon']
|
1184
1275
|
erb :onepage
|
1185
1276
|
end
|
@@ -1334,6 +1425,8 @@ class ShowOff < Sinatra::Application
|
|
1334
1425
|
path = showoff.instance_variable_get(:@root_path)
|
1335
1426
|
logger = showoff.instance_variable_get(:@logger)
|
1336
1427
|
|
1428
|
+
I18n.locale = opts[:language]
|
1429
|
+
|
1337
1430
|
case what
|
1338
1431
|
when 'supplemental'
|
1339
1432
|
data = showoff.send(what, opt, true)
|
@@ -1342,7 +1435,7 @@ class ShowOff < Sinatra::Application
|
|
1342
1435
|
data = showoff.send(what, opt)
|
1343
1436
|
when 'print'
|
1344
1437
|
opt ||= 'handouts'
|
1345
|
-
data = showoff.send(what,
|
1438
|
+
data = showoff.send(what, opt)
|
1346
1439
|
else
|
1347
1440
|
data = showoff.send(what, true)
|
1348
1441
|
end
|
@@ -1512,6 +1605,33 @@ class ShowOff < Sinatra::Application
|
|
1512
1605
|
(request.cookies['presenter'] == @@cookie)
|
1513
1606
|
end
|
1514
1607
|
|
1608
|
+
def master_presenter?
|
1609
|
+
@@master == @client_id
|
1610
|
+
end
|
1611
|
+
|
1612
|
+
def manage_client_cookies(presenter=false)
|
1613
|
+
# store a cookie to tell clients apart. More reliable than using IP due to proxies, etc.
|
1614
|
+
if request.nil? # when running showoff static
|
1615
|
+
@client_id = guid()
|
1616
|
+
else
|
1617
|
+
if request.cookies['client_id']
|
1618
|
+
@client_id = request.cookies['client_id']
|
1619
|
+
else
|
1620
|
+
@client_id = guid()
|
1621
|
+
response.set_cookie('client_id', @client_id)
|
1622
|
+
end
|
1623
|
+
|
1624
|
+
# if we have no content translations then remove the cookie
|
1625
|
+
response.delete_cookie('locale') if language_names.empty?
|
1626
|
+
end
|
1627
|
+
|
1628
|
+
if presenter
|
1629
|
+
@@master ||= @client_id
|
1630
|
+
@@cookie ||= guid()
|
1631
|
+
response.set_cookie('presenter', @@cookie)
|
1632
|
+
end
|
1633
|
+
end
|
1634
|
+
|
1515
1635
|
post '/form/:id' do |id|
|
1516
1636
|
client_id = request.cookies['client_id']
|
1517
1637
|
@logger.warn("Saving form answers from ip:#{request.ip} with ID of #{client_id} for id:##{id}")
|
@@ -1723,7 +1843,7 @@ class ShowOff < Sinatra::Application
|
|
1723
1843
|
control['id'] = guid()
|
1724
1844
|
EM.next_tick { settings.presenters.each{|s| s.send(control.to_json) } }
|
1725
1845
|
|
1726
|
-
when 'complete'
|
1846
|
+
when 'complete', 'answerkey'
|
1727
1847
|
EM.next_tick { settings.sockets.each{|s| s.send(control.to_json) } }
|
1728
1848
|
|
1729
1849
|
when 'annotation', 'annotationConfig'
|
@@ -1771,7 +1891,8 @@ class ShowOff < Sinatra::Application
|
|
1771
1891
|
|
1772
1892
|
# gawd, this whole routing scheme is bollocks
|
1773
1893
|
get %r{/([^/]*)/?([^/]*)} do
|
1774
|
-
@
|
1894
|
+
@locale = locale(request.cookies['locale'])
|
1895
|
+
@title = ShowOffUtils.showoff_title(settings.pres_dir)
|
1775
1896
|
@pause_msg = ShowOffUtils.pause_msg
|
1776
1897
|
what = params[:captures].first
|
1777
1898
|
opt = params[:captures][1]
|
@@ -1787,7 +1908,7 @@ class ShowOff < Sinatra::Application
|
|
1787
1908
|
|
1788
1909
|
begin
|
1789
1910
|
if (what != "favicon.ico")
|
1790
|
-
if
|
1911
|
+
if ['supplemental', 'print'].include? what
|
1791
1912
|
data = send(what, opt)
|
1792
1913
|
else
|
1793
1914
|
data = send(what)
|