kramdown-rfc2629 0.12.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +59 -0
- data/bin/kramdown-rfc2629 +16 -0
- data/kramdown-rfc2629.gemspec +18 -0
- data/lib/kramdown-rfc2629.rb +420 -0
- metadata +84 -0
data/README.md
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# kramdown-rfc2629
|
2
|
+
|
3
|
+
[kramdown][] is a [markdown][] parser by Thomas Leitner, which has a
|
4
|
+
number of backends for generating HTML, Latex, and markdown again.
|
5
|
+
|
6
|
+
**kramdown-rfc2629** is an additional backend to that: It allows the
|
7
|
+
generation of [RFC 2629][] compliant XML markup (also known as XML2RFC
|
8
|
+
markup).
|
9
|
+
|
10
|
+
Who would care? Anybody who is writing Internet-Drafts and RFCs in
|
11
|
+
the [IETF][] and prefers (or has co-authors who prefer) to do part of
|
12
|
+
their work in markdown.
|
13
|
+
|
14
|
+
# Usage
|
15
|
+
|
16
|
+
Start by installing the kramdown-rfc2629 gem (this requires kramdown
|
17
|
+
version 0.12.0, but has been tested with version 0.11.0 and 0.12.0):
|
18
|
+
|
19
|
+
sudo gem install kramdown-rfc2629
|
20
|
+
|
21
|
+
The guts of kramdown-rfc2629 are in one Ruby file,
|
22
|
+
`lib/kramdown-rfc2629.rb` --- this melds nicely into the extension
|
23
|
+
structure provided by kramdown. `bin/kramdown-rfc2629` is a simple
|
24
|
+
command-line program showing how to use this. For this, you'll need a
|
25
|
+
Ruby 1.9 that can be found under the name "ruby1.9", and maybe XML2RFC
|
26
|
+
if you want to see the fruits of your work.
|
27
|
+
|
28
|
+
kramdown-rfc2629 mydraft.mkd >mydraft.xml
|
29
|
+
xml2rfc mydraft.xml
|
30
|
+
|
31
|
+
# Examples
|
32
|
+
|
33
|
+
`stupid.mkd` is a markdown version of an actual Internet-Draft (for a
|
34
|
+
protocol called [STuPiD][] \[sic!]). This demonstrates some, but not
|
35
|
+
all features of kramdown-rfc2629. Since markdown/kramdown does not
|
36
|
+
cater for all the structure of an RFC 2629 style document, some of the
|
37
|
+
markup is in XML, and the example switches between XML and markdown
|
38
|
+
using kramdown's `{::nomarkdown}` and `{:/nomarkdown}` (this is ugly,
|
39
|
+
but works well enough).
|
40
|
+
|
41
|
+
# Risks and Side-Effects
|
42
|
+
|
43
|
+
The code is not very polished (in particular, the code for tables is a
|
44
|
+
joke), and you probably still need to understand [RFC 2629][] if you
|
45
|
+
want to write an Internet-Draft.
|
46
|
+
|
47
|
+
# License
|
48
|
+
|
49
|
+
(kramdown itself appears to be licensed GPLv3.) As kramdown-rfc2629
|
50
|
+
is in part derived from kramdown source, there is little choice: It is
|
51
|
+
also licensed under GPLv3, which you can google yourself. (Being
|
52
|
+
stuck at GPLv3 does not make me happy, but it is just for this tool so
|
53
|
+
it's probably not going to kill any RFC author.)
|
54
|
+
|
55
|
+
[kramdown]: http://kramdown.rubyforge.org/
|
56
|
+
[stupid]: http://tools.ietf.org/id/draft-hartke-xmpp-stupid-00
|
57
|
+
[RFC 2629]: http://xml.resource.org
|
58
|
+
[markdown]: http://en.wikipedia.org/wiki/Markdown
|
59
|
+
[IETF]: http://www.ietf.org
|
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby1.9
|
2
|
+
require 'kramdown-rfc2629'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
Encoding.default_external = "UTF-8" # wake up, smell the coffee
|
6
|
+
|
7
|
+
options = {input: 'RFC2629Kramdown'}
|
8
|
+
input = ARGF.read.gsub(/\{::include\s+(.*?)\}/) {
|
9
|
+
File.read($1).chomp
|
10
|
+
}
|
11
|
+
if input =~ /\A<\?xml/ # if this is a whole XML file, protect it
|
12
|
+
input = "{::nomarkdown}\n#{input}\n{:/nomarkdown}\n"
|
13
|
+
end
|
14
|
+
doc = Kramdown::Document.new(input, options)
|
15
|
+
$stderr.puts doc.warnings.to_yaml unless doc.warnings.empty?
|
16
|
+
puts doc.to_rfc2629
|
@@ -0,0 +1,18 @@
|
|
1
|
+
spec = Gem::Specification.new do |s|
|
2
|
+
s.name = 'kramdown-rfc2629'
|
3
|
+
s.version = '0.12.1'
|
4
|
+
s.summary = "Kramdown extension for generating RFC 2629 XML."
|
5
|
+
s.description = %{An RFC2629 (XML2RFC) generating backend for Thomas Leitner's
|
6
|
+
"kramdown" markdown parser. Mostly useful for RFC writers.}
|
7
|
+
s.add_dependency('kramdown', '~> 0.12')
|
8
|
+
s.files = Dir['lib/**/*.rb'] + %w(README.md kramdown-rfc2629.gemspec bin/kramdown-rfc2629)
|
9
|
+
s.require_path = 'lib'
|
10
|
+
s.executables = ['kramdown-rfc2629']
|
11
|
+
s.default_executable = 'kramdown-rfc2629'
|
12
|
+
s.required_ruby_version = '>= 1.9.2'
|
13
|
+
s.requirements = 'wget'
|
14
|
+
# s.has_rdoc = true
|
15
|
+
s.author = "Carsten Bormann"
|
16
|
+
s.email = "cabo@tzi.org"
|
17
|
+
s.homepage = "http://github.com/cabo/kramdown-rfc2629"
|
18
|
+
end
|
@@ -0,0 +1,420 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
#
|
3
|
+
#--
|
4
|
+
# Copyright (C) 2010 Carsten Bormann <cabo@tzi.org>
|
5
|
+
#
|
6
|
+
# This file is designed to work with kramdown.
|
7
|
+
# License: GPLv3, unfortunately (look it up).
|
8
|
+
# Any code that I haven't stolen from kramdown is also licensed under
|
9
|
+
# the 2-clause BSD license (look it up).
|
10
|
+
#++
|
11
|
+
#
|
12
|
+
|
13
|
+
raise "sorry, 1.8 was last decade" unless RUBY_VERSION >= '1.9'
|
14
|
+
|
15
|
+
# this version is adapted to kramdown 0.12.0
|
16
|
+
gem 'kramdown', '= 0.12.0'
|
17
|
+
require 'kramdown'
|
18
|
+
|
19
|
+
require 'rexml/parsers/baseparser'
|
20
|
+
|
21
|
+
class Object
|
22
|
+
def deep_clone
|
23
|
+
Marshal.load(Marshal.dump(self))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
module Kramdown
|
28
|
+
|
29
|
+
module Parser
|
30
|
+
class RFC2629Kramdown < Kramdown
|
31
|
+
|
32
|
+
def initialize(*doc)
|
33
|
+
super
|
34
|
+
@span_parsers.unshift(:xref)
|
35
|
+
end
|
36
|
+
|
37
|
+
XREF_START = /\{\{(.*?)\}\}/u
|
38
|
+
|
39
|
+
# Introduce new {{target}} syntax for empty xrefs, which would
|
40
|
+
# otherwise be an ugly ![!](target) or ![ ](target)
|
41
|
+
# (I'd rather use [[target]], but that somehow clashes with links.)
|
42
|
+
def parse_xref
|
43
|
+
@src.pos += @src.matched_size
|
44
|
+
href = @src[1]
|
45
|
+
el = Element.new(:xref, nil, {'target' => href})
|
46
|
+
@tree.children << el
|
47
|
+
end
|
48
|
+
define_parser(:xref, XREF_START, '{{')
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class Element
|
54
|
+
def rfc2629_fix
|
55
|
+
if a = attr
|
56
|
+
if anchor = a.delete('id')
|
57
|
+
a['anchor'] = anchor
|
58
|
+
end
|
59
|
+
if anchor = a.delete('href')
|
60
|
+
a['target'] = anchor
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
module Converter
|
67
|
+
|
68
|
+
# Converts a Kramdown::Document to HTML.
|
69
|
+
class Rfc2629 < Base
|
70
|
+
|
71
|
+
# we use these to do XML stuff, too (XXX: 0.11 vs. 0.12)
|
72
|
+
include ::Kramdown::Utils::HTML rescue include ::Kramdown::Utils::Html
|
73
|
+
|
74
|
+
def el_html_attributes(el)
|
75
|
+
html_attributes(el) rescue html_attributes(el.attr) # XXX 0.11 vs. 0.12
|
76
|
+
end
|
77
|
+
|
78
|
+
# :stopdoc:
|
79
|
+
|
80
|
+
# Defines the amount of indentation used when nesting XML tags.
|
81
|
+
INDENTATION = 2
|
82
|
+
|
83
|
+
# Initialize the XML converter with the given Kramdown document +doc+.
|
84
|
+
def initialize(*doc)
|
85
|
+
super
|
86
|
+
@sec_level = 1
|
87
|
+
@in_dt = 0
|
88
|
+
end
|
89
|
+
|
90
|
+
def convert(el, indent = -INDENTATION, opts = {})
|
91
|
+
if el.children[-1].type == :raw
|
92
|
+
raw = convert1(el.children.pop, indent, opts)
|
93
|
+
end
|
94
|
+
"#{convert1(el, indent, opts)}#{end_sections(1, indent)}#{raw}"
|
95
|
+
end
|
96
|
+
|
97
|
+
def convert1(el, indent, opts = {})
|
98
|
+
el.rfc2629_fix
|
99
|
+
send("convert_#{el.type}", el, indent, opts)
|
100
|
+
end
|
101
|
+
|
102
|
+
def inner_a(el, indent, opts)
|
103
|
+
indent += INDENTATION
|
104
|
+
el.children.map do |inner_el|
|
105
|
+
inner_el.rfc2629_fix
|
106
|
+
send("convert_#{inner_el.type}", inner_el, indent, opts)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def inner(el, indent, opts)
|
111
|
+
inner_a(el, indent, opts).join('')
|
112
|
+
end
|
113
|
+
|
114
|
+
def convert_blank(el, indent, opts)
|
115
|
+
"\n"
|
116
|
+
end
|
117
|
+
|
118
|
+
def convert_text(el, indent, opts)
|
119
|
+
escape_html(el.value, :text)
|
120
|
+
end
|
121
|
+
|
122
|
+
def convert_p(el, indent, opts)
|
123
|
+
if (el.children.size == 1 && el.children[0].type == :img) || opts[:unpacked]
|
124
|
+
inner(el, indent, opts) # Part of the bad reference hack
|
125
|
+
else
|
126
|
+
"#{' '*indent}<t#{el_html_attributes(el)}>#{inner(el, indent, opts)}</t>\n"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def saner_generate_id(value)
|
131
|
+
generate_id(value).gsub(/-+/, '-')
|
132
|
+
end
|
133
|
+
|
134
|
+
def convert_codeblock(el, indent, opts)
|
135
|
+
el.attr['anchor'] ||= saner_generate_id(el.value)
|
136
|
+
result = el.value
|
137
|
+
# compensate for XML2RFC idiosyncracy by insisting on a blank line
|
138
|
+
unless el.attr.delete('tight')
|
139
|
+
result[0,0] = "\n" unless result[0,1] == "\n"
|
140
|
+
end
|
141
|
+
"#{' '*indent}<figure#{el_html_attributes(el)}><artwork><![CDATA[#{result}#{result =~ /\n\Z/ ? '' : "\n"}]]></artwork></figure>\n"
|
142
|
+
end
|
143
|
+
|
144
|
+
def convert_blockquote(el, indent, opts)
|
145
|
+
"#{' '*indent}<t><list style='empty'#{el_html_attributes(el)}>\n#{inner(el, indent, opts)}#{' '*indent}</list></t>\n"
|
146
|
+
end
|
147
|
+
|
148
|
+
def end_sections(to_level, indent)
|
149
|
+
if indent < 0
|
150
|
+
indent = 0
|
151
|
+
end
|
152
|
+
if @sec_level >= to_level
|
153
|
+
delta = (@sec_level - to_level)
|
154
|
+
@sec_level = to_level
|
155
|
+
"#{' '*indent}</section>\n" * delta
|
156
|
+
else
|
157
|
+
$stderr.puts "Incorrect section nesting: Need to start with 1"
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def convert_header(el, indent, opts)
|
162
|
+
# todo: handle appendix tags
|
163
|
+
el = el.deep_clone
|
164
|
+
options = @doc ? @doc.options : @options # XXX: 0.11 vs. 0.12
|
165
|
+
if options[:auto_ids] && !el.attr['anchor']
|
166
|
+
el.attr['anchor'] = saner_generate_id(el.options[:raw_text])
|
167
|
+
end
|
168
|
+
el.attr['title'] = inner(el, indent, opts)
|
169
|
+
"#{end_sections(el.options[:level], indent)}#{' '*indent}<section#{@sec_level += 1; el_html_attributes(el)}>\n"
|
170
|
+
end
|
171
|
+
|
172
|
+
def convert_hr(el, indent, opts) # misuse for page break
|
173
|
+
"#{' '*indent}<t><vspace blankLines='999' /></t>\n"
|
174
|
+
end
|
175
|
+
|
176
|
+
STYLES = {ul: 'symbols', ol: 'numbers', dl: 'hanging'}
|
177
|
+
|
178
|
+
def convert_ul(el, indent, opts)
|
179
|
+
style = STYLES[el.type]
|
180
|
+
"#{' '*indent}<t><list style='#{style}'#{el_html_attributes(el)}>\n#{inner(el, indent, opts)}#{' '*indent}</list></t>\n"
|
181
|
+
end
|
182
|
+
alias :convert_ol :convert_ul
|
183
|
+
alias :convert_dl :convert_ul
|
184
|
+
|
185
|
+
def convert_li(el, indent, opts)
|
186
|
+
res_a = inner_a(el, indent, opts)
|
187
|
+
if el.children.empty? || el.children.first.options[:category] == :span
|
188
|
+
res = res_a.join('')
|
189
|
+
else # merge multiple <t> elements
|
190
|
+
res = res_a.select { |x|
|
191
|
+
x.strip != ''
|
192
|
+
}.map { |x|
|
193
|
+
x.sub(/\A\s*<t>(.*)<\/t>\s*\Z/m) { $1}
|
194
|
+
}.join("#{' '*indent}<vspace blankLines='1'/>\n").gsub(%r{(</list>)\s*<vspace blankLines='1'/>}) { $1 }.gsub(%r{<vspace blankLines='1'/>\s*(<list)}) { $1 }
|
195
|
+
end
|
196
|
+
"#{' '*indent}<t#{el_html_attributes(el)}>#{res}#{(res =~ /\n\Z/ ? ' '*indent : '')}</t>\n"
|
197
|
+
end
|
198
|
+
def convert_dd(el, indent, opts)
|
199
|
+
output = ' '*indent
|
200
|
+
if @in_dt == 1
|
201
|
+
@in_dt = 0
|
202
|
+
else
|
203
|
+
output << "<t#{el_html_attributes(el)}>"
|
204
|
+
end
|
205
|
+
res = inner(el, indent+INDENTATION, opts.merge(unpacked: true))
|
206
|
+
# if el.children.empty? || el.children.first.options[:category] != :block
|
207
|
+
output << res << (res =~ /\n\Z/ ? ' '*indent : '')
|
208
|
+
# else FIXME: The latter case is needed for more complex cases
|
209
|
+
# output << "\n" << res << ' '*indent
|
210
|
+
# end
|
211
|
+
output << "</t>\n"
|
212
|
+
end
|
213
|
+
|
214
|
+
def convert_dt(el, indent, opts) # SERIOUSLY BAD HACK:
|
215
|
+
close = "#{' '*indent}</t>\n" * @in_dt
|
216
|
+
@in_dt = 1
|
217
|
+
"#{close}#{' '*indent}<t#{el_html_attributes(el)} hangText='#{inner(el, indent, opts)}'>\n"
|
218
|
+
end
|
219
|
+
|
220
|
+
HTML_TAGS_WITH_BODY=['div', 'script']
|
221
|
+
|
222
|
+
def convert_html_element(el, indent, opts)
|
223
|
+
res = inner(el, indent, opts)
|
224
|
+
if el.options[:category] == :span
|
225
|
+
"<#{el.value}#{el_html_attributes(el)}" << (!res.empty? ? ">#{res}</#{el.value}>" : " />")
|
226
|
+
else
|
227
|
+
output = ''
|
228
|
+
output << ' '*indent if !el.options[:parent_is_raw]
|
229
|
+
output << "<#{el.value}#{el_html_attributes(el)}"
|
230
|
+
if !res.empty? && el.options[:parse_type] != :block
|
231
|
+
output << ">#{res}</#{el.value}>"
|
232
|
+
elsif !res.empty?
|
233
|
+
output << ">\n#{res}" << ' '*indent << "</#{el.value}>"
|
234
|
+
elsif HTML_TAGS_WITH_BODY.include?(el.value)
|
235
|
+
output << "></#{el.value}>"
|
236
|
+
else
|
237
|
+
output << " />"
|
238
|
+
end
|
239
|
+
output << "\n" if el.options[:outer_element] || !el.options[:parent_is_raw]
|
240
|
+
output
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
def convert_xml_comment(el, indent, opts)
|
245
|
+
if el.options[:category] == :block && !el.options[:parent_is_raw]
|
246
|
+
' '*indent + el.value + "\n"
|
247
|
+
else
|
248
|
+
el.value
|
249
|
+
end
|
250
|
+
end
|
251
|
+
alias :convert_xml_pi :convert_xml_comment
|
252
|
+
alias :convert_html_doctype :convert_xml_comment
|
253
|
+
|
254
|
+
ALIGNMENTS = { default: :left, left: :left, right: :right, center: :center}
|
255
|
+
|
256
|
+
def convert_table(el, indent, opts) # This only works for tables with headers
|
257
|
+
alignment = el.options[:alignment].map { |al| ALIGNMENTS[al]}
|
258
|
+
"#{' '*indent}<texttable#{el_html_attributes(el)}>\n#{inner(el, indent, opts.merge(table_alignment: alignment))}#{' '*indent}</texttable>\n"
|
259
|
+
end
|
260
|
+
|
261
|
+
def convert_thead(el, indent, opts)
|
262
|
+
inner(el, indent, opts)
|
263
|
+
end
|
264
|
+
alias :convert_tbody :convert_thead
|
265
|
+
alias :convert_tfoot :convert_thead
|
266
|
+
alias :convert_tr :convert_thead
|
267
|
+
|
268
|
+
def convert_td(el, indent, opts)
|
269
|
+
if alignment = opts[:table_alignment]
|
270
|
+
alignment = alignment.shift
|
271
|
+
end
|
272
|
+
res = inner(el, indent, opts)
|
273
|
+
if alignment
|
274
|
+
"#{' '*indent}<ttcol align='#{alignment}'#{el_html_attributes(el)}>#{res.empty? ? " " : res}</ttcol>\n"
|
275
|
+
else
|
276
|
+
"#{' '*indent}<c#{el_html_attributes(el)}>#{res.empty? ? " " : res}</c>\n"
|
277
|
+
end
|
278
|
+
end
|
279
|
+
alias :convert_th :convert_td
|
280
|
+
|
281
|
+
def convert_comment(el, indent, opts)
|
282
|
+
## Don't actually output all those comments into the XML:
|
283
|
+
# if el.options[:category] == :block
|
284
|
+
# "#{' '*indent}<!-- #{el.value} -->\n"
|
285
|
+
# else
|
286
|
+
# "<!-- #{el.value} -->"
|
287
|
+
# end
|
288
|
+
end
|
289
|
+
|
290
|
+
def convert_br(el, indent, opts)
|
291
|
+
"<br />"
|
292
|
+
end
|
293
|
+
|
294
|
+
def convert_a(el, indent, opts)
|
295
|
+
do_obfuscation = el.attr['href'] =~ /^mailto:/
|
296
|
+
if do_obfuscation
|
297
|
+
el = el.deep_clone
|
298
|
+
href = obfuscate(el.attr['href'].sub(/^mailto:/, ''))
|
299
|
+
mailto = obfuscate('mailto')
|
300
|
+
el.attr['href'] = "#{mailto}:#{href}"
|
301
|
+
end
|
302
|
+
res = inner(el, indent, opts)
|
303
|
+
res = obfuscate(res) if do_obfuscation
|
304
|
+
"<eref#{el_html_attributes(el)}>#{res}</eref>"
|
305
|
+
end
|
306
|
+
|
307
|
+
def convert_xref(el, indent, opts)
|
308
|
+
"<xref#{el_html_attributes(el)}/>"
|
309
|
+
end
|
310
|
+
|
311
|
+
REFCACHEDIR = ".refcache"
|
312
|
+
def get_and_cache_resource(url, tn = Time.now, tvalid = 7200)
|
313
|
+
Dir.mkdir(REFCACHEDIR) unless Dir.exists?(REFCACHEDIR)
|
314
|
+
fn = "#{REFCACHEDIR}/#{File.basename(url)}"
|
315
|
+
f = File.stat(fn) rescue nil
|
316
|
+
if !f || tn - f.ctime >= tvalid
|
317
|
+
$stderr.puts "#{fn}: #{f && tn-f.ctime}"
|
318
|
+
`cd #{REFCACHEDIR}; wget -Nnv "#{url}"` # ignore errors if offline (hack)
|
319
|
+
File.utime nil, nil, fn
|
320
|
+
end
|
321
|
+
File.read(fn)
|
322
|
+
end
|
323
|
+
|
324
|
+
def convert_img(el, indent, opts) # misuse the tag!
|
325
|
+
if a = el.attr
|
326
|
+
alt = a.delete('alt').strip
|
327
|
+
alt = '' if alt == '!' # work around re-wrap uglyness
|
328
|
+
if anchor = a.delete('src')
|
329
|
+
a['target'] = anchor
|
330
|
+
end
|
331
|
+
end
|
332
|
+
if alt == ":include:" # Really bad misuse of tag...
|
333
|
+
to_insert = ""
|
334
|
+
anchor.scan(/([A-Z-]+)[.]?([a-z0-9-]+)/) do |t, n|
|
335
|
+
fn = "reference.#{t}.#{n}.xml"
|
336
|
+
sub = { "RFC" => "bibxml", "I-D" => "bibxml3" }[t]
|
337
|
+
puts "Huh: ${fn}" unless sub
|
338
|
+
url = "http://xml.resource.org/public/rfc/#{sub}/#{fn}"
|
339
|
+
to_insert = get_and_cache_resource(url)
|
340
|
+
end
|
341
|
+
to_insert.gsub(/<\?xml version='1.0' encoding='UTF-8'\?>/, '')
|
342
|
+
else
|
343
|
+
"<xref#{el_html_attributes(el)}>#{alt}</xref>"
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
def convert_codespan(el, indent, opts)
|
348
|
+
"<spanx style='verb'#{el_html_attributes(el)}>#{escape_html(el.value)}</spanx>"
|
349
|
+
end
|
350
|
+
|
351
|
+
def convert_footnote(el, indent, opts) # XXX: This is wrong.
|
352
|
+
"<xref target='#{escape_html(el.value)}'#{el_html_attributes(el)}/>"
|
353
|
+
end
|
354
|
+
|
355
|
+
def convert_raw(el, indent, opts)
|
356
|
+
end_sections(1, indent) +
|
357
|
+
el.value + (el.options[:category] == :block ? "\n" : '')
|
358
|
+
end
|
359
|
+
|
360
|
+
EMPH = { em: "emph", strong: "strong"}
|
361
|
+
|
362
|
+
def convert_em(el, indent, opts)
|
363
|
+
"<spanx style='#{EMPH[el.type]}'#{el_html_attributes(el)}>#{inner(el, indent, opts)}</spanx>"
|
364
|
+
end
|
365
|
+
alias :convert_strong :convert_em
|
366
|
+
|
367
|
+
def convert_entity(el, indent, opts)
|
368
|
+
entity_to_str(el.value)
|
369
|
+
end
|
370
|
+
|
371
|
+
TYPOGRAPHIC_SYMS = {
|
372
|
+
:mdash => [::Kramdown::Utils::Entities.entity('mdash')],
|
373
|
+
:ndash => [::Kramdown::Utils::Entities.entity('ndash')],
|
374
|
+
:hellip => [::Kramdown::Utils::Entities.entity('hellip')],
|
375
|
+
:laquo_space => [::Kramdown::Utils::Entities.entity('laquo'), ::Kramdown::Utils::Entities.entity('nbsp')],
|
376
|
+
:raquo_space => [::Kramdown::Utils::Entities.entity('nbsp'), ::Kramdown::Utils::Entities.entity('raquo')],
|
377
|
+
:laquo => [::Kramdown::Utils::Entities.entity('laquo')],
|
378
|
+
:raquo => [::Kramdown::Utils::Entities.entity('raquo')]
|
379
|
+
}
|
380
|
+
def convert_typographic_sym(el, indent, opts)
|
381
|
+
TYPOGRAPHIC_SYMS[el.value].map {|e| entity_to_str(e)}.join('')
|
382
|
+
end
|
383
|
+
|
384
|
+
def convert_smart_quote(el, indent, opts)
|
385
|
+
entity_to_str(::Kramdown::Utils::Entities.entity(el.value.to_s))
|
386
|
+
end
|
387
|
+
|
388
|
+
def convert_math(el, indent, opts) # XXX: This is wrong
|
389
|
+
el = el.deep_clone
|
390
|
+
el.attr['class'] ||= ''
|
391
|
+
el.attr['class'] += (el.attr['class'].empty? ? '' : ' ') + 'math'
|
392
|
+
type = 'span'
|
393
|
+
type = 'div' if el.options[:category] == :block
|
394
|
+
"<#{type}#{el_html_attributes(el)}>#{escape_html(el.value, :text)}</#{type}>#{type == 'div' ? "\n" : ''}"
|
395
|
+
end
|
396
|
+
|
397
|
+
def convert_abbreviation(el, indent, opts) # XXX: This is wrong
|
398
|
+
title = @doc.parse_infos[:abbrev_defs][el.value]
|
399
|
+
title = nil if title.empty?
|
400
|
+
"<abbr#{title ? " title=\"#{title}\"" : ''}>#{el.value}</abbr>"
|
401
|
+
end
|
402
|
+
|
403
|
+
def convert_root(el, indent, opts)
|
404
|
+
result = inner(el, indent, opts)
|
405
|
+
end
|
406
|
+
|
407
|
+
# Helper method for obfuscating the +text+ by using XML entities.
|
408
|
+
def obfuscate(text)
|
409
|
+
result = ""
|
410
|
+
text.each_byte do |b|
|
411
|
+
result += (b > 128 ? b.chr : "&#%03d;" % b)
|
412
|
+
end
|
413
|
+
result.force_encoding(text.encoding) if RUBY_VERSION >= '1.9'
|
414
|
+
result
|
415
|
+
end
|
416
|
+
|
417
|
+
end
|
418
|
+
|
419
|
+
end
|
420
|
+
end
|
metadata
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: kramdown-rfc2629
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 12
|
8
|
+
- 1
|
9
|
+
version: 0.12.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Carsten Bormann
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2011-01-04 00:00:00 +01:00
|
18
|
+
default_executable: kramdown-rfc2629
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: kramdown
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ~>
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
- 12
|
31
|
+
version: "0.12"
|
32
|
+
type: :runtime
|
33
|
+
version_requirements: *id001
|
34
|
+
description: |-
|
35
|
+
An RFC2629 (XML2RFC) generating backend for Thomas Leitner's
|
36
|
+
"kramdown" markdown parser. Mostly useful for RFC writers.
|
37
|
+
email: cabo@tzi.org
|
38
|
+
executables:
|
39
|
+
- kramdown-rfc2629
|
40
|
+
extensions: []
|
41
|
+
|
42
|
+
extra_rdoc_files: []
|
43
|
+
|
44
|
+
files:
|
45
|
+
- lib/kramdown-rfc2629.rb
|
46
|
+
- README.md
|
47
|
+
- kramdown-rfc2629.gemspec
|
48
|
+
- bin/kramdown-rfc2629
|
49
|
+
has_rdoc: true
|
50
|
+
homepage: http://github.com/cabo/kramdown-rfc2629
|
51
|
+
licenses: []
|
52
|
+
|
53
|
+
post_install_message:
|
54
|
+
rdoc_options: []
|
55
|
+
|
56
|
+
require_paths:
|
57
|
+
- lib
|
58
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
59
|
+
none: false
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
segments:
|
64
|
+
- 1
|
65
|
+
- 9
|
66
|
+
- 2
|
67
|
+
version: 1.9.2
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
none: false
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
segments:
|
74
|
+
- 0
|
75
|
+
version: "0"
|
76
|
+
requirements:
|
77
|
+
- wget
|
78
|
+
rubyforge_project:
|
79
|
+
rubygems_version: 1.3.7
|
80
|
+
signing_key:
|
81
|
+
specification_version: 3
|
82
|
+
summary: Kramdown extension for generating RFC 2629 XML.
|
83
|
+
test_files: []
|
84
|
+
|