motion-kramdown 0.5.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.
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