parade 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +21 -0
- data/README.md +542 -0
- data/Rakefile +15 -0
- data/bin/parade +138 -0
- data/lib/parade.rb +43 -0
- data/lib/parade/commands/commands.rb +84 -0
- data/lib/parade/commands/generate_outline.rb +34 -0
- data/lib/parade/commands/generate_presentation.rb +34 -0
- data/lib/parade/commands/generate_rackup.rb +32 -0
- data/lib/parade/commands/html_output.rb +47 -0
- data/lib/parade/commands/render_from_template.rb +50 -0
- data/lib/parade/commands/static_html.rb +38 -0
- data/lib/parade/commands/static_pdf.rb +39 -0
- data/lib/parade/commands/unknown.rb +23 -0
- data/lib/parade/features/live_ruby.rb +18 -0
- data/lib/parade/features/pdf_presentation.rb +24 -0
- data/lib/parade/features/preshow.rb +11 -0
- data/lib/parade/helpers/encode_image.rb +24 -0
- data/lib/parade/helpers/template_generator.rb +130 -0
- data/lib/parade/metadata.rb +73 -0
- data/lib/parade/metadata/assignment.rb +38 -0
- data/lib/parade/metadata/css_classes.rb +22 -0
- data/lib/parade/metadata/html_id.rb +35 -0
- data/lib/parade/metadata/template.rb +31 -0
- data/lib/parade/parsers/dsl.rb +138 -0
- data/lib/parade/parsers/dsl_file_parser.rb +17 -0
- data/lib/parade/parsers/json_file_parser.rb +67 -0
- data/lib/parade/parsers/markdown_image_paths.rb +44 -0
- data/lib/parade/parsers/markdown_slide_splitter.rb +63 -0
- data/lib/parade/parsers/presentation_directory_parser.rb +36 -0
- data/lib/parade/parsers/presentation_file_parser.rb +27 -0
- data/lib/parade/parsers/presentation_filepath_parser.rb +35 -0
- data/lib/parade/parsers/slides_file_content_parser.rb +27 -0
- data/lib/parade/renderers/columns_renderer.rb +68 -0
- data/lib/parade/renderers/command_line_renderer.rb +142 -0
- data/lib/parade/renderers/html_with_pygments.rb +42 -0
- data/lib/parade/renderers/inline_images.rb +31 -0
- data/lib/parade/renderers/special_paragraph_renderer.rb +23 -0
- data/lib/parade/renderers/update_image_paths.rb +75 -0
- data/lib/parade/section.rb +183 -0
- data/lib/parade/server.rb +139 -0
- data/lib/parade/slide.rb +128 -0
- data/lib/parade/version.rb +3 -0
- data/lib/public/css/960.css +653 -0
- data/lib/public/css/fg.menu.css +114 -0
- data/lib/public/css/ghf_marked.css +180 -0
- data/lib/public/css/jquery-terminal.css +73 -0
- data/lib/public/css/onepage.css +62 -0
- data/lib/public/css/parade.css +450 -0
- data/lib/public/css/pdf.css +13 -0
- data/lib/public/css/reset.css +53 -0
- data/lib/public/css/spinner_bar.gif +0 -0
- data/lib/public/css/theme/images/ui-bg_diagonals-small_100_f0efea_40x40.png +0 -0
- data/lib/public/css/theme/images/ui-bg_flat_35_f0f0f0_40x100.png +0 -0
- data/lib/public/css/theme/images/ui-bg_glass_55_fcf0ba_1x400.png +0 -0
- data/lib/public/css/theme/images/ui-bg_glow-ball_25_2e2e28_600x600.png +0 -0
- data/lib/public/css/theme/images/ui-bg_highlight-soft_100_f0efea_1x100.png +0 -0
- data/lib/public/css/theme/images/ui-bg_highlight-soft_25_327E04_1x100.png +0 -0
- data/lib/public/css/theme/images/ui-bg_highlight-soft_25_5A9D1A_1x100.png +0 -0
- data/lib/public/css/theme/images/ui-bg_highlight-soft_95_ffedad_1x100.png +0 -0
- data/lib/public/css/theme/images/ui-bg_inset-soft_22_3b3b35_1x100.png +0 -0
- data/lib/public/css/theme/images/ui-icons_808080_256x240.png +0 -0
- data/lib/public/css/theme/images/ui-icons_8DC262_256x240.png +0 -0
- data/lib/public/css/theme/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/lib/public/css/theme/images/ui-icons_e7e6e4_256x240.png +0 -0
- data/lib/public/css/theme/images/ui-icons_eeeeee_256x240.png +0 -0
- data/lib/public/css/theme/images/ui-icons_ffffff_256x240.png +0 -0
- data/lib/public/css/theme/ui.accordion.css +9 -0
- data/lib/public/css/theme/ui.all.css +2 -0
- data/lib/public/css/theme/ui.base.css +9 -0
- data/lib/public/css/theme/ui.core.css +37 -0
- data/lib/public/css/theme/ui.datepicker.css +62 -0
- data/lib/public/css/theme/ui.dialog.css +13 -0
- data/lib/public/css/theme/ui.progressbar.css +4 -0
- data/lib/public/css/theme/ui.resizable.css +13 -0
- data/lib/public/css/theme/ui.slider.css +17 -0
- data/lib/public/css/theme/ui.tabs.css +9 -0
- data/lib/public/css/theme/ui.theme.css +245 -0
- data/lib/public/favicon.ico +0 -0
- data/lib/public/js/coffee-script.js +8 -0
- data/lib/public/js/fg.menu.js +645 -0
- data/lib/public/js/jTypeWriter.js +26 -0
- data/lib/public/js/jquery-1.4.2.js +6240 -0
- data/lib/public/js/jquery-print.js +109 -0
- data/lib/public/js/jquery-pubsub.js +27 -0
- data/lib/public/js/jquery-terminal.js +2712 -0
- data/lib/public/js/jquery.batchImageLoad.js +56 -0
- data/lib/public/js/jquery.cycle.all.js +1284 -0
- data/lib/public/js/keyboard.js +733 -0
- data/lib/public/js/parade-code-execution.js +122 -0
- data/lib/public/js/parade-command-input.js +16 -0
- data/lib/public/js/parade-command-visor.js +92 -0
- data/lib/public/js/parade-keyboard-input.js +54 -0
- data/lib/public/js/parade.js +675 -0
- data/lib/public/js/spine.js +904 -0
- data/lib/templates/config.ru.erb +4 -0
- data/lib/templates/showoff.erb +27 -0
- data/lib/templates/slides.md.erb +25 -0
- data/lib/views/header.erb +73 -0
- data/lib/views/index.erb +53 -0
- data/lib/views/inline_css.erb +3 -0
- data/lib/views/inline_js.erb +3 -0
- data/lib/views/onepage.erb +17 -0
- data/lib/views/pdf.erb +17 -0
- data/lib/views/slide.erb +5 -0
- metadata +317 -0
@@ -0,0 +1,38 @@
|
|
1
|
+
require_relative 'html_output'
|
2
|
+
|
3
|
+
module Parade
|
4
|
+
module Commands
|
5
|
+
|
6
|
+
#
|
7
|
+
# Saves an html representation of the presentation to a single HTML file.
|
8
|
+
#
|
9
|
+
# @see HtmlOutput
|
10
|
+
#
|
11
|
+
class StaticHtml
|
12
|
+
include RenderFromTemplate
|
13
|
+
|
14
|
+
def description
|
15
|
+
"Output into a single HTML file"
|
16
|
+
end
|
17
|
+
|
18
|
+
def generate(options)
|
19
|
+
options.merge!('template' => 'onepage')
|
20
|
+
|
21
|
+
html_generator = HtmlOutput.new
|
22
|
+
html_content = html_generator.generate(options)
|
23
|
+
|
24
|
+
output_file = options[:output] || default_html_output
|
25
|
+
|
26
|
+
if create_file_with_contents output_file, html_content, options
|
27
|
+
puts "Saved HTML to #{output_file}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def default_html_output
|
32
|
+
"presentation.html"
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require_relative 'html_output'
|
2
|
+
|
3
|
+
module Parade
|
4
|
+
module Commands
|
5
|
+
|
6
|
+
#
|
7
|
+
# Saves a PDF version of the presentation that is from the HtmlOutput
|
8
|
+
#
|
9
|
+
class StaticPdf
|
10
|
+
|
11
|
+
def description
|
12
|
+
"Output into a PDF format"
|
13
|
+
end
|
14
|
+
|
15
|
+
def generate(options)
|
16
|
+
options.merge!('template' => 'pdf')
|
17
|
+
|
18
|
+
html_generator = HtmlOutput.new
|
19
|
+
html_content = html_generator.generate(options)
|
20
|
+
|
21
|
+
kit = PDFKit.new(html_content,:page_size => 'Letter', :orientation => 'Landscape')
|
22
|
+
|
23
|
+
output_file = options[:output] || default_pdf_output
|
24
|
+
|
25
|
+
return if (File.exists?(output_file) and not options.key?(:force))
|
26
|
+
|
27
|
+
kit.to_file(output_file)
|
28
|
+
|
29
|
+
puts "Saved PDF to #{output_file}"
|
30
|
+
end
|
31
|
+
|
32
|
+
def default_pdf_output
|
33
|
+
"presentation.pdf"
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
|
2
|
+
module Parade
|
3
|
+
module Commands
|
4
|
+
|
5
|
+
#
|
6
|
+
# The Unknown Command is provided as the default for the command lists
|
7
|
+
# as a default to ensure that a nil is not returned, allowing a for
|
8
|
+
# an object that adheres to the generate method.
|
9
|
+
#
|
10
|
+
class Unknown
|
11
|
+
|
12
|
+
def description
|
13
|
+
"Could not find the command specified"
|
14
|
+
end
|
15
|
+
|
16
|
+
def generate(options)
|
17
|
+
puts description
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Parade
|
2
|
+
module PDFPresentation
|
3
|
+
def self.included(server)
|
4
|
+
|
5
|
+
server.get '/pdf' do
|
6
|
+
|
7
|
+
# TODO: Find the presentation file and/or regenerate it every time
|
8
|
+
|
9
|
+
template_options = { 'erb_template_file' => File.join(File.dirname(__FILE__), "..", "..", "views", "pdf.erb"),
|
10
|
+
'custom_asset_path' => settings.presentation_directory,
|
11
|
+
'slides' => slides }
|
12
|
+
|
13
|
+
html_content = TemplateGenerator.new(template_options).render
|
14
|
+
|
15
|
+
# TODO the image references here are not full filepaths. creating issues
|
16
|
+
|
17
|
+
kit = PDFKit.new(html_content,:page_size => 'Letter', :orientation => 'Landscape')
|
18
|
+
|
19
|
+
send_file kit.to_file('presentation.pdf')
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'base64'
|
2
|
+
|
3
|
+
module Parade
|
4
|
+
module Helpers
|
5
|
+
|
6
|
+
module EncodeImage
|
7
|
+
|
8
|
+
def image_path_to_base64
|
9
|
+
$stderr.puts "Please install `rmagick` to allow images to be included in content"
|
10
|
+
''
|
11
|
+
end
|
12
|
+
|
13
|
+
if defined?(Magick)
|
14
|
+
def image_path_to_base64(path)
|
15
|
+
magick_image = Magick::Image.read(path).first
|
16
|
+
data = Base64.encode64(magick_image.to_blob).gsub("\n",'').strip
|
17
|
+
"data:image/#{magick_image.format};base64,#{data}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
module Parade
|
4
|
+
|
5
|
+
#
|
6
|
+
# TemplateGenator uses ERB to generate a template and uses itself as the
|
7
|
+
# reference as the binding. This template generator is being used primarily
|
8
|
+
# as a way to generate static versions of the content in HTML.
|
9
|
+
#
|
10
|
+
# When created an :erb_template_file needs to be specified in the Hash, all
|
11
|
+
# other fields are dependent on what is contained within the template itself
|
12
|
+
#
|
13
|
+
class TemplateGenerator < OpenStruct
|
14
|
+
|
15
|
+
#
|
16
|
+
# @param [String] filepath the filepath to load
|
17
|
+
#
|
18
|
+
def css_template(filepath)
|
19
|
+
CSSTemplateGenerator.new :filepath => filepath
|
20
|
+
end
|
21
|
+
|
22
|
+
def js_template(filepath)
|
23
|
+
JSTemplateGenerator.new :filepath => filepath
|
24
|
+
end
|
25
|
+
|
26
|
+
#
|
27
|
+
# Return javascript to be inlined in a template
|
28
|
+
#
|
29
|
+
# @param [String] filepath the filepath to the javascript file
|
30
|
+
# @return [String] HTML content that is inlined javascript data
|
31
|
+
def js(filepath)
|
32
|
+
js_template(filepath).render
|
33
|
+
end
|
34
|
+
|
35
|
+
#
|
36
|
+
# Return stylesheet to be inlined in a template
|
37
|
+
#
|
38
|
+
# @param [String] filepath the filepath to the stylesheet file
|
39
|
+
# @return [String] HTML content that is inlined stylesheet data
|
40
|
+
#
|
41
|
+
def css(filepath)
|
42
|
+
css_template(filepath).render
|
43
|
+
end
|
44
|
+
|
45
|
+
def custom_css_files
|
46
|
+
if custom_asset_path
|
47
|
+
Dir.glob("#{custom_asset_path}**/*.css").map do |path|
|
48
|
+
css_template(path).render
|
49
|
+
end.join("\n")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def custom_js_files
|
54
|
+
if custom_asset_path
|
55
|
+
Dir.glob("#{custom_asset_path}**/*.js").map do |path|
|
56
|
+
js_template(path).render
|
57
|
+
end.join("\n")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
#
|
62
|
+
# To provide support of having references to other templates, this will
|
63
|
+
# handle erb method calls and in-line that template's content
|
64
|
+
#
|
65
|
+
# @param [Strign] filepath the filepath to another template
|
66
|
+
# @return [String] HTML content of the specfied template at the filepath
|
67
|
+
#
|
68
|
+
def erb(filepath)
|
69
|
+
template_filepath = File.join File.dirname(erb_template_file), "#{filepath}.erb"
|
70
|
+
template_file = ERB.new File.read(template_filepath)
|
71
|
+
template_file.result(binding)
|
72
|
+
end
|
73
|
+
|
74
|
+
#
|
75
|
+
# @return [Types] the HTML content of the template specified
|
76
|
+
#
|
77
|
+
def render
|
78
|
+
template_file = ERB.new File.read(erb_template_file)
|
79
|
+
template_file.result(binding)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
#
|
84
|
+
# Generate inline CSS assets. Using CssParser it is able to traverse imports
|
85
|
+
# to ensure all CSS is inlined within the document.
|
86
|
+
#
|
87
|
+
# Also embeds all images contained within the CSS into the inlined CSS.
|
88
|
+
#
|
89
|
+
class CSSTemplateGenerator < TemplateGenerator
|
90
|
+
include Helpers::EncodeImage
|
91
|
+
|
92
|
+
def content
|
93
|
+
content_filepath = File.exists?(filepath) ? filepath : File.join(File.dirname(__FILE__), "..", "..", "public", "css", filepath)
|
94
|
+
parser = CssParser::Parser.new
|
95
|
+
parser.load_file!(content_filepath)
|
96
|
+
|
97
|
+
css_contents = parser.to_s
|
98
|
+
|
99
|
+
css_contents.gsub(/url\([\s"']*([^\)"'\s]*)[\s"']*\)/m) do |image_uri|
|
100
|
+
image_name = Regexp.last_match(1)
|
101
|
+
image_path = File.join File.dirname(content_filepath), image_name
|
102
|
+
base64_data = image_path_to_base64(image_path)
|
103
|
+
"url(#{base64_data})"
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
def erb_template_file
|
109
|
+
File.join File.dirname(__FILE__), "..", "..", "views", "inline_css.erb"
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
#
|
115
|
+
# Inline the specified javascript
|
116
|
+
#
|
117
|
+
class JSTemplateGenerator < TemplateGenerator
|
118
|
+
|
119
|
+
def content
|
120
|
+
content_filepath = File.exists?(filepath) ? filepath : File.join(File.dirname(__FILE__), "..", "..", "public", "js", filepath)
|
121
|
+
File.read content_filepath
|
122
|
+
end
|
123
|
+
|
124
|
+
def erb_template_file
|
125
|
+
File.join File.dirname(__FILE__), "..", "..", "views", "inline_js.erb"
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require_relative 'metadata/assignment'
|
2
|
+
require_relative 'metadata/css_classes'
|
3
|
+
require_relative 'metadata/html_id'
|
4
|
+
require_relative 'metadata/template'
|
5
|
+
|
6
|
+
module Parade
|
7
|
+
|
8
|
+
#
|
9
|
+
# Slides within Parade contain metadata. This metadata allows you to set
|
10
|
+
# CSS classes and IDs. You are also able, as well to assigna unique
|
11
|
+
# transitions between each slide.
|
12
|
+
#
|
13
|
+
class Metadata
|
14
|
+
|
15
|
+
#
|
16
|
+
# @example Raw Metadata that contains CSS Class, ID, transitions, and template data
|
17
|
+
#
|
18
|
+
# metadata = Metadata.parse "transition=fade one two #id three tpl=template_name"
|
19
|
+
# metadata.classes # => [ 'one', 'two', 'three' ]
|
20
|
+
# metadata.transition # => 'fade'
|
21
|
+
# metadata.id # => 'id'
|
22
|
+
# metadata.template # => 'template_name'
|
23
|
+
#
|
24
|
+
# @param [String] metadata a single string that contains all the raw metadata.
|
25
|
+
#
|
26
|
+
def self.parse(metadata)
|
27
|
+
|
28
|
+
metadata_hash = {}
|
29
|
+
|
30
|
+
metadata.to_s.split(' ').each do |term|
|
31
|
+
metadata_type = parsers.find {|parser| parser.match? term }
|
32
|
+
metadata_type.apply(term,metadata_hash)
|
33
|
+
end
|
34
|
+
|
35
|
+
self.new metadata_hash
|
36
|
+
end
|
37
|
+
|
38
|
+
#
|
39
|
+
# The list of the parsers that are used to parse the metadata string.
|
40
|
+
#
|
41
|
+
def self.parsers
|
42
|
+
[ Template.new,
|
43
|
+
Assignment.new,
|
44
|
+
HTMLId.new,
|
45
|
+
CSSClasses.new ]
|
46
|
+
end
|
47
|
+
|
48
|
+
#
|
49
|
+
# The initialize method is generally not called directly and instead is
|
50
|
+
# used through the `Metadata.parse` method.
|
51
|
+
#
|
52
|
+
# @param [Hash] params this hash contains the key-value pairs that map
|
53
|
+
# directly to accessor methods within the Metadata class.
|
54
|
+
#
|
55
|
+
# @see Metadata.parse
|
56
|
+
#
|
57
|
+
def initialize(params = {})
|
58
|
+
params.each {|k,v| send("#{k}=",v) if respond_to? "#{k}=" }
|
59
|
+
end
|
60
|
+
|
61
|
+
attr_writer :classes
|
62
|
+
|
63
|
+
def classes
|
64
|
+
@classes || []
|
65
|
+
end
|
66
|
+
|
67
|
+
attr_accessor :id
|
68
|
+
attr_accessor :transition
|
69
|
+
attr_accessor :template
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Parade
|
2
|
+
class Metadata
|
3
|
+
|
4
|
+
#
|
5
|
+
# The Assignment metadata parser is a general parser that allows the ability
|
6
|
+
# to assign a value to the specified field of the metadata. This is
|
7
|
+
# usually used for assigning the *transition* field but could be used to set
|
8
|
+
# the *id* in the metadata.
|
9
|
+
#
|
10
|
+
# @example Settings the Metadata id
|
11
|
+
#
|
12
|
+
# metadata = Metadata.parse "id=unique-slide-id"
|
13
|
+
# metadata.id # => "unique-slide-id"
|
14
|
+
#
|
15
|
+
# @see Metadata
|
16
|
+
#
|
17
|
+
class Assignment
|
18
|
+
|
19
|
+
def match?(term)
|
20
|
+
term =~ /.+=.+/
|
21
|
+
end
|
22
|
+
|
23
|
+
def apply(term,hash)
|
24
|
+
key, value = parse(term)
|
25
|
+
hash[key] = value
|
26
|
+
hash
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def parse(term)
|
32
|
+
term.split('=')
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Parade
|
2
|
+
class Metadata
|
3
|
+
|
4
|
+
#
|
5
|
+
# By default the CSS Class metadata parser is a catch all parser that will
|
6
|
+
# use all the terms and create CSS classes.
|
7
|
+
#
|
8
|
+
class CSSClasses
|
9
|
+
|
10
|
+
def match?(term)
|
11
|
+
true
|
12
|
+
end
|
13
|
+
|
14
|
+
def apply(term,hash)
|
15
|
+
(hash[:classes] ||= []) << term
|
16
|
+
hash
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|