amber 0.2.6
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +661 -0
- data/README.md +114 -0
- data/bin/amber +71 -0
- data/lib/amber.rb +76 -0
- data/lib/amber/cli.rb +98 -0
- data/lib/amber/logger.rb +21 -0
- data/lib/amber/menu.rb +141 -0
- data/lib/amber/page_array.rb +61 -0
- data/lib/amber/render/asset.rb +55 -0
- data/lib/amber/render/autolink.rb +78 -0
- data/lib/amber/render/bracketlink.rb +33 -0
- data/lib/amber/render/helpers/haml_helper.rb +54 -0
- data/lib/amber/render/helpers/html_helper.rb +75 -0
- data/lib/amber/render/helpers/language_helper.rb +25 -0
- data/lib/amber/render/helpers/navigation_helper.rb +203 -0
- data/lib/amber/render/layout.rb +66 -0
- data/lib/amber/render/table_of_contents.rb +383 -0
- data/lib/amber/render/template.rb +158 -0
- data/lib/amber/render/view.rb +189 -0
- data/lib/amber/server.rb +113 -0
- data/lib/amber/site.rb +154 -0
- data/lib/amber/site_configuration.rb +132 -0
- data/lib/amber/static_page.rb +151 -0
- data/lib/amber/static_page/filesystem.rb +270 -0
- data/lib/amber/static_page/page_properties.rb +124 -0
- data/lib/amber/static_page/property_set.rb +78 -0
- data/lib/amber/static_page/render.rb +122 -0
- metadata +203 -0
@@ -0,0 +1,66 @@
|
|
1
|
+
#
|
2
|
+
# A Layout is similar to a layout in Rails (a template that decorates the pages)
|
3
|
+
#
|
4
|
+
|
5
|
+
gem 'tilt', '>= 2.0.0'
|
6
|
+
require 'tilt'
|
7
|
+
require 'haml'
|
8
|
+
|
9
|
+
#Haml::Options.defaults[:format] = :html5
|
10
|
+
|
11
|
+
module Amber
|
12
|
+
module Render
|
13
|
+
|
14
|
+
class Layout
|
15
|
+
def self.load(layout_dir=nil)
|
16
|
+
@layout_dirs ||= []
|
17
|
+
@layout_dirs << layout_dir if layout_dir
|
18
|
+
reload
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.reload
|
22
|
+
@layouts ||= {}
|
23
|
+
@layouts['default'] = DefaultLayout.new
|
24
|
+
@layout_dirs.each do |dir|
|
25
|
+
Dir.glob("#{dir}/*").each do |layout_file|
|
26
|
+
name = File.basename(layout_file).sub(/^([^\.]*).*$/, "\\1")
|
27
|
+
@layouts[name] = Layout.new(layout_file)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.[](layout)
|
33
|
+
@layouts[layout]
|
34
|
+
end
|
35
|
+
|
36
|
+
def initialize(file_path, &block)
|
37
|
+
if file_path =~ /\.haml$/
|
38
|
+
@template = Tilt::HamlTemplate.new(file_path, {:format => :html5})
|
39
|
+
else
|
40
|
+
@template = Tilt.new(file_path, &block)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def render(view, &block)
|
45
|
+
@template.render(view, &block)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class DefaultLayout < Layout
|
50
|
+
def initialize
|
51
|
+
@template = Tilt::StringTemplate.new {DEFAULT}
|
52
|
+
end
|
53
|
+
DEFAULT = '<!DOCTYPE html>
|
54
|
+
<html>
|
55
|
+
<head>
|
56
|
+
<title>#{ @page.nav_title } - #{@site.title}</title>
|
57
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
58
|
+
</head>
|
59
|
+
<body>
|
60
|
+
#{ yield }
|
61
|
+
</body>
|
62
|
+
</html>'
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,383 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'nokogiri'
|
4
|
+
require 'cgi'
|
5
|
+
|
6
|
+
#
|
7
|
+
# Generates a table of contents for any HTML markup, and adds anchors to headings.
|
8
|
+
#
|
9
|
+
|
10
|
+
module Amber::Render
|
11
|
+
|
12
|
+
##
|
13
|
+
## TABLE OF CONTENTS
|
14
|
+
##
|
15
|
+
|
16
|
+
class TableOfContents
|
17
|
+
#
|
18
|
+
# options:
|
19
|
+
# :content_selector (css selector for headings, nokogiri backend only)
|
20
|
+
# :href_base -- use this href for the toc links
|
21
|
+
# :numeric_prefix -- prefix toc entries and headings with numeric counter (e.g. 1.1.0, 1.2.0, ...)
|
22
|
+
#
|
23
|
+
def initialize(html, options = {})
|
24
|
+
@html = html
|
25
|
+
@toc = TocItem.new
|
26
|
+
@levels = {"h1" => 0, "h2" => 0, "h3" => 0, "h4" => 0}
|
27
|
+
@heading_anchors = {}
|
28
|
+
@options = options
|
29
|
+
@options[:tag] ||= 'ol'
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_html
|
33
|
+
parse_doc unless @parsed
|
34
|
+
# override this!
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_toc
|
38
|
+
parse_doc unless @parsed
|
39
|
+
# override this!
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def parse_doc
|
45
|
+
each_heading(@html) do |heading, heading_text|
|
46
|
+
heading_anchor = anchor_text(heading_text)
|
47
|
+
heading_text = strip_anchors(heading_text)
|
48
|
+
if @options[:numeric_prefix]
|
49
|
+
increment_level(heading)
|
50
|
+
heading_text = level_text + " " + heading_text
|
51
|
+
end
|
52
|
+
@toc.add_heading(heading, heading_text, heading_anchor)
|
53
|
+
'<a name="%s"></a>%s' % [heading_anchor, heading_text]
|
54
|
+
end
|
55
|
+
@parsed = true
|
56
|
+
end
|
57
|
+
|
58
|
+
#
|
59
|
+
# returns anchor text from heading text.
|
60
|
+
# e.g. First Heading! => first-heading
|
61
|
+
#
|
62
|
+
# if there are duplicates, they get numbered:
|
63
|
+
# heading => heading
|
64
|
+
# heading => heading-2
|
65
|
+
# heading => heading-3
|
66
|
+
#
|
67
|
+
def anchor_text(heading_text)
|
68
|
+
text = nameize(strip_html_tags(heading_text))
|
69
|
+
text_with_suffix = text
|
70
|
+
i = 2
|
71
|
+
while @heading_anchors[text_with_suffix]
|
72
|
+
text_with_suffix = "#{text}-#{i}"
|
73
|
+
i+=1
|
74
|
+
end
|
75
|
+
@heading_anchors[text_with_suffix] = true
|
76
|
+
text_with_suffix
|
77
|
+
end
|
78
|
+
|
79
|
+
#
|
80
|
+
# convert any string to one suitable for a url.
|
81
|
+
# resist the urge to translit non-ascii slugs to ascii.
|
82
|
+
# it is always much better to keep strings as utf8.
|
83
|
+
#
|
84
|
+
def nameize(str)
|
85
|
+
str = str.dup
|
86
|
+
str.gsub!(/&(\w{2,6}?|#[0-9A-Fa-f]{2,6});/,'') # remove html entitities
|
87
|
+
str.gsub!(/[^- [[:word:]]]/u, '') # remove non-word characters (using unicode definition of a word char)
|
88
|
+
str.strip!
|
89
|
+
str.downcase! # upper case characters in urls are confusing
|
90
|
+
str.gsub!(/\ +/u, '-') # spaces to dashes, preferred separator char everywhere
|
91
|
+
CGI.escape(str)
|
92
|
+
end
|
93
|
+
|
94
|
+
# removes all html markup
|
95
|
+
def strip_html_tags(html)
|
96
|
+
Nokogiri::HTML::DocumentFragment.parse(html, 'UTF-8').children.collect{|child| child.inner_text}.join
|
97
|
+
end
|
98
|
+
|
99
|
+
# remove <a name='x'></a> from html, but leaves all other tags in place.
|
100
|
+
def strip_anchors(html)
|
101
|
+
Nokogiri::HTML::DocumentFragment.parse(html, 'UTF-8').children.collect{|child|
|
102
|
+
if child.name == "text"
|
103
|
+
child.inner_text
|
104
|
+
elsif child.name != 'a' || !child.attributes.detect{|atr| atr[0] == 'name'}
|
105
|
+
child.to_s
|
106
|
+
end
|
107
|
+
}.join
|
108
|
+
end
|
109
|
+
|
110
|
+
#
|
111
|
+
# prefix headings with text like 1.2.1, if :numeric_prefix => true
|
112
|
+
#
|
113
|
+
def level_text
|
114
|
+
[@levels["h1"], @levels["h2"], @levels["h3"], @levels["h4"]].join(".").gsub(/\.0/, "")
|
115
|
+
end
|
116
|
+
|
117
|
+
#
|
118
|
+
# keeps a counter of the latest heading at each level
|
119
|
+
#
|
120
|
+
def increment_level(heading)
|
121
|
+
@levels[heading] += 1
|
122
|
+
@levels["h2"] = 0 if heading == "h1"
|
123
|
+
@levels["h3"] = 0 if heading == "h1" || heading == "h2"
|
124
|
+
@levels["h4"] = 0 if heading == "h1" || heading == "h2" || heading == "h3"
|
125
|
+
end
|
126
|
+
|
127
|
+
def each_heading(html, &block)
|
128
|
+
raise 'override me'
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
##
|
133
|
+
## NOKOGIRI TOC
|
134
|
+
##
|
135
|
+
|
136
|
+
class NokogiriTableOfContents < TableOfContents
|
137
|
+
def to_html
|
138
|
+
super
|
139
|
+
@nokogiri_doc.to_html.gsub(/(<h\d.*?>)\n/, '\1').gsub(/\n(<\/h\d.*?>)/, '\1')
|
140
|
+
end
|
141
|
+
|
142
|
+
def to_toc
|
143
|
+
super
|
144
|
+
ul = Nokogiri::XML::Node.new(@options[:tag], Nokogiri::HTML.fragment(""))
|
145
|
+
@toc.populate_node(ul, @options)
|
146
|
+
ul.to_pretty_html
|
147
|
+
end
|
148
|
+
|
149
|
+
private
|
150
|
+
|
151
|
+
def each_heading(html, &block)
|
152
|
+
@nokogiri_doc = Nokogiri::HTML.fragment(html, "UTF-8")
|
153
|
+
if @options[:content_selector]
|
154
|
+
selector = @levels.keys.map {|h| "#{@options[:content_selector]} #{h}" }.join(",")
|
155
|
+
else
|
156
|
+
selector = @levels.keys.join(",")
|
157
|
+
end
|
158
|
+
@nokogiri_doc.css(selector).each do |node|
|
159
|
+
node.inner_html = yield(node.name, node.inner_html)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
##
|
165
|
+
## REGEX TOC
|
166
|
+
##
|
167
|
+
|
168
|
+
class RegexTableOfContents < TableOfContents
|
169
|
+
def to_html
|
170
|
+
super
|
171
|
+
@new_html
|
172
|
+
end
|
173
|
+
|
174
|
+
def to_toc
|
175
|
+
super
|
176
|
+
@toc.to_html(@options)
|
177
|
+
end
|
178
|
+
|
179
|
+
private
|
180
|
+
|
181
|
+
HEADING_EX = %r{
|
182
|
+
<\s*((h\d).*?)\s*> # match starting <h1>
|
183
|
+
(.+)? # match innner text
|
184
|
+
<\s*\/\2\s*> # match closing </h1>
|
185
|
+
}x
|
186
|
+
|
187
|
+
def each_heading(html, &block)
|
188
|
+
@new_html = html.gsub(HEADING_EX) do |match|
|
189
|
+
"<%s>%s</%s>" % [$1, yield($2, $3), $2]
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
##
|
195
|
+
## TOC ITEM
|
196
|
+
##
|
197
|
+
## A tree of TocItems composes the table of contents outline.
|
198
|
+
##
|
199
|
+
|
200
|
+
class TocItem
|
201
|
+
attr_reader :children, :level, :text, :anchor
|
202
|
+
|
203
|
+
def initialize(heading='h0', text=nil, anchor=nil)
|
204
|
+
@level = heading[1].to_i if heading.is_a?(String)
|
205
|
+
@text = text
|
206
|
+
@anchor = anchor
|
207
|
+
@children = []
|
208
|
+
end
|
209
|
+
|
210
|
+
def add_heading(heading, heading_text, heading_anchor)
|
211
|
+
self.parent_for(heading).children << TocItem.new(heading, heading_text, heading_anchor)
|
212
|
+
end
|
213
|
+
|
214
|
+
#
|
215
|
+
# generates nokogiri html node tree from this toc
|
216
|
+
#
|
217
|
+
def populate_node(node, options)
|
218
|
+
@children.each do |item|
|
219
|
+
li = node.document.create_element("li")
|
220
|
+
li.add_child(li.document.create_element("a", item.text, :href => "#{options[:href_base]}##{item.anchor}"))
|
221
|
+
if item.children.any?
|
222
|
+
ul = li.document.create_element(options[:tag])
|
223
|
+
item.populate_node(ul, options)
|
224
|
+
li.add_child(ul)
|
225
|
+
end
|
226
|
+
node.add_child(li)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
#
|
231
|
+
# generates html string from this toc
|
232
|
+
#
|
233
|
+
def to_html(options={})
|
234
|
+
html = []
|
235
|
+
tag = options[:tag]
|
236
|
+
indent = options[:indent] || 0
|
237
|
+
str = options[:indent_str] || " "
|
238
|
+
html << '%s<%s>' % [(str*indent), tag]
|
239
|
+
@children.each do |item|
|
240
|
+
html << '%s<li>' % (str*(indent+1))
|
241
|
+
html << '%s<a href="%s#%s">%s</a>' % [str*(indent+2), options[:href_base], item.anchor, item.text]
|
242
|
+
if item.children.any?
|
243
|
+
html << item.to_html({
|
244
|
+
:indent => indent+2,
|
245
|
+
:indent_str => str,
|
246
|
+
:tag => tag,
|
247
|
+
:href_base => options[:href_base]
|
248
|
+
})
|
249
|
+
end
|
250
|
+
html << '%s</li>' % (str*(indent+1))
|
251
|
+
end
|
252
|
+
html << '%s</%s>' % [(str*indent), tag]
|
253
|
+
html.join("\n")
|
254
|
+
end
|
255
|
+
|
256
|
+
#
|
257
|
+
# Returns the appropriate TocItem for appending a new item
|
258
|
+
# at a particular heading level.
|
259
|
+
#
|
260
|
+
def parent_for(heading)
|
261
|
+
heading = heading[1].to_i if heading.is_a?(String)
|
262
|
+
if children.any? && children.last.level < heading
|
263
|
+
children.last.parent_for(heading)
|
264
|
+
else
|
265
|
+
self
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
end
|
271
|
+
|
272
|
+
class Nokogiri::XML::Node
|
273
|
+
def to_pretty_html(indent=0)
|
274
|
+
indent_str = " " * indent
|
275
|
+
children_html = []
|
276
|
+
text_html = nil
|
277
|
+
if children.size == 1 && children.first.name == "text"
|
278
|
+
text_html = children.first.content
|
279
|
+
else
|
280
|
+
children.each do |child|
|
281
|
+
if child.name == "text"
|
282
|
+
children_html << "#{" " * (indent+1)}#{child.content}" if !child.content.empty?
|
283
|
+
else
|
284
|
+
children_html << child.to_pretty_html(indent+1)
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|
288
|
+
attrs = []
|
289
|
+
attributes.each do |attribute|
|
290
|
+
attrs << %(#{attribute[0]}="#{attribute[1]}")
|
291
|
+
end
|
292
|
+
if attrs.any?
|
293
|
+
attr_html = " " + attrs.join(' ')
|
294
|
+
else
|
295
|
+
attr_html = ""
|
296
|
+
end
|
297
|
+
html = []
|
298
|
+
if text_html
|
299
|
+
html << "#{indent_str}<#{name}#{attr_html}>#{text_html}</#{name}>"
|
300
|
+
elsif children_html.any?
|
301
|
+
html << "#{indent_str}<#{name}#{attr_html}>"
|
302
|
+
html += children_html
|
303
|
+
html << "#{indent_str}</#{name}>"
|
304
|
+
else
|
305
|
+
html << "#{indent_str}<#{name}#{attr_html}></#{name}>"
|
306
|
+
end
|
307
|
+
html.join("\n")
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
|
312
|
+
=begin
|
313
|
+
|
314
|
+
AN ATTEMPT TO GET NOKOGIRI TO OUTPUT REASONABLE HTML5. NO LUCK.
|
315
|
+
|
316
|
+
#
|
317
|
+
# convert a Nokogiri::HTML::Document into well formatted html.
|
318
|
+
# unfortunately, Nokogiri formatting only works on complete documents, so we strip away the <html> tags. :(
|
319
|
+
#
|
320
|
+
def format_doc(doc)
|
321
|
+
INDENT_XSLT.apply_to(doc).to_s.sub("<!DOCTYPE html>\n<html><body>", '').sub('</body></html>', '')
|
322
|
+
end
|
323
|
+
|
324
|
+
# from https://github.com/jarijokinen/html5-beautifier/blob/master/lib/html5-beautifier/xslt/html5-beautifier.xslt
|
325
|
+
# MIT License
|
326
|
+
INDENT_XSLT_STRING = <<EOF
|
327
|
+
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
|
328
|
+
<xsl:output method="html" omit-xml-declaration="yes" encoding="utf-8" />
|
329
|
+
<xsl:param name="indent-increment" select="'__INDENT_STRING__'" />
|
330
|
+
|
331
|
+
<xsl:template name="newline">
|
332
|
+
<xsl:text disable-output-escaping="yes">
|
333
|
+
</xsl:text>
|
334
|
+
</xsl:template>
|
335
|
+
|
336
|
+
<xsl:template match="/">
|
337
|
+
<xsl:text disable-output-escaping="yes"><!DOCTYPE html></xsl:text>
|
338
|
+
<xsl:apply-templates />
|
339
|
+
</xsl:template>
|
340
|
+
|
341
|
+
<xsl:template match="comment() | processing-instruction()">
|
342
|
+
<xsl:param name="indent" select="''" />
|
343
|
+
<xsl:call-template name="newline" />
|
344
|
+
<xsl:value-of select="$indent" />
|
345
|
+
<xsl:copy />
|
346
|
+
</xsl:template>
|
347
|
+
|
348
|
+
<xsl:template match="text()">
|
349
|
+
<xsl:param name="indent" select="''" />
|
350
|
+
<xsl:call-template name="newline" />
|
351
|
+
<xsl:value-of select="$indent" />
|
352
|
+
<xsl:value-of select="normalize-space(.)" />
|
353
|
+
</xsl:template>
|
354
|
+
|
355
|
+
<xsl:template match="text()[normalize-space(.)='']" />
|
356
|
+
|
357
|
+
<xsl:template match="*">
|
358
|
+
<xsl:param name="indent" select="''" />
|
359
|
+
<xsl:call-template name="newline" />
|
360
|
+
<xsl:value-of select="$indent" />
|
361
|
+
<xsl:choose>
|
362
|
+
<xsl:when test="count(child::*) > 0 and __EXCLUDE_ELEMENTS__">
|
363
|
+
<xsl:copy>
|
364
|
+
<xsl:copy-of select="@*" />
|
365
|
+
<xsl:apply-templates select="*|text()">
|
366
|
+
<xsl:with-param name="indent" select="concat($indent, $indent-increment)" />
|
367
|
+
</xsl:apply-templates>
|
368
|
+
<xsl:call-template name="newline" />
|
369
|
+
<xsl:value-of select="$indent" />
|
370
|
+
</xsl:copy>
|
371
|
+
</xsl:when>
|
372
|
+
<xsl:otherwise>
|
373
|
+
<xsl:copy-of select="." />
|
374
|
+
</xsl:otherwise>
|
375
|
+
</xsl:choose>
|
376
|
+
</xsl:template>
|
377
|
+
</xsl:stylesheet>
|
378
|
+
EOF
|
379
|
+
|
380
|
+
INDENT_XSLT = Nokogiri::XSLT(INDENT_XSLT_STRING.gsub('__INDENT_STRING__', ' '))
|
381
|
+
|
382
|
+
|
383
|
+
=end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
require 'haml'
|
2
|
+
require 'tilt'
|
3
|
+
require 'RedCloth'
|
4
|
+
require 'rdiscount'
|
5
|
+
|
6
|
+
module Amber
|
7
|
+
module Render
|
8
|
+
class Template
|
9
|
+
|
10
|
+
PROPERTY_HEADER = /^\s*(^(|- )@\w[^\n]*?\n)*/m
|
11
|
+
|
12
|
+
RENDER_MAP = {
|
13
|
+
:text => 'render_textile',
|
14
|
+
:textile => 'render_textile',
|
15
|
+
:md => 'render_markdown',
|
16
|
+
:markdown => 'render_markdown',
|
17
|
+
:html => 'render_raw'
|
18
|
+
}
|
19
|
+
|
20
|
+
TEXTILE_TOC_RE = /^\s*h([1-6])\.\s+(.*)/
|
21
|
+
|
22
|
+
ERB_TAG_RE = /<%=.*?%>/
|
23
|
+
ERB_PLACEHOLDER_RE = /xx erb tag\d+ xx/
|
24
|
+
|
25
|
+
attr_reader :file
|
26
|
+
attr_reader :type
|
27
|
+
attr_reader :content
|
28
|
+
attr_reader :partial
|
29
|
+
|
30
|
+
def initialize(options={})
|
31
|
+
if options[:file]
|
32
|
+
@file = options[:file]
|
33
|
+
@type = type_from_file(@file)
|
34
|
+
elsif options[:content]
|
35
|
+
@content = options[:content]
|
36
|
+
@type = options[:type] # e.g. :haml. required if @content
|
37
|
+
end
|
38
|
+
@partial = options[:partial]
|
39
|
+
end
|
40
|
+
|
41
|
+
#
|
42
|
+
# returns rendered content or title, depending on render_mode
|
43
|
+
#
|
44
|
+
def render(view, options={})
|
45
|
+
view.locals[:_type] = @type
|
46
|
+
render_mode = options.delete(:mode) || :content
|
47
|
+
toc = options.delete(:toc)
|
48
|
+
|
49
|
+
if render_mode == :title
|
50
|
+
render_title(view)
|
51
|
+
else
|
52
|
+
html = render_html(view)
|
53
|
+
if render_mode == :toc
|
54
|
+
RegexTableOfContents.new(html, options).to_toc
|
55
|
+
elsif toc || render_mode == :toc_and_content
|
56
|
+
toc = RegexTableOfContents.new(html, options)
|
57
|
+
%(<div id="TOC">%s</div>\n\n%s) % [toc.to_toc, toc.to_html]
|
58
|
+
else
|
59
|
+
html
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def render_html(view)
|
67
|
+
if @type == :haml
|
68
|
+
return render_haml(@file, view)
|
69
|
+
else
|
70
|
+
@content ||= File.read(@file, :encoding => 'UTF-8').sub(PROPERTY_HEADER, '') # remove property header
|
71
|
+
if method = RENDER_MAP[@type]
|
72
|
+
content, erb_tags = replace_erb_tags(@content)
|
73
|
+
html = self.send(method, view, content)
|
74
|
+
return render_erb(restore_erb_tags(html, erb_tags), view)
|
75
|
+
else
|
76
|
+
return "sorry, i don't understand how to render `#{@type}`"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def render_erb(string, view)
|
82
|
+
template = Tilt::ERBTemplate.new {string}
|
83
|
+
template.render(view)
|
84
|
+
end
|
85
|
+
|
86
|
+
#
|
87
|
+
# takes raw markup, and replaces every <%= x %> with a
|
88
|
+
# markup-safe placeholder. erb_tags holds a map of placeholder
|
89
|
+
# to original erb. e.g. {"ERBTAG0" => "<%= 'hi]]"}
|
90
|
+
#
|
91
|
+
def replace_erb_tags(content)
|
92
|
+
counter = 0
|
93
|
+
erb_tags = {}
|
94
|
+
new_content = content.gsub(ERB_TAG_RE) do |match|
|
95
|
+
placeholder = "xx erb tag#{counter} xx"
|
96
|
+
erb_tags[placeholder] = match
|
97
|
+
counter+=1
|
98
|
+
placeholder
|
99
|
+
end
|
100
|
+
return [new_content, erb_tags]
|
101
|
+
end
|
102
|
+
|
103
|
+
#
|
104
|
+
# replaces erb placeholders with actual erb
|
105
|
+
#
|
106
|
+
def restore_erb_tags(html, erb_tags)
|
107
|
+
html.gsub(ERB_PLACEHOLDER_RE) do |match|
|
108
|
+
erb_tags[match]
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def render_title(view)
|
113
|
+
locale = view.locals[:locale]
|
114
|
+
if title = view.page.explicit_title(locale)
|
115
|
+
"<h1>#{title}</h1>\n"
|
116
|
+
else
|
117
|
+
""
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def render_haml(file_path, view)
|
122
|
+
template = Tilt::HamlTemplate.new(file_path, {:format => :html5, :default_encoding => 'UTF-8'})
|
123
|
+
add_bracket_links(view, template.render(view))
|
124
|
+
end
|
125
|
+
|
126
|
+
def render_textile(view, content)
|
127
|
+
content = add_bracket_links(view, content)
|
128
|
+
Autolink.auto_link(RedCloth.new(content).to_html)
|
129
|
+
end
|
130
|
+
|
131
|
+
def render_markdown(view, content)
|
132
|
+
content = add_bracket_links(view, content)
|
133
|
+
RDiscount.new(content, :smart, :autolink).to_html
|
134
|
+
end
|
135
|
+
|
136
|
+
def render_raw(view, content)
|
137
|
+
add_bracket_links(view, content)
|
138
|
+
end
|
139
|
+
|
140
|
+
def add_bracket_links(view, content)
|
141
|
+
content = Bracketlink.bracket_link(content) do |from, to|
|
142
|
+
view.link({from => to})
|
143
|
+
end
|
144
|
+
content
|
145
|
+
end
|
146
|
+
|
147
|
+
def type_from_file(file_path)
|
148
|
+
suffix = File.extname(file_path)
|
149
|
+
if suffix
|
150
|
+
suffix.sub! /^\./, ''
|
151
|
+
suffix = suffix.to_sym
|
152
|
+
end
|
153
|
+
suffix
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|