showoff 0.20.1 → 0.20.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 +5 -5
- data/Rakefile +24 -12
- data/bin/showoff +47 -24
- data/lib/showoff.rb +43 -20
- data/lib/showoff/compiler.rb +106 -0
- data/lib/showoff/compiler/downloads.rb +91 -0
- data/lib/showoff/compiler/fixups.rb +142 -0
- data/lib/showoff/compiler/form.rb +236 -0
- data/lib/showoff/compiler/glossary.rb +164 -0
- data/lib/showoff/compiler/i18n.rb +24 -0
- data/lib/showoff/compiler/notes.rb +73 -0
- data/lib/showoff/compiler/table_of_contents.rb +51 -0
- data/lib/showoff/compiler/variables.rb +71 -0
- data/lib/showoff/config.rb +218 -0
- data/lib/showoff/locale.rb +132 -0
- data/lib/showoff/logger.rb +15 -0
- data/lib/showoff/monkeypatches.rb +28 -0
- data/lib/showoff/presentation.rb +181 -0
- data/lib/showoff/presentation/section.rb +70 -0
- data/lib/showoff/presentation/slide.rb +113 -0
- data/lib/showoff/state.rb +89 -0
- data/lib/showoff/version.rb +2 -2
- data/lib/showoff_ng.rb +99 -0
- data/lib/showoff_utils.rb +21 -19
- data/public/css/showoff.css +14 -1
- data/public/js/highlight.pack-9.15.10.js +22614 -0
- data/public/js/showoff.js +3 -3
- data/views/header.erb +3 -3
- data/views/header_mini.erb +2 -2
- data/views/onepage.erb +4 -10
- data/views/presenter.erb +5 -5
- data/views/slide.erb +29 -0
- metadata +24 -21
- data/locales/id.yml +0 -2
- data/public/js/highlight.pack-9.2.0.js +0 -15448
@@ -0,0 +1,132 @@
|
|
1
|
+
require 'i18n'
|
2
|
+
require 'i18n/backend/fallbacks'
|
3
|
+
require 'iso-639'
|
4
|
+
|
5
|
+
I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
|
6
|
+
I18n.load_path += Dir[File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'locales', '*.yml'))]
|
7
|
+
I18n.backend.load_translations
|
8
|
+
I18n.enforce_available_locales = false
|
9
|
+
|
10
|
+
class Showoff::Locale
|
11
|
+
@@contentLocale = nil
|
12
|
+
|
13
|
+
# Set the minimized canonical version of the specified content locale, selecting
|
14
|
+
# the nearest match to whatever exists in the presentation's locales directory.
|
15
|
+
# If the locale doesn't exist on disk, it will just default to no translation
|
16
|
+
#
|
17
|
+
# @todo: I don't think this is right at all -- it doesn't autoselect content
|
18
|
+
# languages, just built in Showoff languages. It only worked by accident before
|
19
|
+
#
|
20
|
+
# @param user_locale [String, Symbol] The locale to select.
|
21
|
+
#
|
22
|
+
# @returns [Symbol] The selected and saved locale.
|
23
|
+
def self.setContentLocale(user_locale = nil)
|
24
|
+
if [nil, '', 'auto'].include? user_locale
|
25
|
+
languages = I18n.available_locales
|
26
|
+
@@contentLocale = I18n.fallbacks[I18n.locale].select { |f| languages.include? f }.first
|
27
|
+
else
|
28
|
+
locales = Dir.glob("#{Showoff::Config.root}/locales/*").map {|e| File.basename e }
|
29
|
+
locales.delete 'strings.json'
|
30
|
+
|
31
|
+
@@contentLocale = with_locale(user_locale) do |str|
|
32
|
+
str.to_sym if locales.include? str
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.contentLocale
|
38
|
+
@@contentLocale
|
39
|
+
end
|
40
|
+
|
41
|
+
# Find the closest match to current locale in an array of possibilities
|
42
|
+
#
|
43
|
+
# @param items [Array] An array of possibilities to check
|
44
|
+
# @return [Symbol] The closest match to the current locale.
|
45
|
+
def self.resolve(items)
|
46
|
+
with_locale(contentLocale) do |str|
|
47
|
+
str.to_sym if items.include? str
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Turns a locale code into a string name
|
52
|
+
#
|
53
|
+
# @param locale [String, Symbol] The code of the locale to translate
|
54
|
+
# @returns [String] The name of the locale.
|
55
|
+
def self.languageName(locale = contentLocale)
|
56
|
+
with_locale(locale) do |str|
|
57
|
+
result = ISO_639.find(str)
|
58
|
+
result[3] unless result.nil?
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# This function returns the directory containing translated *content*, defaulting
|
63
|
+
# to the presentation root. This works similarly to I18n fallback, but we cannot
|
64
|
+
# reuse that as it's a different translation mechanism.
|
65
|
+
|
66
|
+
# @returns [String] Path to the translated content.
|
67
|
+
def self.contentPath
|
68
|
+
root = Showoff::Config.root
|
69
|
+
|
70
|
+
with_locale(contentLocale) do |str|
|
71
|
+
path = "#{root}/locales/#{str}"
|
72
|
+
return path if File.directory?(path)
|
73
|
+
end || root
|
74
|
+
end
|
75
|
+
|
76
|
+
# Generates a hash of all language codes available and the long name description of each
|
77
|
+
#
|
78
|
+
# @returns [Hash] The language code/name hash.
|
79
|
+
def self.contentLanguages
|
80
|
+
root = Showoff::Config.root
|
81
|
+
|
82
|
+
strings = JSON.parse(File.read("#{root}/locales/strings.json")) rescue {}
|
83
|
+
locales = Dir.glob("#{root}/locales/*")
|
84
|
+
.select {|f| File.directory?(f) }
|
85
|
+
.map {|f| File.basename(f) }
|
86
|
+
|
87
|
+
(strings.keys + locales).inject({}) do |memo, locale|
|
88
|
+
memo.update(locale => languageName(locale))
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
# Generates a hash of all translations for the current language. This is used
|
94
|
+
# for the javascript half of the UI translations
|
95
|
+
#
|
96
|
+
# @returns [Hash] The locale code/strings hash.
|
97
|
+
def self.translations
|
98
|
+
languages = I18n.backend.send(:translations)
|
99
|
+
fallback = I18n.fallbacks[I18n.locale].select { |f| languages.keys.include? f }.first
|
100
|
+
languages[fallback]
|
101
|
+
end
|
102
|
+
|
103
|
+
# Finds the language key from strings.json and returns the strings hash. This is
|
104
|
+
# used for user translations in the presentation content, e.g. SVG translations.
|
105
|
+
#
|
106
|
+
# @returns [Hash] The user translation code/strings hash.
|
107
|
+
def self.userTranslations
|
108
|
+
path = "#{Showoff::Config.root}/locales/strings.json"
|
109
|
+
return {} unless File.file? path
|
110
|
+
strings = JSON.parse(File.read(path)) rescue {}
|
111
|
+
|
112
|
+
with_locale(contentLocale) do |key|
|
113
|
+
return strings[key] if strings.include? key
|
114
|
+
end
|
115
|
+
{}
|
116
|
+
end
|
117
|
+
|
118
|
+
# This is just a unified lookup method that takes a full locale name
|
119
|
+
# and then resolves it to an available version of the name
|
120
|
+
def self.with_locale(locale)
|
121
|
+
locale = locale.to_s
|
122
|
+
until (locale.empty?) do
|
123
|
+
result = yield(locale)
|
124
|
+
return result unless result.nil?
|
125
|
+
|
126
|
+
# if not found, chop off a section and try again
|
127
|
+
locale = locale.rpartition(/[-_]/).first
|
128
|
+
end
|
129
|
+
end
|
130
|
+
private_class_method :with_locale
|
131
|
+
|
132
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'logger'
|
2
|
+
class Showoff::Logger
|
3
|
+
@@logger = Logger.new(STDERR)
|
4
|
+
@@logger.progname = 'Showoff'
|
5
|
+
@@logger.formatter = proc { |severity,datetime,progname,msg| "(#{progname}) #{severity}: #{msg}\n" }
|
6
|
+
@@logger.level = Showoff::State.get(:verbose) ? Logger::DEBUG : Logger::WARN
|
7
|
+
@@logger.level = Logger::WARN
|
8
|
+
|
9
|
+
[:debug, :info, :warn, :error, :fatal].each do |meth|
|
10
|
+
define_singleton_method(meth) do |msg|
|
11
|
+
@@logger.send(meth, msg)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# Yay for Ruby 2.0!
|
2
|
+
class Hash
|
3
|
+
unless Hash.method_defined? :dig
|
4
|
+
def dig(*args)
|
5
|
+
args.reduce(self) do |iter, arg|
|
6
|
+
break nil unless iter.is_a? Enumerable
|
7
|
+
break nil unless iter.include? arg
|
8
|
+
iter[arg]
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
class Nokogiri::XML::Element
|
16
|
+
unless Nokogiri::XML::Element.method_defined? :add_class
|
17
|
+
def add_class(classlist)
|
18
|
+
self[:class] = [self[:class], classlist].join(' ')
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
unless Nokogiri::XML::Element.method_defined? :classes
|
23
|
+
def classes
|
24
|
+
self[:class] ? self[:class].split(' ') : []
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,181 @@
|
|
1
|
+
class Showoff::Presentation
|
2
|
+
require 'showoff/presentation/section'
|
3
|
+
require 'showoff/presentation/slide'
|
4
|
+
require 'showoff/compiler'
|
5
|
+
require 'keymap'
|
6
|
+
|
7
|
+
attr_reader :sections
|
8
|
+
|
9
|
+
def initialize(options)
|
10
|
+
@options = options
|
11
|
+
@sections = Showoff::Config.sections.map do |name, files|
|
12
|
+
Showoff::Presentation::Section.new(name, files)
|
13
|
+
end
|
14
|
+
|
15
|
+
# weird magic variables the presentation expects
|
16
|
+
@baseurl = nil # this doesn't appear to have ever been used
|
17
|
+
@title = Showoff::Config.get('name') || I18n.t('name')
|
18
|
+
@favicon = Showoff::Config.get('favicon') || 'favicon.ico'
|
19
|
+
@feedback = Showoff::Config.get('feedback') # note: the params check is obsolete
|
20
|
+
@pause_msg = Showoff::Config.get('pause_msg')
|
21
|
+
@language = Showoff::Locale.translations
|
22
|
+
@edit = Showoff::Config.get('edit') if options[:review]
|
23
|
+
|
24
|
+
# invert the logic to maintain backwards compatibility of interactivity on by default
|
25
|
+
@interactive = ! options[:standalone]
|
26
|
+
|
27
|
+
# Load up the default keymap, then merge in any customizations
|
28
|
+
keymapfile = File.expand_path(File.join('~', '.showoff', 'keymap.json'))
|
29
|
+
@keymap = Keymap.default
|
30
|
+
@keymap.merge! JSON.parse(File.read(keymapfile)) rescue {}
|
31
|
+
|
32
|
+
# map keys to the labels we're using
|
33
|
+
@keycode_dictionary = Keymap.keycodeDictionary
|
34
|
+
@keycode_shifted_keys = Keymap.shiftedKeyDictionary
|
35
|
+
|
36
|
+
@highlightStyle = Showoff::Config.get('highlight') || 'default'
|
37
|
+
|
38
|
+
if Showoff::State.get(:supplemental)
|
39
|
+
@wrapper_classes = ['supplemental']
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def compile
|
44
|
+
Showoff::State.reset([:slide_count, :section_major, :section_minor])
|
45
|
+
|
46
|
+
# @todo For now, we reparse the html so that we can generate content via slide
|
47
|
+
# templates. This adds a bit of extra time, but not too much. Perhaps
|
48
|
+
# we'll change that at some point.
|
49
|
+
html = @sections.map(&:render).join("\n")
|
50
|
+
doc = Nokogiri::HTML::DocumentFragment.parse(html)
|
51
|
+
|
52
|
+
Showoff::Compiler::TableOfContents.generate!(doc)
|
53
|
+
Showoff::Compiler::Glossary.generatePage!(doc)
|
54
|
+
|
55
|
+
doc
|
56
|
+
end
|
57
|
+
|
58
|
+
# The index page does not contain content; just a placeholder div that's
|
59
|
+
# dynamically loaded after the page is displayed. This increases perceived
|
60
|
+
# responsiveness.
|
61
|
+
def index
|
62
|
+
ERB.new(File.read(File.join(Showoff::GEMROOT, 'views','index.erb')), nil, '-').result(binding)
|
63
|
+
end
|
64
|
+
|
65
|
+
def slides
|
66
|
+
compile.to_html
|
67
|
+
end
|
68
|
+
|
69
|
+
def static
|
70
|
+
# This singleton guard removes ordering coupling between assets() & static()
|
71
|
+
@doc ||= compile
|
72
|
+
@slides = @doc.to_html
|
73
|
+
|
74
|
+
# All static snapshots should be non-interactive by definition
|
75
|
+
@interactive = false
|
76
|
+
|
77
|
+
case Showoff::State.get(:format)
|
78
|
+
when 'web'
|
79
|
+
template = 'index.erb'
|
80
|
+
when 'print', 'supplemental', 'pdf'
|
81
|
+
template = 'onepage.erb'
|
82
|
+
end
|
83
|
+
|
84
|
+
ERB.new(File.read(File.join(Showoff::GEMROOT, 'views', template)), nil, '-').result(binding)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Generates a list of all image/font/etc files used by the presentation. This
|
88
|
+
# will only identify the sources of <img> tags and files referenced by the
|
89
|
+
# CSS url() function.
|
90
|
+
#
|
91
|
+
# @see
|
92
|
+
# https://github.com/puppetlabs/showoff/blob/220d6eef4c5942eda625dd6edc5370c7490eced7/lib/showoff.rb#L1509-L1573
|
93
|
+
# @returns [Array]
|
94
|
+
# List of assets, such as images or fonts, used by the presentation.
|
95
|
+
def assets
|
96
|
+
# This singleton guard removes ordering coupling between assets() & static()
|
97
|
+
@doc ||= compile
|
98
|
+
|
99
|
+
# matches url(<path>) and returns the path as a capture group
|
100
|
+
urlsrc = /url\([\"\']?(.*?)(?:[#\?].*)?[\"\']?\)/
|
101
|
+
|
102
|
+
# get all image and url() sources
|
103
|
+
files = @doc.search('img').map {|img| img[:src] }
|
104
|
+
@doc.search('*').each do |node|
|
105
|
+
next unless node[:style]
|
106
|
+
next unless matches = node[:style].match(urlsrc)
|
107
|
+
files << matches[1]
|
108
|
+
end
|
109
|
+
|
110
|
+
# add in images from css files too
|
111
|
+
css_files.each do |css_path|
|
112
|
+
data = File.read(File.join(Showoff::Config.root, css_path))
|
113
|
+
|
114
|
+
# @todo: This isn't perfect. It will match commented out styles. But its
|
115
|
+
# worst case behavior is displaying a warning message, so that's ok for now.
|
116
|
+
data.scan(urlsrc).flatten.each do |path|
|
117
|
+
# resolve relative paths in the stylesheet
|
118
|
+
path = File.join(File.dirname(css_path), path) unless path.start_with? '/'
|
119
|
+
files << path
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# also all user-defined styles and javascript files
|
124
|
+
files.concat css_files
|
125
|
+
files.concat js_files
|
126
|
+
files.uniq
|
127
|
+
end
|
128
|
+
|
129
|
+
def erb(template)
|
130
|
+
ERB.new(File.read(File.join(Showoff::GEMROOT, 'views', "#{template}.erb")), nil, '-').result(binding)
|
131
|
+
end
|
132
|
+
|
133
|
+
def css_files
|
134
|
+
base = Dir.glob("#{Showoff::Config.root}/*.css").map { |path| File.basename(path) }
|
135
|
+
extra = Array(Showoff::Config.get('styles'))
|
136
|
+
base + extra
|
137
|
+
end
|
138
|
+
|
139
|
+
def js_files
|
140
|
+
base = Dir.glob("#{Showoff::Config.root}/*.js").map { |path| File.basename(path) }
|
141
|
+
extra = Array(Showoff::Config.get('scripts'))
|
142
|
+
base + extra
|
143
|
+
end
|
144
|
+
|
145
|
+
# return a list of keys associated with a given action in the keymap
|
146
|
+
def mapped_keys(action, klass='key')
|
147
|
+
list = @keymap.select { |key,value| value == action }.keys
|
148
|
+
|
149
|
+
if klass
|
150
|
+
list.map { |val| "<span class=\"#{klass}\">#{val}</span>" }.join
|
151
|
+
else
|
152
|
+
list.join ', '
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
|
157
|
+
|
158
|
+
|
159
|
+
# @todo: backwards compatibility shim
|
160
|
+
def user_translations
|
161
|
+
Showoff::Locale.userTranslations
|
162
|
+
end
|
163
|
+
|
164
|
+
# @todo: backwards compatibility shim
|
165
|
+
def language_names
|
166
|
+
Showoff::Locale.contentLanguages
|
167
|
+
end
|
168
|
+
|
169
|
+
|
170
|
+
# @todo: this should be part of the server. Move there with the least disruption.
|
171
|
+
def master_presenter?
|
172
|
+
false
|
173
|
+
end
|
174
|
+
|
175
|
+
# @todo: this should be part of the server. Move there with the least disruption.
|
176
|
+
def valid_presenter_cookie?
|
177
|
+
false
|
178
|
+
end
|
179
|
+
|
180
|
+
|
181
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
class Showoff::Presentation::Section
|
2
|
+
attr_reader :slides, :name
|
3
|
+
|
4
|
+
def initialize(name, files)
|
5
|
+
@name = name
|
6
|
+
@slides = []
|
7
|
+
files.each { |filename| loadSlides(filename) }
|
8
|
+
|
9
|
+
# merged output means that we just want to generate *everything*. This is used by internal,
|
10
|
+
# methods such as content validation, where we want all content represented.
|
11
|
+
# https://github.com/puppetlabs/showoff/blob/220d6eef4c5942eda625dd6edc5370c7490eced7/lib/showoff.rb#L429-L453
|
12
|
+
unless Showoff::State.get(:merged)
|
13
|
+
if Showoff::State.get(:supplemental)
|
14
|
+
# if we're looking for supplemental material, only include the content we want
|
15
|
+
@slides.select! {|slide| slide.classes.include? 'supplemental' }
|
16
|
+
@slides.select! {|slide| slide.classes.include? Showoff::State.get(:supplemental) }
|
17
|
+
else
|
18
|
+
# otherwise just skip all supplemental material completely
|
19
|
+
@slides.reject! {|slide| slide.classes.include? 'supplemental' }
|
20
|
+
end
|
21
|
+
|
22
|
+
case Showoff::State.get(:format)
|
23
|
+
when 'web'
|
24
|
+
@slides.reject! {|slide| slide.classes.include? 'toc' }
|
25
|
+
@slides.reject! {|slide| slide.classes.include? 'printonly' }
|
26
|
+
when 'print', 'supplemental'
|
27
|
+
@slides.reject! {|slide| slide.classes.include? 'noprint' }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
def render
|
34
|
+
@slides.map(&:render).join("\n")
|
35
|
+
end
|
36
|
+
|
37
|
+
# Gets the raw file content from disk and partitions it by slide markers into
|
38
|
+
# content for each slide.
|
39
|
+
#
|
40
|
+
# Returns an array of Slide objects
|
41
|
+
#
|
42
|
+
# Source:
|
43
|
+
# https://github.com/puppetlabs/showoff/blob/3f43754c84f97be4284bb34f9bc7c42175d45226/lib/showoff.rb#L396-L414
|
44
|
+
def loadSlides(filename)
|
45
|
+
return unless filename.end_with? '.md'
|
46
|
+
|
47
|
+
content = File.read(File.join(Showoff::Locale.contentPath, filename))
|
48
|
+
|
49
|
+
# if there are no !SLIDE markers, then make every H1 define a new slide
|
50
|
+
unless content =~ /^\<?!SLIDE/m
|
51
|
+
content = content.gsub(/^# /m, "<!SLIDE>\n# ")
|
52
|
+
end
|
53
|
+
|
54
|
+
slides = content.split(/^<?!SLIDE\s?([^>]*)>?/)
|
55
|
+
slides.shift # has an extra empty string because the regex matches the entire source string.
|
56
|
+
|
57
|
+
# this is a counter keeping track of how many slides came from the file.
|
58
|
+
# It kicks in at 2 because at this point, slides are a tuple of (options, content)
|
59
|
+
seq = slides.size > 2 ? 1 : nil
|
60
|
+
|
61
|
+
# iterate each slide tuple and add slide objects to the array
|
62
|
+
slides.each_slice(2) do |data|
|
63
|
+
options, content = data
|
64
|
+
@slides << Showoff::Presentation::Slide.new(options, content, :section => @name, :name => filename, :seq => seq)
|
65
|
+
seq +=1 if seq
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'erb'
|
2
|
+
|
3
|
+
class Showoff::Presentation::Slide
|
4
|
+
attr_reader :section, :section_title, :name, :seq, :id, :ref, :background, :transition, :form, :markdown, :classes
|
5
|
+
|
6
|
+
def initialize(options, content, context={})
|
7
|
+
@markdown = content
|
8
|
+
@transition = 'none'
|
9
|
+
@classes = []
|
10
|
+
setOptions!(options)
|
11
|
+
setContext!(context)
|
12
|
+
end
|
13
|
+
|
14
|
+
def render
|
15
|
+
Showoff::State.increment(:slide_count)
|
16
|
+
options = { :form => @form,
|
17
|
+
:name => @name,
|
18
|
+
:seq => @seq,
|
19
|
+
}
|
20
|
+
|
21
|
+
content, notes = Showoff::Compiler.new(options).render(@markdown)
|
22
|
+
|
23
|
+
# if a template file has been specified for this slide, load from disk and render it
|
24
|
+
# @todo How many people are actually using these limited templates?!
|
25
|
+
if tpl_file = Showoff::Config.get('template', @template)
|
26
|
+
template = File.read(tpl_file)
|
27
|
+
content = template.gsub(/~~~CONTENT~~~/, content)
|
28
|
+
end
|
29
|
+
|
30
|
+
ERB.new(File.read(File.join(Showoff::GEMROOT, 'views','slide.erb')), nil, '-').result(binding)
|
31
|
+
end
|
32
|
+
|
33
|
+
# This is a list of classes that we want applied *only* to content, and not to the slide,
|
34
|
+
# typically so that overly aggressive selectors don't match more than they should.
|
35
|
+
#
|
36
|
+
# @see
|
37
|
+
# https://github.com/puppetlabs/showoff/blob/3f43754c84f97be4284bb34f9bc7c42175d45226/lib/showoff.rb#L734-L737
|
38
|
+
def slideClasses
|
39
|
+
blacklist = ['bigtext']
|
40
|
+
@classes.reject { |klass| blacklist.include? klass }
|
41
|
+
end
|
42
|
+
|
43
|
+
# options are key=value elements within the [] brackets
|
44
|
+
def setOptions!(options)
|
45
|
+
return unless options
|
46
|
+
return unless matches = options.match(/(\[(.*?)\])?(.*)/)
|
47
|
+
|
48
|
+
if matches[2]
|
49
|
+
matches[2].split(",").each do |element|
|
50
|
+
key, val = element.split("=")
|
51
|
+
case key
|
52
|
+
when 'tpl', 'template'
|
53
|
+
@template = val
|
54
|
+
when 'bg', 'background'
|
55
|
+
@background = val
|
56
|
+
# For legacy reasons, the options below may also be specified in classes.
|
57
|
+
# Currently that takes priority.
|
58
|
+
# @todo: better define the difference between options and classes.
|
59
|
+
when 'form'
|
60
|
+
@form = val
|
61
|
+
when 'id'
|
62
|
+
@id = val
|
63
|
+
when 'transition'
|
64
|
+
@transition = val
|
65
|
+
else
|
66
|
+
Showoff::Logger.warn "Unknown slide option: #{key}=#{val}"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
if matches[3]
|
72
|
+
@classes = matches[3].split
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# currently a mishmash of passed in context and calculated valued extracted from classes
|
77
|
+
def setContext!(context)
|
78
|
+
@section = context[:section] || 'main'
|
79
|
+
@name = context[:name].chomp('.md')
|
80
|
+
@seq = context[:seq]
|
81
|
+
|
82
|
+
#TODO: this should be in options
|
83
|
+
# extract id from classes if set, or default to the HTML sanitized name
|
84
|
+
@classes.delete_if { |x| x =~ /^#([\w-]+)/ && @id = $1 }
|
85
|
+
@id ||= @name.dup.gsub(/[^-A-Za-z0-9_]/, '_')
|
86
|
+
@id << seq.to_s if @seq
|
87
|
+
|
88
|
+
# provide an href for the slide. If we've got multiple slides in this file, we'll have a sequence number
|
89
|
+
# include that sequence number to index directly into that content
|
90
|
+
@ref = @seq ? "#{@name}:#{@seq.to_s}" : @name
|
91
|
+
|
92
|
+
#TODO: this should be in options
|
93
|
+
# extract transition from classes, or default to 'none'
|
94
|
+
@classes.delete_if { |x| x =~ /^transition=(.+)/ && @transition = $1 }
|
95
|
+
|
96
|
+
#TODO: this should be in options
|
97
|
+
# extract form id from classes, or default to nil
|
98
|
+
@classes.delete_if { |x| x =~ /^form=(.+)/ && @form = $1 }
|
99
|
+
|
100
|
+
# Extract a section title from subsection slides and add it to state so that it
|
101
|
+
# can be carried forward to subsequent slides until a new section title is discovered.
|
102
|
+
# @see
|
103
|
+
# https://github.com/puppetlabs/showoff/blob/3f43754c84f97be4284bb34f9bc7c42175d45226/lib/showoff.rb#L499-L508
|
104
|
+
if @classes.include? 'subsection'
|
105
|
+
matches = @markdown.match(/#+ *(.*?)#*$/)
|
106
|
+
@section_title = matches[1] || @section
|
107
|
+
Showoff::State.set(:section_title, @section_title)
|
108
|
+
else
|
109
|
+
@section_title = Showoff::State.get(:section_title) || @section
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|