parade 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. data/LICENSE +21 -0
  2. data/README.md +542 -0
  3. data/Rakefile +15 -0
  4. data/bin/parade +138 -0
  5. data/lib/parade.rb +43 -0
  6. data/lib/parade/commands/commands.rb +84 -0
  7. data/lib/parade/commands/generate_outline.rb +34 -0
  8. data/lib/parade/commands/generate_presentation.rb +34 -0
  9. data/lib/parade/commands/generate_rackup.rb +32 -0
  10. data/lib/parade/commands/html_output.rb +47 -0
  11. data/lib/parade/commands/render_from_template.rb +50 -0
  12. data/lib/parade/commands/static_html.rb +38 -0
  13. data/lib/parade/commands/static_pdf.rb +39 -0
  14. data/lib/parade/commands/unknown.rb +23 -0
  15. data/lib/parade/features/live_ruby.rb +18 -0
  16. data/lib/parade/features/pdf_presentation.rb +24 -0
  17. data/lib/parade/features/preshow.rb +11 -0
  18. data/lib/parade/helpers/encode_image.rb +24 -0
  19. data/lib/parade/helpers/template_generator.rb +130 -0
  20. data/lib/parade/metadata.rb +73 -0
  21. data/lib/parade/metadata/assignment.rb +38 -0
  22. data/lib/parade/metadata/css_classes.rb +22 -0
  23. data/lib/parade/metadata/html_id.rb +35 -0
  24. data/lib/parade/metadata/template.rb +31 -0
  25. data/lib/parade/parsers/dsl.rb +138 -0
  26. data/lib/parade/parsers/dsl_file_parser.rb +17 -0
  27. data/lib/parade/parsers/json_file_parser.rb +67 -0
  28. data/lib/parade/parsers/markdown_image_paths.rb +44 -0
  29. data/lib/parade/parsers/markdown_slide_splitter.rb +63 -0
  30. data/lib/parade/parsers/presentation_directory_parser.rb +36 -0
  31. data/lib/parade/parsers/presentation_file_parser.rb +27 -0
  32. data/lib/parade/parsers/presentation_filepath_parser.rb +35 -0
  33. data/lib/parade/parsers/slides_file_content_parser.rb +27 -0
  34. data/lib/parade/renderers/columns_renderer.rb +68 -0
  35. data/lib/parade/renderers/command_line_renderer.rb +142 -0
  36. data/lib/parade/renderers/html_with_pygments.rb +42 -0
  37. data/lib/parade/renderers/inline_images.rb +31 -0
  38. data/lib/parade/renderers/special_paragraph_renderer.rb +23 -0
  39. data/lib/parade/renderers/update_image_paths.rb +75 -0
  40. data/lib/parade/section.rb +183 -0
  41. data/lib/parade/server.rb +139 -0
  42. data/lib/parade/slide.rb +128 -0
  43. data/lib/parade/version.rb +3 -0
  44. data/lib/public/css/960.css +653 -0
  45. data/lib/public/css/fg.menu.css +114 -0
  46. data/lib/public/css/ghf_marked.css +180 -0
  47. data/lib/public/css/jquery-terminal.css +73 -0
  48. data/lib/public/css/onepage.css +62 -0
  49. data/lib/public/css/parade.css +450 -0
  50. data/lib/public/css/pdf.css +13 -0
  51. data/lib/public/css/reset.css +53 -0
  52. data/lib/public/css/spinner_bar.gif +0 -0
  53. data/lib/public/css/theme/images/ui-bg_diagonals-small_100_f0efea_40x40.png +0 -0
  54. data/lib/public/css/theme/images/ui-bg_flat_35_f0f0f0_40x100.png +0 -0
  55. data/lib/public/css/theme/images/ui-bg_glass_55_fcf0ba_1x400.png +0 -0
  56. data/lib/public/css/theme/images/ui-bg_glow-ball_25_2e2e28_600x600.png +0 -0
  57. data/lib/public/css/theme/images/ui-bg_highlight-soft_100_f0efea_1x100.png +0 -0
  58. data/lib/public/css/theme/images/ui-bg_highlight-soft_25_327E04_1x100.png +0 -0
  59. data/lib/public/css/theme/images/ui-bg_highlight-soft_25_5A9D1A_1x100.png +0 -0
  60. data/lib/public/css/theme/images/ui-bg_highlight-soft_95_ffedad_1x100.png +0 -0
  61. data/lib/public/css/theme/images/ui-bg_inset-soft_22_3b3b35_1x100.png +0 -0
  62. data/lib/public/css/theme/images/ui-icons_808080_256x240.png +0 -0
  63. data/lib/public/css/theme/images/ui-icons_8DC262_256x240.png +0 -0
  64. data/lib/public/css/theme/images/ui-icons_cd0a0a_256x240.png +0 -0
  65. data/lib/public/css/theme/images/ui-icons_e7e6e4_256x240.png +0 -0
  66. data/lib/public/css/theme/images/ui-icons_eeeeee_256x240.png +0 -0
  67. data/lib/public/css/theme/images/ui-icons_ffffff_256x240.png +0 -0
  68. data/lib/public/css/theme/ui.accordion.css +9 -0
  69. data/lib/public/css/theme/ui.all.css +2 -0
  70. data/lib/public/css/theme/ui.base.css +9 -0
  71. data/lib/public/css/theme/ui.core.css +37 -0
  72. data/lib/public/css/theme/ui.datepicker.css +62 -0
  73. data/lib/public/css/theme/ui.dialog.css +13 -0
  74. data/lib/public/css/theme/ui.progressbar.css +4 -0
  75. data/lib/public/css/theme/ui.resizable.css +13 -0
  76. data/lib/public/css/theme/ui.slider.css +17 -0
  77. data/lib/public/css/theme/ui.tabs.css +9 -0
  78. data/lib/public/css/theme/ui.theme.css +245 -0
  79. data/lib/public/favicon.ico +0 -0
  80. data/lib/public/js/coffee-script.js +8 -0
  81. data/lib/public/js/fg.menu.js +645 -0
  82. data/lib/public/js/jTypeWriter.js +26 -0
  83. data/lib/public/js/jquery-1.4.2.js +6240 -0
  84. data/lib/public/js/jquery-print.js +109 -0
  85. data/lib/public/js/jquery-pubsub.js +27 -0
  86. data/lib/public/js/jquery-terminal.js +2712 -0
  87. data/lib/public/js/jquery.batchImageLoad.js +56 -0
  88. data/lib/public/js/jquery.cycle.all.js +1284 -0
  89. data/lib/public/js/keyboard.js +733 -0
  90. data/lib/public/js/parade-code-execution.js +122 -0
  91. data/lib/public/js/parade-command-input.js +16 -0
  92. data/lib/public/js/parade-command-visor.js +92 -0
  93. data/lib/public/js/parade-keyboard-input.js +54 -0
  94. data/lib/public/js/parade.js +675 -0
  95. data/lib/public/js/spine.js +904 -0
  96. data/lib/templates/config.ru.erb +4 -0
  97. data/lib/templates/showoff.erb +27 -0
  98. data/lib/templates/slides.md.erb +25 -0
  99. data/lib/views/header.erb +73 -0
  100. data/lib/views/index.erb +53 -0
  101. data/lib/views/inline_css.erb +3 -0
  102. data/lib/views/inline_js.erb +3 -0
  103. data/lib/views/onepage.erb +17 -0
  104. data/lib/views/pdf.erb +17 -0
  105. data/lib/views/slide.erb +5 -0
  106. metadata +317 -0
@@ -0,0 +1,35 @@
1
+ require_relative 'presentation_directory_parser'
2
+ require_relative 'presentation_file_parser'
3
+ require_relative 'slides_file_content_parser'
4
+
5
+ module Parade
6
+ module Parsers
7
+
8
+ class PresentationFilepathParser
9
+
10
+ def self.parse(filepath,options = {})
11
+ return nil unless File.exists? filepath
12
+
13
+ if File.directory? filepath
14
+ PresentationDirectoryParser.parse filepath, options
15
+ else
16
+
17
+ if presentation_file?(filepath)
18
+ PresentationFileParser.parse filepath, options
19
+ else
20
+ SlidesFileContentParser.parse filepath, options
21
+ end
22
+
23
+ end
24
+
25
+ end
26
+
27
+
28
+ def self.presentation_file?(filepath)
29
+ File.basename(filepath) == "parade"
30
+ end
31
+
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,27 @@
1
+ require_relative 'markdown_image_paths'
2
+ require_relative 'markdown_slide_splitter'
3
+
4
+ module Parade
5
+ module Parsers
6
+
7
+ module SlidesFileContentParser
8
+ def self.parse(filepath,options = {})
9
+ slides_content = File.read(filepath)
10
+ relative_path = File.dirname(filepath).gsub(options[:root_path].gsub(/\/$/,''),'')
11
+ slides_content = MarkdownImagePaths.parse(slides_content,:path => relative_path)
12
+
13
+ create_section_with slides_content
14
+ end
15
+
16
+ private
17
+
18
+ def self.create_section_with(slides_content)
19
+ section = Section.new
20
+ section.add_slides(MarkdownSlideSplitter.parse(slides_content))
21
+ section
22
+ end
23
+
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,68 @@
1
+ module Parade
2
+ module Renderers
3
+
4
+ #
5
+ # With the given HTML content, search for the CSS class for the HTML element
6
+ # and when found generate columns for each element found. The size of the
7
+ # columns is a division of the number of segments.
8
+ #
9
+ class ColumnsRenderer
10
+
11
+ attr_accessor :css_class
12
+ attr_accessor :html_element
13
+ attr_accessor :segments
14
+
15
+ #
16
+ # @example Creating a ColumnsRenderer
17
+ #
18
+ # Creation of a column renderer that will look for slides with the class
19
+ # 'columns', and create columns out of all h2 elements found, dividing
20
+ # them across 12 elements.
21
+ #
22
+ # ColumnsRenderer.new(:css_class => 'columns',:html_element => "h2",:segments => 12)
23
+ #
24
+ def initialize(params={})
25
+ params.each {|k,v| send("#{k}=",v) if respond_to? "#{k}=" }
26
+ end
27
+
28
+ def render(content)
29
+
30
+ html = Nokogiri::XML.fragment(content)
31
+ parser = CommandlineParser.new
32
+
33
+ html.css(".#{css_class}").each do |slide|
34
+
35
+ columns = []
36
+ slop = []
37
+
38
+ chunks = slide.children.chunk {|child| child.name == html_element }
39
+
40
+ slide.children = ""
41
+
42
+ slide['class'] += " container_#{segments}"
43
+ current_column = slide
44
+
45
+ column_count = chunks.find_all {|is_column,contents| is_column }.count
46
+
47
+ chunks.each do |is_column,contents|
48
+
49
+ if is_column
50
+ slide.add_child current_column unless current_column == slide
51
+ current_column = Nokogiri::XML::Node.new('div',html)
52
+ current_column['class'] = "grid_#{ segments / column_count }"
53
+ end
54
+
55
+ contents.each {|content| current_column.add_child content }
56
+ end
57
+
58
+ slide.add_child current_column
59
+
60
+ end
61
+
62
+ html.to_s
63
+
64
+ end
65
+ end
66
+
67
+ end
68
+ end
@@ -0,0 +1,142 @@
1
+ require 'parslet'
2
+
3
+ module Parade
4
+ module Renderers
5
+
6
+ #
7
+ # Slides that have been marked as 'commandline' will be processed here to
8
+ # ensure that the command portion is played out as if typed. Followed by
9
+ # the result which appears after the command is completed.
10
+ #
11
+ # @example Slide marker to denote a command-line slide
12
+ #
13
+ # !SLIDE commandline incremental
14
+ #
15
+ #
16
+ # To denote the command, it needs to be prefaced with a `$`. The remaining
17
+ # code is considered to be the result.
18
+ #
19
+ # @example Contents of a slide to show a command and a result
20
+ #
21
+ # ```bash
22
+ # $ git commit -am 'incremental bullet points working'
23
+ # [master ac5fd8a] incremental bullet points working
24
+ # 2 files changed, 32 insertions(+), 5 deletions(-)
25
+ # ```
26
+ #
27
+ class CommandLineRenderer
28
+
29
+ #
30
+ # @param [String] html_content the html content of a single slide that
31
+ # will have the commandline rendered correctly if it is a class on
32
+ # the slide.
33
+ # @return [String] the same html content if there is no commandline class
34
+ # or the new rendered html content with the new required HTML elements.
35
+ #
36
+ def self.render(html_content)
37
+
38
+ html = Nokogiri::XML.fragment(html_content)
39
+ parser = CommandlineParser.new
40
+
41
+ html.css('.commandline pre').each do |code|
42
+ out = code.text
43
+ code.content = ''
44
+ tree = parser.parse(out)
45
+ transform = Parslet::Transform.new do
46
+ rule(:prompt => simple(:prompt), :input => simple(:input), :output => simple(:output)) do
47
+ command = Nokogiri::XML::Node.new('pre', html)
48
+ command.set_attribute('class', 'command')
49
+ command.content = "#{prompt} #{input}"
50
+ code << command
51
+
52
+ # Add newline after the input so that users can
53
+ # advance faster than the typewriter effect
54
+ # and still keep inputs on separate lines.
55
+ code << "\n"
56
+
57
+ unless output.to_s.empty?
58
+ result = Nokogiri::XML::Node.new('pre', html)
59
+ result.set_attribute('class', 'result')
60
+ result.content = output
61
+ code << result
62
+ end
63
+
64
+ end
65
+ end
66
+ transform.apply(tree)
67
+ end
68
+
69
+ html.to_s
70
+
71
+ end
72
+
73
+ end
74
+
75
+ # For parsing commandline slide content.
76
+ class CommandlineParser < Parslet::Parser
77
+
78
+ rule(:prompt) do
79
+ str('$') | str('#') | str('>>')
80
+ end
81
+
82
+ rule(:text) do
83
+ match['[:print:]'].repeat
84
+ end
85
+
86
+ rule(:singleline_input) do
87
+ (str("\\\n").absent? >> match['[:print:]']).repeat
88
+ end
89
+
90
+ rule(:input) do
91
+ multiline_input | singleline_input
92
+ end
93
+
94
+ rule(:multiline_input) do
95
+
96
+ # some command \
97
+ # continued \
98
+ # \
99
+ # and stop
100
+ ( singleline_input >> str('\\') >> newline ).repeat(1) >> singleline_input
101
+ end
102
+
103
+ rule(:command) do
104
+
105
+ # $ some command
106
+ # some output
107
+ ( prompt.as(:prompt) >> space? >> input.as(:input) >> output? ).as(:command)
108
+ end
109
+
110
+ rule(:output) do
111
+
112
+ # output
113
+ prompt.absent? >> text
114
+ end
115
+
116
+ rule(:output?) do
117
+
118
+ #
119
+ # some text
120
+ # some text
121
+ #
122
+ # some text
123
+ ( newline >> ( ( output >> newline ).repeat >> output.maybe ).as(:output) ).maybe
124
+ end
125
+
126
+ rule(:commands) do
127
+ command.repeat
128
+ end
129
+
130
+ rule(:newline) do
131
+ str("\n") | str("\r\n")
132
+ end
133
+
134
+ rule(:space?) do
135
+ match['[:space:]'].repeat
136
+ end
137
+
138
+ root(:commands)
139
+ end
140
+
141
+ end
142
+ end
@@ -0,0 +1,42 @@
1
+ require 'redcarpet'
2
+ require 'pygments.rb'
3
+
4
+ module Parade
5
+ module Renderers
6
+ class HTMLwithPygments < Redcarpet::Render::XHTML
7
+
8
+ #
9
+ # When rendering the markdown, the code should be rendered using the
10
+ # Pygments highlight which corresponds to the ghf_marked.css
11
+ #
12
+ # Additionally a class `sh_javascript` or `sh_ruby` is added that will
13
+ # assist in providing a system to provide live interactive elements
14
+ # through the javascript defined in `parade.js`.
15
+ #
16
+ # @param [String] code the fenced code to be highlighted
17
+ # @param [String] language the name of the fenced code
18
+ #
19
+ def block_code(code, language)
20
+ syntax_highlighted_html = Pygments.highlight code, :lexer => language,
21
+ :options => {:encoding => 'utf-8'}
22
+
23
+ syntax_highlighted_html.gsub('class="highlight"',"class=\"highlight sh_#{language}\"")
24
+ end
25
+
26
+ def self.render(content)
27
+ markdown = Redcarpet::Markdown.new(Renderers::HTMLwithPygments,
28
+ :fenced_code_blocks => true,
29
+ :no_intra_emphasis => true,
30
+ :autolink => true,
31
+ :strikethrough => true,
32
+ :lax_html_blocks => true,
33
+ :superscript => true,
34
+ :hard_wrap => true,
35
+ :tables => true,
36
+ :xhtml => true)
37
+ markdown.render(content)
38
+ end
39
+
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,31 @@
1
+
2
+ module Parade
3
+ module Renderers
4
+
5
+ #
6
+ # This Renderer will inline the images into content output. Allowing you
7
+ # to create portable documents.
8
+ #
9
+ module InlineImages
10
+
11
+ def self.render(content,options = {})
12
+
13
+ content.gsub(/img src=["']\/?([^\/].*?)["']/) do |image_source|
14
+ image_name = Regexp.last_match(1)
15
+
16
+ base64_data = image_path_to_base64(image_name)
17
+
18
+ if base64_data
19
+ %{img src="#{base64_data}"}
20
+ else
21
+ image_source
22
+ end
23
+ end
24
+
25
+ end
26
+
27
+ extend Parade::Helpers::EncodeImage
28
+
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,23 @@
1
+ module Parade
2
+ module Renderers
3
+
4
+ #
5
+ # Within the markdown you are able to add additional formatting by starting
6
+ # a new line with a period followed by the class name. This is commonly
7
+ # used for writing presenter notes.
8
+ #
9
+ # @example Adding a presenter note within the markdown
10
+ #
11
+ # ## This Slide has important details
12
+ # * Detail 1
13
+ # * Detail 2
14
+ # .notes Ensure that you talk about detail 1 the most!
15
+ #
16
+ class SpecialParagraphRenderer
17
+ def self.render(html_content)
18
+ html_content.gsub(/<p>\.(.*?) /, '<p class="\1">')
19
+ end
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,75 @@
1
+ module Parade
2
+ module Renderers
3
+
4
+ #
5
+ # UpdateImagePaths is used to ensure the img source paths in the HTML
6
+ # content is properly prefaced with the "image" path as that is necessary
7
+ # for the Sinatra server to properly know that it is to return an image.
8
+ #
9
+ # Additional processing of the image is provided if RMagick has been
10
+ # installed. Namely it sets the size correctly.
11
+ #
12
+ class UpdateImagePaths
13
+
14
+ attr_accessor :root_path
15
+
16
+ def initialize(params = {})
17
+ params.each {|k,v| send("#{k}=",v) if respond_to? "#{k}=" }
18
+ end
19
+
20
+ #
21
+ # @param [String] content HTML content that is parsed for image srcs
22
+ # @param [Hash] options additional parameters, at the moment it is unused.
23
+ #
24
+ def self.render(content,options = {})
25
+ self.new(options).render(content)
26
+ end
27
+
28
+ def render(content,options = {})
29
+ render_root_path = options[:root_path] || root_path || "."
30
+
31
+ content.gsub(/img src=["'](?!https?:\/\/)\/?([^\/].*?)["']/) do |image_source|
32
+ image_name = Regexp.last_match(1)
33
+
34
+ html_image_path = File.join("/","image",image_name)
35
+ updated_image_source = %{img src="#{html_image_path}"}
36
+
37
+ html_asset_path = File.join(render_root_path,image_name)
38
+ width, height = get_image_size(html_asset_path)
39
+ updated_image_source << %( width="#{width}" height="#{height}") if width and height
40
+
41
+ updated_image_source
42
+ end
43
+ end
44
+
45
+ def get_image_size(path) ; end
46
+
47
+ if defined?(Magick)
48
+
49
+ def get_image_size(path)
50
+ unless cached_image_size.key?(path)
51
+
52
+ image = Magick::Image.ping(path).first
53
+
54
+ # # don't set a size for svgs so they can expand to fit their container
55
+ if image.mime_type == 'image/svg+xml'
56
+ cached_image_size[path] = [nil, nil]
57
+ else
58
+ cached_image_size[path] = [image.columns, image.rows]
59
+ end
60
+
61
+ end
62
+ cached_image_size[path]
63
+ end
64
+
65
+ def cached_image_size
66
+ @cached_image_size ||= {}
67
+ end
68
+
69
+
70
+ end
71
+
72
+ end
73
+
74
+ end
75
+ end