slippery 0.0.1
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 +15 -0
- data/.travis.yml +27 -0
- data/Gemfile +10 -0
- data/Gemfile.devtools +59 -0
- data/Gemfile.lock +220 -0
- data/LICENSE +9 -0
- data/README.md +122 -0
- data/Rakefile +2 -0
- data/assets/impress.js/css/impress-demo.css +703 -0
- data/assets/impress.js/js/impress.js +800 -0
- data/assets/reveal.js/css/print/paper.css +176 -0
- data/assets/reveal.js/css/print/pdf.css +190 -0
- data/assets/reveal.js/css/reveal.css +1616 -0
- data/assets/reveal.js/css/reveal.min.css +7 -0
- data/assets/reveal.js/css/theme/README.md +23 -0
- data/assets/reveal.js/css/theme/beige.css +142 -0
- data/assets/reveal.js/css/theme/default.css +142 -0
- data/assets/reveal.js/css/theme/moon.css +142 -0
- data/assets/reveal.js/css/theme/night.css +130 -0
- data/assets/reveal.js/css/theme/serif.css +132 -0
- data/assets/reveal.js/css/theme/simple.css +132 -0
- data/assets/reveal.js/css/theme/sky.css +139 -0
- data/assets/reveal.js/css/theme/solarized.css +142 -0
- data/assets/reveal.js/css/theme/source/beige.scss +50 -0
- data/assets/reveal.js/css/theme/source/default.scss +42 -0
- data/assets/reveal.js/css/theme/source/moon.scss +68 -0
- data/assets/reveal.js/css/theme/source/night.scss +35 -0
- data/assets/reveal.js/css/theme/source/serif.scss +35 -0
- data/assets/reveal.js/css/theme/source/simple.scss +38 -0
- data/assets/reveal.js/css/theme/source/sky.scss +46 -0
- data/assets/reveal.js/css/theme/source/solarized.scss +74 -0
- data/assets/reveal.js/css/theme/template/mixins.scss +29 -0
- data/assets/reveal.js/css/theme/template/settings.scss +33 -0
- data/assets/reveal.js/css/theme/template/theme.scss +163 -0
- data/assets/reveal.js/js/head.min.js +8 -0
- data/assets/reveal.js/js/reveal.js +2577 -0
- data/assets/reveal.js/js/reveal.min.js +8 -0
- data/assets/reveal.js/lib/css/zenburn.css +115 -0
- data/assets/reveal.js/lib/font/league_gothic-webfont.eot +0 -0
- data/assets/reveal.js/lib/font/league_gothic-webfont.svg +230 -0
- data/assets/reveal.js/lib/font/league_gothic-webfont.ttf +0 -0
- data/assets/reveal.js/lib/font/league_gothic-webfont.woff +0 -0
- data/assets/reveal.js/lib/font/league_gothic_license +2 -0
- data/assets/reveal.js/lib/js/classList.js +2 -0
- data/assets/reveal.js/lib/js/head.min.js +8 -0
- data/assets/reveal.js/lib/js/html5shiv.js +7 -0
- data/assets/reveal.js/plugin/highlight/highlight.js +31 -0
- data/assets/reveal.js/plugin/leap/leap.js +154 -0
- data/assets/reveal.js/plugin/markdown/example.html +97 -0
- data/assets/reveal.js/plugin/markdown/example.md +29 -0
- data/assets/reveal.js/plugin/markdown/markdown.js +190 -0
- data/assets/reveal.js/plugin/markdown/marked.js +37 -0
- data/assets/reveal.js/plugin/multiplex/client.js +13 -0
- data/assets/reveal.js/plugin/multiplex/index.js +56 -0
- data/assets/reveal.js/plugin/multiplex/master.js +50 -0
- data/assets/reveal.js/plugin/notes/notes.html +253 -0
- data/assets/reveal.js/plugin/notes/notes.js +100 -0
- data/assets/reveal.js/plugin/notes-server/client.js +57 -0
- data/assets/reveal.js/plugin/notes-server/index.js +59 -0
- data/assets/reveal.js/plugin/notes-server/notes.html +142 -0
- data/assets/reveal.js/plugin/postmessage/example.html +39 -0
- data/assets/reveal.js/plugin/postmessage/postmessage.js +42 -0
- data/assets/reveal.js/plugin/print-pdf/print-pdf.js +44 -0
- data/assets/reveal.js/plugin/remotes/remotes.js +39 -0
- data/assets/reveal.js/plugin/search/search.js +196 -0
- data/assets/reveal.js/plugin/zoom-js/zoom.js +256 -0
- data/config/flay.yml +3 -0
- data/config/flog.yml +2 -0
- data/config/mutant.yml +3 -0
- data/config/reek.yml +108 -0
- data/config/rubocop.yml +71 -0
- data/config/yardstick.yml +2 -0
- data/lib/slippery/converter.rb +130 -0
- data/lib/slippery/document.rb +20 -0
- data/lib/slippery/presentation.rb +36 -0
- data/lib/slippery/processor_helpers.rb +43 -0
- data/lib/slippery/processors/add_google_font.rb +27 -0
- data/lib/slippery/processors/graphviz_dot.rb +46 -0
- data/lib/slippery/processors/hr_to_sections.rb +36 -0
- data/lib/slippery/processors/impress_js/add_impress_js.rb +30 -0
- data/lib/slippery/processors/impress_js/auto_offsets.rb +25 -0
- data/lib/slippery/processors/reveal_js/add_reveal_js.rb +78 -0
- data/lib/slippery/processors/self_contained.rb +62 -0
- data/lib/slippery/version.rb +3 -0
- data/lib/slippery.rb +42 -0
- data/slippery.gemspec +25 -0
- data/spec/fixtures/blockquotes.md +6 -0
- data/spec/fixtures/code_blocks.md +9 -0
- data/spec/fixtures/definition_lists.md +2 -0
- data/spec/fixtures/header_and_paragraph.md +2 -0
- data/spec/fixtures/headers.md +13 -0
- data/spec/fixtures/ordered_list.md +3 -0
- data/spec/fixtures/unordered_list.md +3 -0
- data/spec/slippery/converter_spec.rb +67 -0
- data/spec/slippery_spec.rb +0 -0
- data/spec/spec_helper.rb +20 -0
- metadata +208 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
# Main class that does the conversion from Markdown/Kramdown to Hexp.
|
|
4
|
+
# Subclass this for custom behavior.
|
|
5
|
+
#
|
|
6
|
+
# See https://github.com/3/blob/master/lib/kramdown/element.rb for a list of
|
|
7
|
+
# types
|
|
8
|
+
#
|
|
9
|
+
class Slippery::Converter
|
|
10
|
+
private_attr_accessor :type, :value, :attr, :children, :options
|
|
11
|
+
undef_method :p
|
|
12
|
+
|
|
13
|
+
# Convert a Kramdown syntax tree into Hexp.
|
|
14
|
+
#
|
|
15
|
+
# @example
|
|
16
|
+
# markdown = "# Hello!\n\nChunky *bacon*!\n"
|
|
17
|
+
# document = Kramdown::Document.new(markdown)
|
|
18
|
+
# hexp = converter.convert(document.root)
|
|
19
|
+
#
|
|
20
|
+
# @param el [Kramdown::Element] The root element to convert
|
|
21
|
+
# @return [Hexp::Node]
|
|
22
|
+
# @api public
|
|
23
|
+
#
|
|
24
|
+
def convert(el)
|
|
25
|
+
#Kernel.p el ; exit
|
|
26
|
+
@type, @value, @attr, @children, @options =
|
|
27
|
+
el.type, el.value, el.attr, el.children, el.options
|
|
28
|
+
send(type)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Process a Kramdown :root type element
|
|
32
|
+
#
|
|
33
|
+
# @return [Hexp::Node]
|
|
34
|
+
# @api semipublic
|
|
35
|
+
#
|
|
36
|
+
def root
|
|
37
|
+
H[:html, [H[:head], tag!(:body)]]
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Process a Kramdown :header type element
|
|
41
|
+
#
|
|
42
|
+
# @return [Hexp::Node]
|
|
43
|
+
# @api semipublic
|
|
44
|
+
#
|
|
45
|
+
def header
|
|
46
|
+
tag! "h#{options[:level]}".intern
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Process a Kramdown :codeblock type element
|
|
50
|
+
#
|
|
51
|
+
# @return [Hexp::Node]
|
|
52
|
+
# @api semipublic
|
|
53
|
+
#
|
|
54
|
+
def codeblock
|
|
55
|
+
H[:pre, attr, H[:code, value]]
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def smart_quote
|
|
59
|
+
{
|
|
60
|
+
:lsquo => '‘',
|
|
61
|
+
:rsquo => '’',
|
|
62
|
+
:ldquo => '“',
|
|
63
|
+
:rdquo => '”',
|
|
64
|
+
}[value]
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def typographic_sym
|
|
68
|
+
{
|
|
69
|
+
:hellip => '…',
|
|
70
|
+
:mdash => '—',
|
|
71
|
+
:ndash => '–',
|
|
72
|
+
:laquo => '«',
|
|
73
|
+
:raquo => '»',
|
|
74
|
+
:laquo_space => '« ',
|
|
75
|
+
:raquo_space => ' »',
|
|
76
|
+
}[value]
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def html_element
|
|
80
|
+
H[value.to_sym, attr, convert_children]
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def entity
|
|
84
|
+
value.char
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def codespan
|
|
88
|
+
H[:code, value]
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def xml_comment; end
|
|
92
|
+
|
|
93
|
+
# Create a Hexp::Node from the current element
|
|
94
|
+
#
|
|
95
|
+
# Helper for when you want to turn the Kramdown element straight into a
|
|
96
|
+
# Hexp::Node with the same attributes, and a one-to-one mapping of the child
|
|
97
|
+
# elements.
|
|
98
|
+
#
|
|
99
|
+
# @param tag [Symbol] The HTML tag to generate
|
|
100
|
+
# @return [Hexp::Node]
|
|
101
|
+
# @api semipublic
|
|
102
|
+
#
|
|
103
|
+
def tag!(tag)
|
|
104
|
+
H[tag, attr, convert_children]
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
[:text, :blank, :raw].each do |sym|
|
|
108
|
+
define_method sym do
|
|
109
|
+
Hexp::TextNode.new(value)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
[:p, :blockquote, :ul, :li, :ol, :dl, :dt, :dd, :em, :strong, :img, :a, :hr, :br].each do |sym|
|
|
114
|
+
define_method sym do
|
|
115
|
+
tag! type
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Convert the children of the Kramdown element to Hexps
|
|
120
|
+
#
|
|
121
|
+
# In other words, recurse down the tree. This will pass each
|
|
122
|
+
# child element into the converter.
|
|
123
|
+
#
|
|
124
|
+
# @return [Array<Hexp::Node>]
|
|
125
|
+
# @api private
|
|
126
|
+
#
|
|
127
|
+
def convert_children
|
|
128
|
+
children.map {|ch| convert ch }.compact
|
|
129
|
+
end
|
|
130
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module Slippery
|
|
2
|
+
class Document
|
|
3
|
+
include Hexp
|
|
4
|
+
|
|
5
|
+
private_attr_accessor :processors
|
|
6
|
+
|
|
7
|
+
def initialize(markdown)
|
|
8
|
+
@markdown = markdown
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def kramdown_document
|
|
12
|
+
@kramdown_document ||= Kramdown::Document.new(@markdown, input: 'GFM')
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def to_hexp
|
|
16
|
+
@hexp ||= Slippery::Converter.new.convert(kramdown_document.root).to_hexp
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
module Slippery
|
|
2
|
+
class Presentation
|
|
3
|
+
include Hexp
|
|
4
|
+
|
|
5
|
+
DEFAULT_OPTIONS = {
|
|
6
|
+
type: :reveal_js
|
|
7
|
+
}.freeze
|
|
8
|
+
|
|
9
|
+
def initialize(document, options = {})
|
|
10
|
+
@document = document
|
|
11
|
+
@options = DEFAULT_OPTIONS.merge(options).freeze
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def processors
|
|
15
|
+
{
|
|
16
|
+
:impress_js => [
|
|
17
|
+
HrToSections.new(H[:div, class: 'step']),
|
|
18
|
+
ImpressJs::AddImpressJs.new(js_options),
|
|
19
|
+
ImpressJs::AutoOffsets.new,
|
|
20
|
+
],
|
|
21
|
+
:reveal_js => [
|
|
22
|
+
HrToSections.new(H[:section]),
|
|
23
|
+
RevealJs::AddRevealJs.new(js_options),
|
|
24
|
+
]
|
|
25
|
+
}[@options[:type]]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def js_options
|
|
29
|
+
@options.reject {|key,_| [:type].include? key }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def to_hexp
|
|
33
|
+
@document.process(*processors)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module Slippery
|
|
2
|
+
module ProcessorHelpers
|
|
3
|
+
def self.included(klz)
|
|
4
|
+
klz.extend ClassMethods
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def include_local_javascript(element, relative_path)
|
|
8
|
+
element.add javascript_include_tag(asset_uri(relative_path))
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def include_local_css(element, relative_path)
|
|
12
|
+
element.add stylesheet_link_tag(asset_uri(relative_path))
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def asset_uri(path)
|
|
16
|
+
'file://' + File.expand_path('../../../assets/'+ path, __FILE__)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def javascript_include_tag(path)
|
|
20
|
+
H[:script, {src: path, type: 'text/javascript'}]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def stylesheet_link_tag(path)
|
|
24
|
+
H[:link, {href: path, rel: 'stylesheet'}]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def data_attributes(attrs)
|
|
28
|
+
Hash[*attrs.flat_map {|k,v| ["data-#{k}", v]}]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
module ClassMethods
|
|
32
|
+
def processor(name, selector = nil, &blk)
|
|
33
|
+
if selector
|
|
34
|
+
define_method name do
|
|
35
|
+
->(node) { node.replace(selector) {|node| instance_exec(node, &blk) } }
|
|
36
|
+
end
|
|
37
|
+
else
|
|
38
|
+
define_method name { ->(node) { blk.call(node) } }
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require 'cgi'
|
|
2
|
+
|
|
3
|
+
module Slippery
|
|
4
|
+
module Processors
|
|
5
|
+
class AddGoogleFont
|
|
6
|
+
def initialize(font_name, variants = [])
|
|
7
|
+
@font_name = font_name.freeze
|
|
8
|
+
@variants = variants.freeze
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def call(doc)
|
|
12
|
+
doc.rewrite('head', &add_font_link)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def add_font_link
|
|
16
|
+
->(head) {
|
|
17
|
+
head << H[:link, {rel: 'stylesheet', type: 'text/css', href: href}]
|
|
18
|
+
}
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def href
|
|
22
|
+
'http://fonts.googleapis.com/css?family=' +
|
|
23
|
+
CGI.escape(@font_name) + (@variants.empty? ? '' : ':' + @variants.join(','))
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
require 'tempfile'
|
|
2
|
+
|
|
3
|
+
module Slippery
|
|
4
|
+
module Processors
|
|
5
|
+
# Turn embedded dot files into embedded SVGs
|
|
6
|
+
class GraphvizDot
|
|
7
|
+
def initialize(selector = '.language-dot')
|
|
8
|
+
@selector = selector
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.call(doc)
|
|
12
|
+
self.new.call(doc)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def call(doc)
|
|
16
|
+
doc
|
|
17
|
+
.rewrite(@selector, &create_svg_from_dot)
|
|
18
|
+
.rewrite('polygon[fill=white][stroke=white]') { [] }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def create_svg_from_dot
|
|
22
|
+
->(node) do
|
|
23
|
+
dot_to_hexp(node.text).process(copy_width_height(node))
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def dot_to_hexp(dot_source)
|
|
28
|
+
file = Tempfile.new(['slippery','.dot'])
|
|
29
|
+
file << dot_source
|
|
30
|
+
file.close
|
|
31
|
+
Hexp.parse(`dot #{file.path} -Tsvg`)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def copy_width_height(node)
|
|
35
|
+
->(svg) do
|
|
36
|
+
return svg unless node[:width] || node[:height]
|
|
37
|
+
[:width, :height].each do |attr|
|
|
38
|
+
svg = svg.remove_attr(attr)
|
|
39
|
+
svg = svg.attr(attr, node[attr]) if node[attr]
|
|
40
|
+
end
|
|
41
|
+
svg
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
module Slippery
|
|
2
|
+
module Processors
|
|
3
|
+
# Take a flat list of elements, and wrap elements between <hr> lines into
|
|
4
|
+
# a sections.
|
|
5
|
+
#
|
|
6
|
+
# @example
|
|
7
|
+
# HrToSections.new('body', H[:section]).call(doc)
|
|
8
|
+
#
|
|
9
|
+
class HrToSections
|
|
10
|
+
def self.call(doc)
|
|
11
|
+
self.new.call(doc)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def initialize(wrapper = H[:section], selector = 'body')
|
|
15
|
+
@selector, @wrapper = selector, wrapper
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def call(doc)
|
|
19
|
+
doc.replace(@selector) { |element| hr_to_section(element) }
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def hr_to_section(element)
|
|
23
|
+
sections = [@wrapper]
|
|
24
|
+
element.children.each do |child|
|
|
25
|
+
if child.tag == :hr
|
|
26
|
+
sections << @wrapper.merge_attrs(child)
|
|
27
|
+
else
|
|
28
|
+
sections[-1] = sections.last << child
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
element.set_children(sections)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module Slippery
|
|
2
|
+
module Processors
|
|
3
|
+
module ImpressJs
|
|
4
|
+
class AddImpressJs
|
|
5
|
+
include ProcessorHelpers
|
|
6
|
+
|
|
7
|
+
def self.call(doc)
|
|
8
|
+
self.new.call(doc)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
attr_reader :attributes
|
|
12
|
+
|
|
13
|
+
DEFAULT_ATTRS = {'transition-duration' => 1000}.freeze
|
|
14
|
+
|
|
15
|
+
def initialize(attributes = {})
|
|
16
|
+
@attributes = DEFAULT_ATTRS.merge(attributes).freeze
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def call(doc)
|
|
20
|
+
doc.replace('body') do |body|
|
|
21
|
+
include_local_javascript(body, 'impress.js/js/impress.js')
|
|
22
|
+
.set_attributes({id: 'impress'}.merge(data_attributes(attributes)))
|
|
23
|
+
.add H[:script, "impress().init();"]
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module Slippery
|
|
2
|
+
module Processors
|
|
3
|
+
module ImpressJs
|
|
4
|
+
class AutoOffsets
|
|
5
|
+
def initialize(offset_x = 1000, offset_y = 0)
|
|
6
|
+
@offsets = [offset_x, offset_y]
|
|
7
|
+
@position = [0,0]
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def call(doc)
|
|
11
|
+
doc.replace('.step') do |step|
|
|
12
|
+
['data-x', 'data-y'].each_with_index do |axis, idx|
|
|
13
|
+
if step.attr?(axis)
|
|
14
|
+
@position[idx] = step[axis].to_i
|
|
15
|
+
else
|
|
16
|
+
@position[idx] += @offsets[idx]
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
step % { 'data-x' => @position[0], 'data-y' => @position[1] }
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
module Slippery
|
|
2
|
+
module Processors
|
|
3
|
+
module RevealJs
|
|
4
|
+
class AddRevealJs
|
|
5
|
+
include ProcessorHelpers
|
|
6
|
+
|
|
7
|
+
def self.call(doc)
|
|
8
|
+
self.new.call(doc)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
attr_reader :attributes
|
|
12
|
+
|
|
13
|
+
DEFAULT_OPTIONS = {theme: 'default'}.freeze
|
|
14
|
+
|
|
15
|
+
def initialize(options = {})
|
|
16
|
+
@options = DEFAULT_OPTIONS.merge(options).freeze
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def call(doc)
|
|
20
|
+
doc.process(
|
|
21
|
+
reveal_wrap,
|
|
22
|
+
add_reveal_js,
|
|
23
|
+
add_reveal_css,
|
|
24
|
+
add_theme,
|
|
25
|
+
add_settings
|
|
26
|
+
)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
processor :add_reveal_js, 'body' do |body|
|
|
30
|
+
body = include_local_javascript(body, 'reveal.js/lib/js/head.min.js')
|
|
31
|
+
include_local_javascript(body, 'reveal.js/js/reveal.js')
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
processor :add_reveal_css, 'head' do |head|
|
|
35
|
+
include_local_css(head, 'reveal.js/css/reveal.min.css')
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
processor :add_theme, 'head' do |head|
|
|
39
|
+
include_local_css(head, "reveal.js/css/theme/#{@options[:theme]}.css")
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
processor :add_settings, 'body' do |body|
|
|
43
|
+
body.add(H[:script, "Reveal.initialize({ #{plugin_settings}, #{settings.map {|k,v| "#{k}:#{v.inspect}"}.join(',')} });"])
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
processor :reveal_wrap, 'body' do |body|
|
|
47
|
+
body.set_children(
|
|
48
|
+
H[:div, {class: 'reveal'}, [
|
|
49
|
+
H[:div, {class: 'slides'}, body.children]
|
|
50
|
+
]
|
|
51
|
+
]
|
|
52
|
+
)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def settings
|
|
56
|
+
@options.reject{|key,_| [:theme, :plugins].include? key }
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def plugin_settings
|
|
60
|
+
"dependencies: [" +
|
|
61
|
+
Array(@options.fetch(:plugins, [])).map do |name|
|
|
62
|
+
plugin_config name
|
|
63
|
+
end.join(',') + "]"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def plugins(name)
|
|
67
|
+
{
|
|
68
|
+
notes: 'plugin/notes/notes.js'
|
|
69
|
+
}[name]
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def plugin_config(plugin)
|
|
73
|
+
"{ src: #{plugins(plugin).inspect}, async: true, condition: function() { return !!document.body.classList; } }"
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
require 'base64'
|
|
2
|
+
require 'open-uri'
|
|
3
|
+
|
|
4
|
+
module Slippery
|
|
5
|
+
module Processors
|
|
6
|
+
class SelfContained
|
|
7
|
+
def self.call(doc)
|
|
8
|
+
self.new.call(doc)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def call(doc)
|
|
12
|
+
doc
|
|
13
|
+
.rewrite('link[rel=stylesheet]', &convert_stylesheet_to_inline)
|
|
14
|
+
.rewrite('script[src]', &convert_script_to_inline)
|
|
15
|
+
.rewrite('img', &convert_image_to_data_uri)
|
|
16
|
+
#.rewrite('style', &convert_style_uri_to_data_uri)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def convert_stylesheet_to_inline
|
|
20
|
+
->(link) { H[:style, { type: 'text/css' }, read_uri(link[:href])] }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def convert_script_to_inline
|
|
24
|
+
->(script) do
|
|
25
|
+
attrs = script.attributes.reject { |k,v| k=='src' }
|
|
26
|
+
H[:script, attrs, read_uri(script[:src])]
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def convert_image_to_data_uri
|
|
31
|
+
->(img) { H[:img, img.attributes.merge(src: data_uri(img[:src]))] }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# def convert_style_uri_to_data_uri
|
|
35
|
+
# ->(style) {
|
|
36
|
+
# H[:style, style.attributes, style.children.first.gsub(/url\(['"]?[^'"\)]+['"]?\)/) {|url| "url('#{data_uri url[/url\(['"]?([^'"\)]+)/,1] }')"} ] }
|
|
37
|
+
# end
|
|
38
|
+
|
|
39
|
+
def read_uri(uri)
|
|
40
|
+
@@download_cache ||= {}
|
|
41
|
+
if uri =~ /http/
|
|
42
|
+
@@download_cache[uri] ||= open(uri.sub('file://', '')).read
|
|
43
|
+
else
|
|
44
|
+
open(uri.sub('file://', '')).read
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def data_uri(uri)
|
|
49
|
+
base64 = Base64.encode64(read_uri uri)
|
|
50
|
+
ext = File.extname(uri)
|
|
51
|
+
type = {
|
|
52
|
+
'.jpg' => 'image/jpeg',
|
|
53
|
+
'.jpeg' => 'image/jpeg',
|
|
54
|
+
'.png' => 'image/png',
|
|
55
|
+
'.gif' => 'image/gif',
|
|
56
|
+
}[ext.downcase]
|
|
57
|
+
|
|
58
|
+
"data:#{type};base64,#{base64}"
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
data/lib/slippery.rb
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
require 'kramdown'
|
|
2
|
+
require 'hexp'
|
|
3
|
+
|
|
4
|
+
# Slippery namespace module
|
|
5
|
+
module Slippery
|
|
6
|
+
def self.convert(element)
|
|
7
|
+
Slippery::Converter.new.convert(element)
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Core extension
|
|
12
|
+
class Class
|
|
13
|
+
unless defined?(private_attr_accessor)
|
|
14
|
+
# Like attr_accessor, but only available inside the class
|
|
15
|
+
#
|
|
16
|
+
# @param args [Array<Symbol>] The attributes to define
|
|
17
|
+
# @api private
|
|
18
|
+
#
|
|
19
|
+
def private_attr_accessor(*args)
|
|
20
|
+
attr_accessor(*args)
|
|
21
|
+
private(*args)
|
|
22
|
+
private(*args.map {|method| "#{method}=".intern })
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
require 'slippery/version'
|
|
28
|
+
require 'slippery/converter'
|
|
29
|
+
require 'slippery/document'
|
|
30
|
+
require 'slippery/presentation'
|
|
31
|
+
|
|
32
|
+
require 'slippery/processor_helpers'
|
|
33
|
+
|
|
34
|
+
require 'slippery/processors/add_google_font'
|
|
35
|
+
require 'slippery/processors/graphviz_dot'
|
|
36
|
+
require 'slippery/processors/hr_to_sections'
|
|
37
|
+
require 'slippery/processors/self_contained'
|
|
38
|
+
|
|
39
|
+
require 'slippery/processors/impress_js/add_impress_js'
|
|
40
|
+
require 'slippery/processors/impress_js/auto_offsets'
|
|
41
|
+
|
|
42
|
+
require 'slippery/processors/reveal_js/add_reveal_js'
|
data/slippery.gemspec
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require File.expand_path('../lib/slippery/version', __FILE__)
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |gem|
|
|
6
|
+
gem.name = 'slippery'
|
|
7
|
+
gem.version = Slippery::VERSION
|
|
8
|
+
gem.authors = [ 'Arne Brasseur' ]
|
|
9
|
+
gem.email = [ 'arne@arnebrasseur.net' ]
|
|
10
|
+
gem.description = 'Make presentations with Markdown'
|
|
11
|
+
gem.summary = gem.description
|
|
12
|
+
gem.homepage = 'https://github.com/plexus/slippery'
|
|
13
|
+
gem.license = 'MIT'
|
|
14
|
+
|
|
15
|
+
gem.require_paths = %w[lib]
|
|
16
|
+
gem.files = `git ls-files`.split($/)
|
|
17
|
+
gem.test_files = `git ls-files -- spec`.split($/)
|
|
18
|
+
gem.extra_rdoc_files = %w[README.md LICENSE]
|
|
19
|
+
|
|
20
|
+
gem.add_runtime_dependency 'hexp' , '~> 0.0'
|
|
21
|
+
gem.add_runtime_dependency 'kramdown' , '~> 1.1'
|
|
22
|
+
|
|
23
|
+
gem.add_development_dependency 'rake', '~> 10.1'
|
|
24
|
+
gem.add_development_dependency 'rspec', '~> 2.14'
|
|
25
|
+
end
|