parade 0.8.0
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.
- 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,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
|