motion-kramdown 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +84 -0
  3. data/lib/kramdown/compatibility.rb +36 -0
  4. data/lib/kramdown/converter/base.rb +259 -0
  5. data/lib/kramdown/converter/html.rb +461 -0
  6. data/lib/kramdown/converter/kramdown.rb +423 -0
  7. data/lib/kramdown/converter/latex.rb +600 -0
  8. data/lib/kramdown/converter/math_engine/itex2mml.rb +39 -0
  9. data/lib/kramdown/converter/math_engine/mathjax.rb +33 -0
  10. data/lib/kramdown/converter/math_engine/ritex.rb +38 -0
  11. data/lib/kramdown/converter/pdf.rb +624 -0
  12. data/lib/kramdown/converter/remove_html_tags.rb +53 -0
  13. data/lib/kramdown/converter/syntax_highlighter/coderay.rb +78 -0
  14. data/lib/kramdown/converter/syntax_highlighter/rouge.rb +37 -0
  15. data/lib/kramdown/converter/toc.rb +69 -0
  16. data/lib/kramdown/converter.rb +69 -0
  17. data/lib/kramdown/document.rb +144 -0
  18. data/lib/kramdown/element.rb +515 -0
  19. data/lib/kramdown/error.rb +17 -0
  20. data/lib/kramdown/options.rb +584 -0
  21. data/lib/kramdown/parser/base.rb +130 -0
  22. data/lib/kramdown/parser/gfm.rb +55 -0
  23. data/lib/kramdown/parser/html.rb +575 -0
  24. data/lib/kramdown/parser/kramdown/abbreviation.rb +67 -0
  25. data/lib/kramdown/parser/kramdown/autolink.rb +37 -0
  26. data/lib/kramdown/parser/kramdown/blank_line.rb +30 -0
  27. data/lib/kramdown/parser/kramdown/block_boundary.rb +33 -0
  28. data/lib/kramdown/parser/kramdown/blockquote.rb +39 -0
  29. data/lib/kramdown/parser/kramdown/codeblock.rb +56 -0
  30. data/lib/kramdown/parser/kramdown/codespan.rb +44 -0
  31. data/lib/kramdown/parser/kramdown/emphasis.rb +61 -0
  32. data/lib/kramdown/parser/kramdown/eob.rb +26 -0
  33. data/lib/kramdown/parser/kramdown/escaped_chars.rb +25 -0
  34. data/lib/kramdown/parser/kramdown/extensions.rb +201 -0
  35. data/lib/kramdown/parser/kramdown/footnote.rb +56 -0
  36. data/lib/kramdown/parser/kramdown/header.rb +59 -0
  37. data/lib/kramdown/parser/kramdown/horizontal_rule.rb +27 -0
  38. data/lib/kramdown/parser/kramdown/html.rb +160 -0
  39. data/lib/kramdown/parser/kramdown/html_entity.rb +33 -0
  40. data/lib/kramdown/parser/kramdown/line_break.rb +25 -0
  41. data/lib/kramdown/parser/kramdown/link.rb +139 -0
  42. data/lib/kramdown/parser/kramdown/list.rb +256 -0
  43. data/lib/kramdown/parser/kramdown/math.rb +54 -0
  44. data/lib/kramdown/parser/kramdown/paragraph.rb +54 -0
  45. data/lib/kramdown/parser/kramdown/smart_quotes.rb +174 -0
  46. data/lib/kramdown/parser/kramdown/table.rb +171 -0
  47. data/lib/kramdown/parser/kramdown/typographic_symbol.rb +44 -0
  48. data/lib/kramdown/parser/kramdown.rb +359 -0
  49. data/lib/kramdown/parser/markdown.rb +56 -0
  50. data/lib/kramdown/parser.rb +27 -0
  51. data/lib/kramdown/utils/configurable.rb +44 -0
  52. data/lib/kramdown/utils/entities.rb +347 -0
  53. data/lib/kramdown/utils/html.rb +75 -0
  54. data/lib/kramdown/utils/ordered_hash.rb +87 -0
  55. data/lib/kramdown/utils/string_scanner.rb +74 -0
  56. data/lib/kramdown/utils/unidecoder.rb +51 -0
  57. data/lib/kramdown/utils.rb +58 -0
  58. data/lib/kramdown/version.rb +15 -0
  59. data/lib/kramdown.rb +10 -0
  60. data/lib/motion-kramdown.rb +47 -0
  61. data/lib/rubymotion/encodings.rb +37 -0
  62. data/lib/rubymotion/rexml_shim.rb +25 -0
  63. data/lib/rubymotion/set.rb +1349 -0
  64. data/lib/rubymotion/version.rb +6 -0
  65. data/spec/document_tree.rb +48 -0
  66. data/spec/gfm_to_html.rb +95 -0
  67. data/spec/helpers/it_behaves_like.rb +27 -0
  68. data/spec/helpers/option_file.rb +46 -0
  69. data/spec/helpers/spec_options.rb +37 -0
  70. data/spec/helpers/tidy.rb +12 -0
  71. data/spec/html_to_html.rb +40 -0
  72. data/spec/html_to_kramdown_to_html.rb +46 -0
  73. data/spec/kramdown_to_xxx.rb +40 -0
  74. data/spec/test_location.rb +203 -0
  75. data/spec/test_string_scanner_kramdown.rb +19 -0
  76. data/spec/text_to_kramdown_to_html.rb +52 -0
  77. data/spec/text_to_latex.rb +33 -0
  78. metadata +164 -0
@@ -0,0 +1,423 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ #--
4
+ # Copyright (C) 2009-2014 Thomas Leitner <t_leitner@gmx.at>
5
+ #
6
+ # This file is part of kramdown which is licensed under the MIT.
7
+ #++
8
+ #
9
+
10
+ # RM require 'rexml/parsers/baseparser'
11
+
12
+ module Kramdown
13
+
14
+ module Converter
15
+
16
+ # Converts an element tree to the kramdown format.
17
+ class Kramdown < Base
18
+
19
+ # :stopdoc:
20
+
21
+ include ::Kramdown::Utils::Html
22
+
23
+ def initialize(root, options)
24
+ super
25
+ @linkrefs = []
26
+ @footnotes = []
27
+ @abbrevs = []
28
+ @stack = []
29
+ end
30
+
31
+ def convert(el, opts = {:indent => 0})
32
+ res = send("convert_#{el.type}", el, opts)
33
+ if ![:html_element, :li, :dd, :td].include?(el.type) && (ial = ial_for_element(el))
34
+ res << ial
35
+ res << "\n\n" if Element.category(el) == :block
36
+ elsif [:ul, :dl, :ol, :codeblock].include?(el.type) && opts[:next] &&
37
+ ([el.type, :codeblock].include?(opts[:next].type) ||
38
+ (opts[:next].type == :blank && opts[:nnext] && [el.type, :codeblock].include?(opts[:nnext].type)))
39
+ res << "^\n\n"
40
+ elsif Element.category(el) == :block &&
41
+ ![:li, :dd, :dt, :td, :th, :tr, :thead, :tbody, :tfoot, :blank].include?(el.type) &&
42
+ (el.type != :html_element || @stack.last.type != :html_element) &&
43
+ (el.type != :p || !el.options[:transparent])
44
+ res << "\n"
45
+ end
46
+ res
47
+ end
48
+
49
+ def inner(el, opts = {:indent => 0})
50
+ @stack.push(el)
51
+ result = ''
52
+ el.children.each_with_index do |inner_el, index|
53
+ options = opts.dup
54
+ options[:index] = index
55
+ options[:prev] = (index == 0 ? nil : el.children[index-1])
56
+ options[:pprev] = (index <= 1 ? nil : el.children[index-2])
57
+ options[:next] = (index == el.children.length - 1 ? nil : el.children[index+1])
58
+ options[:nnext] = (index >= el.children.length - 2 ? nil : el.children[index+2])
59
+ result << convert(inner_el, options)
60
+ end
61
+ @stack.pop
62
+ result
63
+ end
64
+
65
+ def convert_blank(el, opts)
66
+ ""
67
+ end
68
+
69
+ ESCAPED_CHAR_RE = /(\$\$|[\\*_`\[\]\{"'|])|^[ ]{0,3}(:)/
70
+
71
+ def convert_text(el, opts)
72
+ if opts[:raw_text]
73
+ el.value
74
+ else
75
+ el.value.gsub(/\A\n/) do
76
+ opts[:prev] && opts[:prev].type == :br ? '' : "\n"
77
+ end.gsub(/\s+/, ' ').gsub(ESCAPED_CHAR_RE) { "\\#{$1 || $2}" }
78
+ end
79
+ end
80
+
81
+ def convert_p(el, opts)
82
+ w = @options[:line_width] - opts[:indent].to_s.to_i
83
+ first, second, *rest = inner(el, opts).strip.gsub(/(.{1,#{w}})( +|$\n?)/, "\\1\n").split(/\n/)
84
+ first.gsub!(/^(?:(#|>)|(\d+)\.|([+-]\s))/) { $1 || $3 ? "\\#{$1 || $3}" : "#{$2}\\."} if first
85
+ second.gsub!(/^([=-]+\s*?)$/, "\\\1") if second
86
+ res = [first, second, *rest].compact.join("\n") + "\n"
87
+ if el.children.length == 1 && el.children.first.type == :math
88
+ res = "\\#{res}"
89
+ elsif res.start_with?('\$$') && res.end_with?("\\$$\n")
90
+ res.sub!(/^\\\$\$/, '\$\$')
91
+ end
92
+ res
93
+ end
94
+
95
+
96
+ def convert_codeblock(el, opts)
97
+ el.value.split(/\n/).map {|l| l.empty? ? " " : " #{l}"}.join("\n") + "\n"
98
+ end
99
+
100
+ def convert_blockquote(el, opts)
101
+ opts[:indent] += 2
102
+ inner(el, opts).chomp.split(/\n/).map {|l| "> #{l}"}.join("\n") << "\n"
103
+ end
104
+
105
+ def convert_header(el, opts)
106
+ res = ''
107
+ res << "#{'#' * output_header_level(el.options[:level])} #{inner(el, opts)}"
108
+ res[-1, 1] = "\\#" if res[-1] == ?#
109
+ res << " {##{el.attr['id']}}" if el.attr['id'] && !el.attr['id'].strip.empty?
110
+ res << "\n"
111
+ end
112
+
113
+ def convert_hr(el, opts)
114
+ "* * *\n"
115
+ end
116
+
117
+ def convert_ul(el, opts)
118
+ inner(el, opts).sub(/\n+\Z/, "\n")
119
+ end
120
+ alias :convert_ol :convert_ul
121
+ alias :convert_dl :convert_ul
122
+
123
+ def convert_li(el, opts)
124
+ sym, width = if @stack.last.type == :ul
125
+ ['* ', el.children.first && el.children.first.type == :codeblock ? 4 : 2]
126
+ else
127
+ ["#{opts[:index] + 1}.".ljust(4), 4]
128
+ end
129
+ if ial = ial_for_element(el)
130
+ sym << ial << " "
131
+ end
132
+
133
+ opts[:indent] += width
134
+ text = inner(el, opts)
135
+ newlines = text.scan(/\n*\Z/).first
136
+ first, *last = text.split(/\n/)
137
+ last = last.map {|l| " "*width + l}.join("\n")
138
+ text = (first.nil? ? "\n" : first + (last.empty? ? "" : "\n") + last + newlines)
139
+ if el.children.first && el.children.first.type == :p && !el.children.first.options[:transparent]
140
+ res = "#{sym}#{text}"
141
+ res << "^\n" if el.children.size == 1 && @stack.last.children.last == el &&
142
+ (@stack.last.children.any? {|c| c.children.first.type != :p} || @stack.last.children.size == 1)
143
+ res
144
+ elsif el.children.first && el.children.first.type == :codeblock
145
+ "#{sym}\n #{text}"
146
+ else
147
+ "#{sym}#{text}"
148
+ end
149
+ end
150
+
151
+ def convert_dd(el, opts)
152
+ sym, width = ": ", (el.children.first && el.children.first.type == :codeblock ? 4 : 2)
153
+ if ial = ial_for_element(el)
154
+ sym << ial << " "
155
+ end
156
+
157
+ opts[:indent] += width
158
+ text = inner(el, opts)
159
+ newlines = text.scan(/\n*\Z/).first
160
+ first, *last = text.split(/\n/)
161
+ last = last.map {|l| " "*width + l}.join("\n")
162
+ text = first.to_s + (last.empty? ? "" : "\n") + last + newlines
163
+ text.chomp! if text =~ /\n\n\Z/ && opts[:next] && opts[:next].type == :dd
164
+ text << "\n" if (text !~ /\n\n\Z/ && opts[:next] && opts[:next].type == :dt)
165
+ text << "\n" if el.children.empty?
166
+ if el.children.first && el.children.first.type == :p && !el.children.first.options[:transparent]
167
+ "\n#{sym}#{text}"
168
+ elsif el.children.first && el.children.first.type == :codeblock
169
+ "#{sym}\n #{text}"
170
+ else
171
+ "#{sym}#{text}"
172
+ end
173
+ end
174
+
175
+ def convert_dt(el, opts)
176
+ inner(el, opts) << "\n"
177
+ end
178
+
179
+ HTML_TAGS_WITH_BODY=['div', 'script', 'iframe', 'textarea']
180
+
181
+ def convert_html_element(el, opts)
182
+ markdown_attr = el.options[:category] == :block && el.children.any? do |c|
183
+ c.type != :html_element && (c.type != :p || !c.options[:transparent]) && Element.category(c) == :block
184
+ end
185
+ opts[:force_raw_text] = true if %w{script pre code}.include?(el.value)
186
+ opts[:raw_text] = opts[:force_raw_text] || opts[:block_raw_text] || (el.options[:category] != :span && !markdown_attr)
187
+ opts[:block_raw_text] = true if el.options[:category] == :block && opts[:raw_text]
188
+ res = inner(el, opts)
189
+ if el.options[:category] == :span
190
+ "<#{el.value}#{html_attributes(el.attr)}" << (!res.empty? || HTML_TAGS_WITH_BODY.include?(el.value) ? ">#{res}</#{el.value}>" : " />")
191
+ else
192
+ output = ''
193
+ attr = el.attr.dup
194
+ attr['markdown'] = '1' if markdown_attr
195
+ output << "<#{el.value}#{html_attributes(attr)}"
196
+ if !res.empty? && el.options[:content_model] != :block
197
+ output << ">#{res}</#{el.value}>"
198
+ elsif !res.empty?
199
+ output << ">\n#{res}" << "</#{el.value}>"
200
+ elsif HTML_TAGS_WITH_BODY.include?(el.value)
201
+ output << "></#{el.value}>"
202
+ else
203
+ output << " />"
204
+ end
205
+ output << "\n" if @stack.last.type != :html_element || @stack.last.options[:content_model] != :raw
206
+ output
207
+ end
208
+ end
209
+
210
+ def convert_xml_comment(el, opts)
211
+ if el.options[:category] == :block && (@stack.last.type != :html_element || @stack.last.options[:content_model] != :raw)
212
+ el.value + "\n"
213
+ else
214
+ el.value.dup
215
+ end
216
+ end
217
+ alias :convert_xml_pi :convert_xml_comment
218
+
219
+ def convert_table(el, opts)
220
+ opts[:alignment] = el.options[:alignment]
221
+ inner(el, opts)
222
+ end
223
+
224
+ def convert_thead(el, opts)
225
+ rows = inner(el, opts)
226
+ if opts[:alignment].all? {|a| a == :default}
227
+ "#{rows}|" << "-"*10 << "\n"
228
+ else
229
+ "#{rows}| " << opts[:alignment].map do |a|
230
+ case a
231
+ when :left then ":-"
232
+ when :right then "-:"
233
+ when :center then ":-:"
234
+ when :default then "-"
235
+ end
236
+ end.join(' ') << "\n"
237
+ end
238
+ end
239
+
240
+ def convert_tbody(el, opts)
241
+ res = ''
242
+ res << inner(el, opts)
243
+ res << '|' << '-'*10 << "\n" if opts[:next] && opts[:next].type == :tbody
244
+ res
245
+ end
246
+
247
+ def convert_tfoot(el, opts)
248
+ "|" << "="*10 << "\n#{inner(el, opts)}"
249
+ end
250
+
251
+ def convert_tr(el, opts)
252
+ "| " << el.children.map {|c| convert(c, opts)}.join(" | ") << " |\n"
253
+ end
254
+
255
+ def convert_td(el, opts)
256
+ inner(el, opts)
257
+ end
258
+
259
+ def convert_comment(el, opts)
260
+ if el.options[:category] == :block
261
+ "{::comment}\n#{el.value}\n{:/}\n"
262
+ else
263
+ "{::comment}#{el.value}{:/}"
264
+ end
265
+ end
266
+
267
+ def convert_br(el, opts)
268
+ " \n"
269
+ end
270
+
271
+ def convert_a(el, opts)
272
+ if el.attr['href'].empty?
273
+ "[#{inner(el, opts)}]()"
274
+ elsif el.attr['href'] =~ /^(?:http|ftp)/ || el.attr['href'].count("()") > 0
275
+ index = if link_el = @linkrefs.find {|c| c.attr['href'] == el.attr['href']}
276
+ @linkrefs.index(link_el) + 1
277
+ else
278
+ @linkrefs << el
279
+ @linkrefs.size
280
+ end
281
+ "[#{inner(el, opts)}][#{index}]"
282
+ else
283
+ title = el.attr['title'].to_s.empty? ? '' : ' "' + el.attr['title'].gsub(/"/, "&quot;") + '"'
284
+ "[#{inner(el, opts)}](#{el.attr['href']}#{title})"
285
+ end
286
+ end
287
+
288
+ def convert_img(el, opts)
289
+ alt_text = el.attr['alt'].gsub(ESCAPED_CHAR_RE) { $1 ? "\\#{$1}" : $2 }
290
+ if el.attr['src'].empty?
291
+ "![#{alt_text}]()"
292
+ else
293
+ title = (el.attr['title'] ? ' "' + el.attr['title'].gsub(/"/, "&quot;") + '"' : '')
294
+ link = if el.attr['src'].count("()") > 0
295
+ "<#{el.attr['src']}>"
296
+ else
297
+ el.attr['src']
298
+ end
299
+ "![#{alt_text}](#{link}#{title})"
300
+ end
301
+ end
302
+
303
+ def convert_codespan(el, opts)
304
+ delim = (el.value.scan(/`+/).max || '') + '`'
305
+ "#{delim}#{' ' if delim.size > 1}#{el.value}#{' ' if delim.size > 1}#{delim}"
306
+ end
307
+
308
+ def convert_footnote(el, opts)
309
+ @footnotes << [el.options[:name], el.value]
310
+ "[^#{el.options[:name]}]"
311
+ end
312
+
313
+ def convert_raw(el, opts)
314
+ attr = (el.options[:type] || []).join(' ')
315
+ attr = " type=\"#{attr}\"" if attr.length > 0
316
+ if @stack.last.type == :html_element
317
+ el.value
318
+ elsif el.options[:category] == :block
319
+ "{::nomarkdown#{attr}}\n#{el.value}\n{:/}\n"
320
+ else
321
+ "{::nomarkdown#{attr}}#{el.value}{:/}"
322
+ end
323
+ end
324
+
325
+ def convert_em(el, opts)
326
+ "*#{inner(el, opts)}*" +
327
+ (opts[:next] && [:em, :strong].include?(opts[:next].type) && !ial_for_element(el) ? '{::}' : '')
328
+ end
329
+
330
+ def convert_strong(el, opts)
331
+ "**#{inner(el, opts)}**" +
332
+ (opts[:next] && [:em, :strong].include?(opts[:next].type) && !ial_for_element(el) ? '{::}' : '')
333
+ end
334
+
335
+ def convert_entity(el, opts)
336
+ entity_to_str(el.value, el.options[:original])
337
+ end
338
+
339
+ TYPOGRAPHIC_SYMS = {
340
+ :mdash => '---', :ndash => '--', :hellip => '...',
341
+ :laquo_space => '<< ', :raquo_space => ' >>',
342
+ :laquo => '<<', :raquo => '>>'
343
+ }
344
+ def convert_typographic_sym(el, opts)
345
+ TYPOGRAPHIC_SYMS[el.value]
346
+ end
347
+
348
+ def convert_smart_quote(el, opts)
349
+ el.value.to_s =~ /[rl]dquo/ ? "\"" : "'"
350
+ end
351
+
352
+ def convert_math(el, opts)
353
+ "$$#{el.value}$$" + (el.options[:category] == :block ? "\n" : '')
354
+ end
355
+
356
+ def convert_abbreviation(el, opts)
357
+ el.value
358
+ end
359
+
360
+ def convert_root(el, opts)
361
+ res = inner(el, opts)
362
+ res << create_link_defs
363
+ res << create_footnote_defs
364
+ res << create_abbrev_defs
365
+ res
366
+ end
367
+
368
+ def create_link_defs
369
+ res = ''
370
+ res << "\n\n" if @linkrefs.size > 0
371
+ @linkrefs.each_with_index do |el, i|
372
+ title = el.attr['title']
373
+ res << "[#{i+1}]: #{el.attr['href']}#{title ? ' "' + title.gsub(/"/, "&quot;") + '"' : ''}\n"
374
+ end
375
+ res
376
+ end
377
+
378
+ def create_footnote_defs
379
+ res = ''
380
+ @footnotes.each do |name, data|
381
+ res << "[^#{name}]:\n"
382
+ res << inner(data).chomp.split(/\n/).map {|l| " #{l}"}.join("\n") + "\n\n"
383
+ end
384
+ res
385
+ end
386
+
387
+ def create_abbrev_defs
388
+ return '' unless @root.options[:abbrev_defs]
389
+ res = ''
390
+ @root.options[:abbrev_defs].each do |name, text|
391
+ res << "*[#{name}]: #{text}\n"
392
+ end
393
+ res
394
+ end
395
+
396
+ # Return the IAL containing the attributes of the element +el+.
397
+ def ial_for_element(el)
398
+ res = el.attr.map do |k,v|
399
+ next if [:img, :a].include?(el.type) && ['href', 'src', 'alt', 'title'].include?(k)
400
+ next if el.type == :header && k == 'id' && !v.strip.empty?
401
+ if v.nil?
402
+ ''
403
+ elsif k == 'class' && !v.empty?
404
+ " " + v.split(/\s+/).map {|w| ".#{w}"}.join(" ")
405
+ elsif k == 'id' && !v.strip.empty?
406
+ " ##{v}"
407
+ else
408
+ " #{k}=\"#{v.to_s}\""
409
+ end
410
+ end.compact.join('')
411
+ res = "toc" << (res.strip.empty? ? '' : " #{res}") if (el.type == :ul || el.type == :ol) &&
412
+ (el.options[:ial] && (el.options[:ial][:refs] || []).include?('toc')) # RM can't use rescue nil
413
+ res = "footnotes" << (res.strip.empty? ? '' : " #{res}") if (el.type == :ul || el.type == :ol) &&
414
+ (el.options[:ial] && (el.options[:ial][:refs] || []).include?('footnotes')) # RM can't use rescue nil
415
+ res.strip.empty? ? nil : "{:#{res}}"
416
+ end
417
+
418
+ # :startdoc:
419
+
420
+ end
421
+
422
+ end
423
+ end