showoff 0.20.1 → 0.20.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|