Class: Newstile::Converter::Html

Inherits:
Base
  • Object
show all
Includes:
Utils::HTML
Defined in:
lib/newstile/converter/html.rb

Overview

Converts a Newstile::Document to HTML.

Constant Summary collapse

INDENTATION =

Defines the amount of indentation used when nesting HTML tags.

2
HTML_TAGS_WITH_BODY =
['div', 'script', 'iframe', 'textarea']
TYPOGRAPHIC_SYMS =
{
  :mdash => [::Newstile::Utils::Entities.entity('mdash')],
  :ndash => [::Newstile::Utils::Entities.entity('ndash')],
  :hellip => [::Newstile::Utils::Entities.entity('hellip')],
  :laquo_space => [::Newstile::Utils::Entities.entity('laquo'), ::Newstile::Utils::Entities.entity('nbsp')],
  :raquo_space => [::Newstile::Utils::Entities.entity('nbsp'), ::Newstile::Utils::Entities.entity('raquo')],
  :laquo => [::Newstile::Utils::Entities.entity('laquo')],
  :raquo => [::Newstile::Utils::Entities.entity('raquo')],
  :qdash => [::Newstile::Utils::Entities.entity('8213')],
  :qdash_space => [::Newstile::Utils::Entities.entity('8213'), ::Newstile::Utils::Entities.entity('nbsp')]
}

Constants included from Utils::HTML

Utils::HTML::ESCAPE_ALL_RE, Utils::HTML::ESCAPE_ATTRIBUTE_RE, Utils::HTML::ESCAPE_MAP, Utils::HTML::ESCAPE_RE_FROM_TYPE, Utils::HTML::ESCAPE_TEXT_RE

Instance Method Summary collapse

Methods included from Utils::HTML

#entity_to_str, #escape_html, #html_attributes

Methods inherited from Base

apply_template, convert, #generate_id, get_template

Constructor Details

#initialize(doc) ⇒ Html

Initialize the HTML converter with the given Newstile document doc.



49
50
51
52
53
54
55
# File 'lib/newstile/converter/html.rb', line 49

def initialize(doc)
  super
  @footnote_counter = @footnote_start = @doc.options[:footnote_nr]
  @footnotes = []
  @toc = []
  @toc_code = nil
end

Instance Method Details

#convert(el, indent = -INDENTATION,, opts = {}) ⇒ Object



57
58
59
# File 'lib/newstile/converter/html.rb', line 57

def convert(el, indent = -INDENTATION, opts = {})
  send("convert_#{el.type}", el, indent, opts)
end

#convert_a(el, indent, opts) ⇒ Object



236
237
238
239
240
241
242
243
244
245
246
247
# File 'lib/newstile/converter/html.rb', line 236

def convert_a(el, indent, opts)
  do_obfuscation = el.attr['href'] =~ /^mailto:/
  if do_obfuscation
    el = Marshal.load(Marshal.dump(el)) # so that the original is not changed
    href = obfuscate(el.attr['href'].sub(/^mailto:/, ''))
    mailto = obfuscate('mailto')
    el.attr['href'] = "#{mailto}:#{href}"
  end
  res = inner(el, indent, opts)
  res = obfuscate(res) if do_obfuscation
  "<a#{html_attributes(el)}>#{res}</a>"
end

#convert_abbreviation(el, indent, opts) ⇒ Object



316
317
318
319
320
# File 'lib/newstile/converter/html.rb', line 316

def convert_abbreviation(el, indent, opts)
  title = @doc.parse_infos[:abbrev_defs][el.value]
  title = nil if title.empty?
  "<abbr#{title ? " title=\"#{title}\"" : ''}>#{el.value}</abbr>"
end

#convert_blank(el, indent, opts) ⇒ Object



71
72
73
# File 'lib/newstile/converter/html.rb', line 71

def convert_blank(el, indent, opts)
  "\n"
end

#convert_blockquote(el, indent, opts) ⇒ Object



113
114
115
# File 'lib/newstile/converter/html.rb', line 113

def convert_blockquote(el, indent, opts)
  "#{' '*indent}<blockquote#{html_attributes(el)}>\n#{inner(el, indent, opts)}#{' '*indent}</blockquote>\n"
end

#convert_br(el, indent, opts) ⇒ Object



232
233
234
# File 'lib/newstile/converter/html.rb', line 232

def convert_br(el, indent, opts)
  "<br />"
end

#convert_codeblock(el, indent, opts) ⇒ Object



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/newstile/converter/html.rb', line 87

def convert_codeblock(el, indent, opts)
  el = Marshal.load(Marshal.dump(el)) # so that the original is not changed
  lang = el.attr.delete('lang')
  if lang && HIGHLIGHTING_AVAILABLE
    opts = {:wrap => @doc.options[:coderay_wrap], :line_numbers => @doc.options[:coderay_line_numbers],
      :line_number_start => @doc.options[:coderay_line_number_start], :tab_width => @doc.options[:coderay_tab_width],
      :bold_every => @doc.options[:coderay_bold_every], :css => @doc.options[:coderay_css]}
    result = CodeRay.scan(el.value, lang.to_sym).html(opts).chomp + "\n"
    "#{' '*indent}<div#{html_attributes(el)}>#{result}#{' '*indent}</div>\n"
  else
    result = escape_html(el.value)
    if el.attr['class'].to_s =~ /\bshow-whitespaces\b/
      result.gsub!(/(?:(^[ \t]+)|([ \t]+$)|([ \t]+))/) do |m|
        suffix = ($1 ? '-l' : ($2 ? '-r' : ''))
        m.scan(/./).map do |c|
          case c
          when "\t" then "<span class=\"ws-tab#{suffix}\">\t</span>"
          when " " then "<span class=\"ws-space#{suffix}\">&#8901;</span>"
          end
        end.join('')
      end
    end
    "#{' '*indent}<pre#{html_attributes(el)}><code>#{result}#{result =~ /\n\Z/ ? '' : "\n"}</code></pre>\n"
  end
end

#convert_codespan(el, indent, opts) ⇒ Object



253
254
255
256
257
258
259
260
261
262
# File 'lib/newstile/converter/html.rb', line 253

def convert_codespan(el, indent, opts)
  el = Marshal.load(Marshal.dump(el)) # so that the original is not changed
  lang = el.attr.delete('lang')
  if lang && HIGHLIGHTING_AVAILABLE
    result = CodeRay.scan(el.value, lang.to_sym).html(:wrap => :span, :css => @doc.options[:coderay_css]).chomp
    "<code#{html_attributes(el)}>#{result}</code>"
  else
    "<code#{html_attributes(el)}>#{escape_html(el.value)}</code>"
  end
end

#convert_comment(el, indent, opts) ⇒ Object



224
225
226
227
228
229
230
# File 'lib/newstile/converter/html.rb', line 224

def convert_comment(el, indent, opts)
  if el.options[:category] == :block
    "#{' '*indent}<!-- #{el.value} -->\n"
  else
    "<!-- #{el.value} -->"
  end
end

#convert_dt(el, indent, opts) ⇒ Object



161
162
163
# File 'lib/newstile/converter/html.rb', line 161

def convert_dt(el, indent, opts)
  "#{' '*indent}<dt#{html_attributes(el)}>#{inner(el, indent, opts)}</dt>\n"
end

#convert_em(el, indent, opts) ⇒ Object Also known as: convert_strong



279
280
281
# File 'lib/newstile/converter/html.rb', line 279

def convert_em(el, indent, opts)
  "<#{el.type}#{html_attributes(el)}>#{inner(el, indent, opts)}</#{el.type}>"
end

#convert_entity(el, indent, opts) ⇒ Object



284
285
286
# File 'lib/newstile/converter/html.rb', line 284

def convert_entity(el, indent, opts)
  entity_to_str(el.value, el.options[:original])
end

#convert_footnote(el, indent, opts) ⇒ Object



264
265
266
267
268
269
# File 'lib/newstile/converter/html.rb', line 264

def convert_footnote(el, indent, opts)
  number = @footnote_counter
  @footnote_counter += 1
  @footnotes << [el.options[:name], @doc.parse_infos[:footnotes][el.options[:name]]]
  "<sup id=\"fnref:#{el.options[:name]}\"><a href=\"#fn:#{el.options[:name]}\" rel=\"footnote\">#{number}</a></sup>"
end

#convert_header(el, indent, opts) ⇒ Object



121
122
123
124
125
126
127
128
# File 'lib/newstile/converter/html.rb', line 121

def convert_header(el, indent, opts)
  el = Marshal.load(Marshal.dump(el)) # so that the original is not changed
  if @doc.options[:auto_ids] && !el.attr['id']
    el.attr['id'] = generate_id(el.options[:raw_text])
  end
  @toc << [el.options[:level], el.attr['id'], el.children] if el.attr['id'] && within_toc_depth?(el)
  "#{' '*indent}<h#{el.options[:level]}#{html_attributes(el)}>#{inner(el, indent, opts)}</h#{el.options[:level]}>\n"
end

#convert_hr(el, indent, opts) ⇒ Object



134
135
136
# File 'lib/newstile/converter/html.rb', line 134

def convert_hr(el, indent, opts)
  "#{' '*indent}<hr />\n"
end

#convert_html_element(el, indent, opts) ⇒ Object



167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/newstile/converter/html.rb', line 167

def convert_html_element(el, indent, opts)
  parent = opts[:parent]
  res = inner(el, indent, opts)
  if el.options[:category] == :span
    "<#{el.value}#{html_attributes(el)}" << (!res.empty? || HTML_TAGS_WITH_BODY.include?(el.value) ? ">#{res}</#{el.value}>" : " />")
  else
    output = ''
    output << ' '*indent if parent.type != :html_element || parent.options[:parse_type] != :raw
    output << "<#{el.value}#{html_attributes(el)}"
    if !res.empty? && el.options[:parse_type] != :block
      output << ">#{res}</#{el.value}>"
    elsif !res.empty?
      output << ">\n#{res.chomp}\n"  << ' '*indent << "</#{el.value}>"
    elsif HTML_TAGS_WITH_BODY.include?(el.value)
      output << "></#{el.value}>"
    else
      output << " />"
    end
    output << "\n" if parent.type != :html_element || parent.options[:parse_type] != :raw
    output
  end
end

#convert_img(el, indent, opts) ⇒ Object



249
250
251
# File 'lib/newstile/converter/html.rb', line 249

def convert_img(el, indent, opts)
  "<img#{html_attributes(el)} />"
end

#convert_li(el, indent, opts) ⇒ Object Also known as: convert_dd



149
150
151
152
153
154
155
156
157
158
# File 'lib/newstile/converter/html.rb', line 149

def convert_li(el, indent, opts)
  output = ' '*indent << "<#{el.type}" << html_attributes(el) << ">"
  res = inner(el, indent, opts)
  if el.children.empty? || (el.children.first.type == :p && el.children.first.options[:transparent])
    output << res << (res =~ /\n\Z/ ? ' '*indent : '')
  else
    output << "\n" << res << ' '*indent
  end
  output << "</#{el.type}>\n"
end

#convert_math(el, indent, opts) ⇒ Object



307
308
309
310
311
312
313
314
# File 'lib/newstile/converter/html.rb', line 307

def convert_math(el, indent, opts)
  el = Marshal.load(Marshal.dump(el)) # so that the original is not changed
  el.attr['class'] ||= ''
  el.attr['class'] += (el.attr['class'].empty? ? '' : ' ') + 'math'
  type = 'span'
  type = 'div' if el.options[:category] == :block
  "<#{type}#{html_attributes(el)}>#{escape_html(el.value)}</#{type}>#{type == 'div' ? "\n" : ''}"
end

#convert_p(el, indent, opts) ⇒ Object



79
80
81
82
83
84
85
# File 'lib/newstile/converter/html.rb', line 79

def convert_p(el, indent, opts)
  if el.options[:transparent]
    "#{inner(el, indent, opts)}"
  else
    "#{' '*indent}<p#{html_attributes(el)}>#{inner(el, indent, opts)}</p>\n"
  end
end

#convert_raw(el, indent, opts) ⇒ Object



271
272
273
274
275
276
277
# File 'lib/newstile/converter/html.rb', line 271

def convert_raw(el, indent, opts)
  if !el.options[:type] || el.options[:type].empty? || el.options[:type].include?('html')
    el.value + (el.options[:category] == :block ? "\n" : '')
  else
    ''
  end
end

#convert_root(el, indent, opts) ⇒ Object



322
323
324
325
326
327
328
329
330
331
332
333
334
335
# File 'lib/newstile/converter/html.rb', line 322

def convert_root(el, indent, opts)
  result = inner(el, indent, opts)
  result << footnote_content
  if @toc_code
    toc_tree = generate_toc_tree(@toc, @toc_code[0], @toc_code[1] || {})
    text = if toc_tree.children.size > 0
             convert(toc_tree, 0)
           else
             ''
           end
    result.sub!(/#{@toc_code.last}/, text)
  end
  result
end

#convert_smart_quote(el, indent, opts) ⇒ Object



303
304
305
# File 'lib/newstile/converter/html.rb', line 303

def convert_smart_quote(el, indent, opts)
  entity_to_str(::Newstile::Utils::Entities.entity(el.value.to_s))
end

#convert_summary(el, indent, opts) ⇒ Object



117
118
119
# File 'lib/newstile/converter/html.rb', line 117

def convert_summary(el, indent, opts)
  "#{' '*indent}<p#{html_attributes(el)}><b>#{inner(el, indent, opts)}#{' '*indent}</b></p>\n"
end

#convert_table(el, indent, opts) ⇒ Object



200
201
202
203
204
205
206
207
208
209
# File 'lib/newstile/converter/html.rb', line 200

def convert_table(el, indent, opts)
  if el.options[:alignment].all? {|a| a == :default}
    alignment = ''
  else
    alignment = el.options[:alignment].map do |a|
      "#{' '*(indent + INDENTATION)}" + (a == :default ? "<col />" : "<col align=\"#{a}\" />") + "\n"
    end.join('')
  end
  "#{' '*indent}<table#{html_attributes(el)}>\n#{alignment}#{inner(el, indent, opts)}#{' '*indent}</table>\n"
end

#convert_td(el, indent, opts) ⇒ Object Also known as: convert_th



218
219
220
221
# File 'lib/newstile/converter/html.rb', line 218

def convert_td(el, indent, opts)
  res = inner(el, indent, opts)
  "#{' '*indent}<#{el.type}#{html_attributes(el)}>#{res.empty? ? "&nbsp;" : res}</#{el.type}>\n"
end

#convert_text(el, indent, opts) ⇒ Object



75
76
77
# File 'lib/newstile/converter/html.rb', line 75

def convert_text(el, indent, opts)
  escape_html(el.value, :text)
end

#convert_thead(el, indent, opts) ⇒ Object Also known as: convert_tbody, convert_tfoot, convert_tr



211
212
213
# File 'lib/newstile/converter/html.rb', line 211

def convert_thead(el, indent, opts)
  "#{' '*indent}<#{el.type}#{html_attributes(el)}>\n#{inner(el, indent, opts)}#{' '*indent}</#{el.type}>\n"
end

#convert_typographic_sym(el, indent, opts) ⇒ Object



299
300
301
# File 'lib/newstile/converter/html.rb', line 299

def convert_typographic_sym(el, indent, opts)
  TYPOGRAPHIC_SYMS[el.value].map {|e| entity_to_str(e)}.join('')
end

#convert_ul(el, indent, opts) ⇒ Object Also known as: convert_ol, convert_dl



138
139
140
141
142
143
144
145
# File 'lib/newstile/converter/html.rb', line 138

def convert_ul(el, indent, opts)
  if !@toc_code && (el.options[:ial][:refs].include?('toc') rescue nil) && (el.type == :ul || el.type == :ol)
    @toc_code = [el.type, el.attr, (0..128).to_a.map{|a| rand(36).to_s(36)}.join]
    @toc_code.last
  else
    "#{' '*indent}<#{el.type}#{html_attributes(el)}>\n#{inner(el, indent, opts)}#{' '*indent}</#{el.type}>\n"
  end
end

#convert_xml_comment(el, indent, opts) ⇒ Object Also known as: convert_xml_pi, convert_html_doctype



190
191
192
193
194
195
196
# File 'lib/newstile/converter/html.rb', line 190

def convert_xml_comment(el, indent, opts)
  if el.options[:category] == :block && (opts[:parent].type != :html_element || opts[:parent].options[:parse_type] != :raw)
    ' '*indent + el.value + "\n"
  else
    el.value
  end
end

#footnote_contentObject

Return a HTML list with the footnote content for the used footnotes.



383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
# File 'lib/newstile/converter/html.rb', line 383

def footnote_content
  ol = Element.new(:ol)
  ol.attr['start'] = @footnote_start if @footnote_start != 1
  @footnotes.each do |name, data|
    li = Element.new(:li, nil, {'id' => "fn:#{name}"}, {:first_is_block => true})
    li.children = Marshal.load(Marshal.dump(data[:content].children))
    ol.children << li

    ref = Element.new(:raw, "<a href=\"#fnref:#{name}\" rev=\"footnote\">&#8617;</a>")
    if li.children.last.type == :p
      para = li.children.last
    else
      li.children << (para = Element.new(:p))
    end
    para.children << ref
  end
  (ol.children.empty? ? '' : "<div class=\"footnotes\">\n#{convert(ol, 2)}</div>\n")
end

#generate_toc_tree(toc, type, attr) ⇒ Object



337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
# File 'lib/newstile/converter/html.rb', line 337

def generate_toc_tree(toc, type, attr)
  sections = Element.new(type, nil, attr)
  sections.attr['id'] ||= 'markdown-toc'
  stack = []
  toc.each do |level, id, children|
    li = Element.new(:li, nil, nil, {:level => level})
    li.children << Element.new(:p, nil, nil, {:transparent => true})
    a = Element.new(:a, nil, {'href' => "##{id}"})
    a.children += children
    li.children.last.children << a
    li.children << Element.new(type)

    success = false
    while !success
      if stack.empty?
        sections.children << li
        stack << li
        success = true
      elsif stack.last.options[:level] < li.options[:level]
        stack.last.children.last.children << li
        stack << li
        success = true
      else
        item = stack.pop
        item.children.pop unless item.children.last.children.size > 0
      end
    end
  end
  while !stack.empty?
    item = stack.pop
    item.children.pop unless item.children.last.children.size > 0
  end
  sections
end

#inner(el, indent, opts) ⇒ Object



61
62
63
64
65
66
67
68
69
# File 'lib/newstile/converter/html.rb', line 61

def inner(el, indent, opts)
  result = ''
  indent += INDENTATION
  el.children.each do |inner_el|
    opts[:parent] = el
    result << send("convert_#{inner_el.type}", inner_el, indent, opts)
  end
  result
end

#obfuscate(text) ⇒ Object

Helper method for obfuscating the text by using HTML entities.



373
374
375
376
377
378
379
380
# File 'lib/newstile/converter/html.rb', line 373

def obfuscate(text)
  result = ""
  text.each_byte do |b|
    result += (b > 128 ? b.chr : "&#%03d;" % b)
  end
  result.force_encoding(text.encoding) if RUBY_VERSION >= '1.9'
  result
end

#within_toc_depth?(el) ⇒ Boolean

Returns:

  • (Boolean)


130
131
132
# File 'lib/newstile/converter/html.rb', line 130

def within_toc_depth?(el)
  @doc.options[:toc_depth] <= 0 || el.options[:level] <= @doc.options[:toc_depth]
end