gollum-lib 1.0.9 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of gollum-lib might be problematic. Click here for more details.

@@ -0,0 +1,45 @@
1
+ # Inserts header anchors and creates TOC
2
+ class Gollum::Filter::TOC < Gollum::Filter
3
+ def extract(d) d; end
4
+
5
+ def process(data)
6
+ doc = Nokogiri::HTML::DocumentFragment.parse(data)
7
+ toc = nil
8
+ doc.css('h1,h2,h3,h4,h5,h6').each do |h|
9
+ # must escape "
10
+ h_name = h.content.gsub(' ','-').gsub('"','%22')
11
+
12
+ level = h.name.gsub(/[hH]/,'').to_i
13
+
14
+ # Add anchors
15
+ h.add_child(%Q{<a class="anchor" id="#{h_name}" href="##{h_name}"></a>})
16
+
17
+ # Build TOC
18
+ toc ||= Nokogiri::XML::DocumentFragment.parse('<div class="toc"><div class="toc-title">Table of Contents</div></div>')
19
+ tail ||= toc.child
20
+ tail_level ||= 0
21
+
22
+ while tail_level < level
23
+ node = Nokogiri::XML::Node.new('ul', doc)
24
+ tail = tail.add_child(node)
25
+ tail_level += 1
26
+ end
27
+ while tail_level > level
28
+ tail = tail.parent
29
+ tail_level -= 1
30
+ end
31
+ node = Nokogiri::XML::Node.new('li', doc)
32
+ # % -> %25 so anchors work on Firefox. See issue #475
33
+ node.add_child(%Q{<a href="##{h_name}">#{h.content}</a>})
34
+ tail.add_child(node)
35
+ end
36
+
37
+ toc = toc.to_xml(@markup.to_xml_opts) if toc != nil
38
+ data = doc.to_xml(@markup.to_xml_opts)
39
+
40
+ @markup.toc = toc
41
+ data.gsub("[[_TOC_]]") do
42
+ toc.nil? ? '' : toc
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,54 @@
1
+ # ~*~ encoding: utf-8 ~*~
2
+ require 'net/http'
3
+ require 'uri'
4
+ require 'open-uri'
5
+
6
+ # Web Sequence Diagrams
7
+ #
8
+ # Render an inline web sequence diagram by sending the WSD code through the
9
+ # online renderer available from www.websequencediagrams.com.
10
+ #
11
+ class Gollum::Filter::WSD < Gollum::Filter
12
+ WSD_URL = "http://www.websequencediagrams.com/index.php"
13
+
14
+ # Extract all sequence diagram blocks into the map and replace with
15
+ # placeholders.
16
+ def extract(data)
17
+ return data if @markup.format == :txt
18
+ data.gsub(/^\{\{\{\{\{\{ ?(.+?)\r?\n(.+?)\r?\n\}\}\}\}\}\}\r?$/m) do
19
+ id = Digest::SHA1.hexdigest($2)
20
+ @map[id] = { :style => $1, :code => $2 }
21
+ id
22
+ end
23
+ end
24
+
25
+ # Process all diagrams from the map and replace the placeholders with
26
+ # the final HTML.
27
+ #
28
+ # data - The String data (with placeholders).
29
+ #
30
+ # Returns the marked up String data.
31
+ def process(data)
32
+ @map.each do |id, spec|
33
+ data.gsub!(id) do
34
+ render_wsd(spec[:code], spec[:style])
35
+ end
36
+ end
37
+ data
38
+ end
39
+
40
+ private
41
+ # Render the sequence diagram on the remote server.
42
+ #
43
+ # Returns an <img> tag to the rendered image, or an HTML error.
44
+ def render_wsd(code, style)
45
+ response = Net::HTTP.post_form(URI.parse(WSD_URL), 'style' => style, 'message' => code)
46
+ if response.body =~ /img: "(.+)"/
47
+ url = "http://www.websequencediagrams.com/#{$1}"
48
+ "<img src=\"#{url}\" />"
49
+ else
50
+ puts response.body
51
+ html_error("Sorry, unable to render sequence diagram at this time.")
52
+ end
53
+ end
54
+ end
@@ -1,14 +1,10 @@
1
1
  # ~*~ encoding: utf-8 ~*~
2
2
  require 'digest/sha1'
3
3
  require 'cgi'
4
- require 'pygments'
4
+ require 'rouge'
5
5
  require 'base64'
6
6
 
7
7
  require File.expand_path '../helpers', __FILE__
8
- require File.expand_path '../remote_code', __FILE__
9
-
10
- # initialize Pygments
11
- Pygments.start
12
8
 
13
9
  module Gollum
14
10
 
@@ -16,10 +12,18 @@ module Gollum
16
12
  include Helpers
17
13
 
18
14
  @formats = {}
19
-
15
+
20
16
  class << self
21
- attr_reader :formats
22
-
17
+
18
+ # Only use the formats that are specified in config.rb
19
+ def formats
20
+ if defined? Gollum::Page::FORMAT_NAMES
21
+ @formats.select { |_, value| Gollum::Page::FORMAT_NAMES.values.include? value[:name] }
22
+ else
23
+ @formats
24
+ end
25
+ end
26
+
23
27
  # Register a file extension and associated markup type
24
28
  #
25
29
  # ext - The file extension
@@ -38,7 +42,15 @@ module Gollum
38
42
  end
39
43
 
40
44
  attr_accessor :toc
41
- attr_reader :metadata
45
+ attr_accessor :metadata
46
+ attr_reader :encoding
47
+ attr_reader :sanitize
48
+ attr_reader :format
49
+ attr_reader :wiki
50
+ attr_reader :name
51
+ attr_reader :include_levels
52
+ attr_reader :to_xml_opts
53
+ attr_reader :dir
42
54
 
43
55
  # Initialize a new Markup object.
44
56
  #
@@ -54,13 +66,8 @@ module Gollum
54
66
  @sub_page = page.sub_page
55
67
  @parent_page = page.parent_page
56
68
  @dir = ::File.dirname(page.path)
57
- @tagmap = {}
58
- @codemap = {}
59
- @wsdmap = {}
60
- @premap = {}
61
- @toc = nil
62
69
  @metadata = nil
63
- @to_xml = { :save_with => Nokogiri::XML::Node::SaveOptions::DEFAULT_XHTML ^ 1, :indent => 0, :encoding => 'UTF-8' }
70
+ @to_xml_opts = { :save_with => Nokogiri::XML::Node::SaveOptions::DEFAULT_XHTML ^ 1, :indent => 0, :encoding => 'UTF-8' }
64
71
  end
65
72
 
66
73
  # Render the content with Gollum wiki syntax on top of the file's own
@@ -72,379 +79,40 @@ module Gollum
72
79
  #
73
80
  # Returns the formatted String content.
74
81
  def render(no_follow = false, encoding = nil, include_levels = 10)
75
- sanitize = no_follow ?
82
+ @sanitize = no_follow ?
76
83
  @wiki.history_sanitizer :
77
84
  @wiki.sanitizer
78
-
85
+
79
86
  @encoding = encoding
80
87
  @include_levels = include_levels
81
88
 
82
89
  data = @data.dup
83
- data = extract_metadata(data)
84
- data = extract_remote_code(data)
85
- data = extract_code(data)
86
- data = extract_wsd(data)
87
- data = extract_tags(data)
88
- begin
89
- data = GitHub::Markup.render(@name, data)
90
- if data.nil?
91
- raise "There was an error converting #{@name} to HTML."
92
- end
93
- rescue Object => e
94
- data = html_error(e.message)
95
- end
96
- data = process_tags(data)
97
- data = process_code(data, encoding)
98
-
99
- doc = Nokogiri::HTML::DocumentFragment.parse(data)
100
- doc = sanitize.clean_node!(doc) if sanitize
101
- doc,toc = process_headers(doc)
102
- @toc = @sub_page ? ( @parent_page ? @parent_page.toc_data : "[[_TOC_]]" ) : toc
103
- yield doc if block_given?
104
- # nokogiri's save options are ored together. FORMAT has a value of 1 so ^ 1 removes it.
105
- # formatting will create extra spaces in pre tags.
106
- # https://github.com/sparklemotion/nokogiri/issues/782
107
- # DEFAULT_HTML encodes unicode so XHTML is used for proper unicode support in href.
108
- data = doc.to_xml( @to_xml )
109
-
110
- data = process_toc_tags(data)
111
- data = process_wsd(data)
112
- data.gsub!(/<p><\/p>/) do
113
- ''
114
- end
115
-
116
- data
117
- end
118
-
119
- # Inserts header anchors and creates TOC
120
- #
121
- # doc - Nokogiri parsed document
122
- #
123
- # Returns doc Document and toc String
124
- def process_headers(doc)
125
- toc = nil
126
- doc.css('h1,h2,h3,h4,h5,h6').each do |h|
127
- # must escape "
128
- h_name = h.content.gsub(' ','-').gsub('"','%22')
129
-
130
- level = h.name.gsub(/[hH]/,'').to_i
131
-
132
- # Add anchors
133
- h.add_child(%Q{<a class="anchor" id="#{h_name}" href="##{h_name}"></a>})
134
-
135
- # Build TOC
136
- toc ||= Nokogiri::XML::DocumentFragment.parse('<div class="toc"><div class="toc-title">Table of Contents</div></div>')
137
- tail ||= toc.child
138
- tail_level ||= 0
139
-
140
- while tail_level < level
141
- node = Nokogiri::XML::Node.new('ul', doc)
142
- tail = tail.add_child(node)
143
- tail_level += 1
144
- end
145
- while tail_level > level
146
- tail = tail.parent
147
- tail_level -= 1
148
- end
149
- node = Nokogiri::XML::Node.new('li', doc)
150
- # % -> %25 so anchors work on Firefox. See issue #475
151
- node.add_child(%Q{<a href="##{h_name}">#{h.content}</a>})
152
- tail.add_child(node)
153
- end
154
- toc = toc.to_xml(@to_xml) if toc != nil
155
- [doc, toc]
156
- end
157
-
158
- #########################################################################
159
- #
160
- # Tags
161
- #
162
- #########################################################################
163
-
164
- # Extract all tags into the tagmap and replace with placeholders.
165
- #
166
- # data - The raw String data.
167
- #
168
- # Returns the placeholder'd String data.
169
- def extract_tags(data)
170
- if @format == :asciidoc
171
- return data
172
- end
173
- data.gsub!(/(.?)\[\[(.+?)\]\]([^\[]?)/m) do
174
- if $1 == "'" && $3 != "'"
175
- "[[#{$2}]]#{$3}"
176
- elsif $2.include?('][')
177
- if $2[0..4] == 'file:'
178
- pre = $1
179
- post = $3
180
- parts = $2.split('][')
181
- parts[0][0..4] = ""
182
- link = "#{parts[1]}|#{parts[0].sub(/\.org/,'')}"
183
- id = Digest::SHA1.hexdigest(link)
184
- @tagmap[id] = link
185
- "#{pre}#{id}#{post}"
186
- else
187
- $&
188
- end
189
- else
190
- id = Digest::SHA1.hexdigest($2)
191
- @tagmap[id] = $2
192
- "#{$1}#{id}#{$3}"
193
- end
194
- end
195
- data
196
- end
197
-
198
- # Process all tags from the tagmap and replace the placeholders with the
199
- # final markup.
200
- #
201
- # data - The String data (with placeholders).
202
- #
203
- # Returns the marked up String data.
204
- def process_tags(data)
205
- @tagmap.each do |id, tag|
206
- # If it's preformatted, just put the tag back
207
- if is_preformatted?(data, id)
208
- data.gsub!(id) do
209
- "[[#{tag}]]"
210
- end
211
- else
212
- data.gsub!(id) do
213
- process_tag(tag).gsub('%2F', '/')
214
- end
215
- end
216
- end
217
- data
218
- end
219
-
220
- # Find `id` within `data` and determine if it's within
221
- # preformatted tags.
222
- #
223
- # data - The String data (with placeholders).
224
- # id - The String SHA1 hash.
225
- PREFORMATTED_TAGS = %w(code tt)
226
- def is_preformatted?(data, id)
227
- doc = Nokogiri::HTML::DocumentFragment.parse(data)
228
- node = doc.search("[text()*='#{id}']").first
229
- node && (PREFORMATTED_TAGS.include?(node.name) ||
230
- node.ancestors.any? { |a| PREFORMATTED_TAGS.include?(a.name) })
231
- end
232
-
233
- # Process a single tag into its final HTML form.
234
- #
235
- # tag - The String tag contents (the stuff inside the double
236
- # brackets).
237
- #
238
- # Returns the String HTML version of the tag.
239
- def process_tag(tag)
240
- if tag =~ /^_TOC_$/
241
- %{[[#{tag}]]}
242
- elsif tag =~ /^_$/
243
- %{<div class="clearfloats"></div>}
244
- elsif html = process_include_tag(tag)
245
- html
246
- elsif html = process_image_tag(tag)
247
- html
248
- elsif html = process_file_link_tag(tag)
249
- html
250
- else
251
- process_page_link_tag(tag)
90
+ filter_chain = @wiki.filter_chain.map do |r|
91
+ Gollum::Filter.const_get(r).new(self)
252
92
  end
253
- end
254
-
255
- # Render a (presumably) non-fatal error as HTML
256
- #
257
- def html_error(message)
258
- "<p class=\"gollum-error\">#{message}</p>"
259
- end
260
-
261
- # Attempt to process the tag as an include tag
262
- #
263
- # tag - The String tag contents (the stuff inside the double brackets).
264
- #
265
- # Returns the String HTML if the tag is a valid image tag or nil
266
- # if it is not.
267
- #
268
- def process_include_tag(tag)
269
- return unless /^include:/.match(tag)
270
- page_name = tag[8..-1]
271
93
 
272
- if @include_levels > 0
273
- page = @wiki.page(page_name)
274
- if page
275
- page.formatted_data(@encoding, @include_levels-1)
276
- else
277
- html_error("Cannot include #{process_page_link_tag(page_name)} - does not exist yet")
278
- end
279
- else
280
- html_error("Too many levels of included pages, will not include #{process_page_link_tag(page_name)}")
94
+ # Since the last 'extract' action in our chain *should* be the markup
95
+ # to HTML converter, we now have HTML which we can parse and yield, for
96
+ # anyone who wants it
97
+ if block_given?
98
+ yield Nokogiri::HTML::DocumentFragment.parse(data)
281
99
  end
282
- end
283
-
284
- # Attempt to process the tag as an image tag.
285
- #
286
- # tag - The String tag contents (the stuff inside the double brackets).
287
- #
288
- # Returns the String HTML if the tag is a valid image tag or nil
289
- # if it is not.
290
- def process_image_tag(tag)
291
- parts = tag.split('|')
292
- return if parts.size.zero?
293
100
 
294
- name = parts[0].strip
295
- path = if file = find_file(name)
296
- ::File.join @wiki.base_path, file.path
297
- elsif name =~ /^https?:\/\/.+(jpg|png|gif|svg|bmp)$/i
298
- name
101
+ # First we extract the data through the chain...
102
+ filter_chain.each do |filter|
103
+ data = filter.extract(data)
299
104
  end
300
-
301
- if path
302
- opts = parse_image_tag_options(tag)
303
-
304
- containered = false
305
-
306
- classes = [] # applied to whatever the outermost container is
307
- attrs = [] # applied to the image
308
-
309
- align = opts['align']
310
- if opts['float']
311
- containered = true
312
- align ||= 'left'
313
- if %w{left right}.include?(align)
314
- classes << "float-#{align}"
315
- end
316
- elsif %w{top texttop middle absmiddle bottom absbottom baseline}.include?(align)
317
- attrs << %{align="#{align}"}
318
- elsif align
319
- if %w{left center right}.include?(align)
320
- containered = true
321
- classes << "align-#{align}"
322
- end
323
- end
324
-
325
- if width = opts['width']
326
- if width =~ /^\d+(\.\d+)?(em|px)$/
327
- attrs << %{width="#{width}"}
328
- end
329
- end
330
-
331
- if height = opts['height']
332
- if height =~ /^\d+(\.\d+)?(em|px)$/
333
- attrs << %{height="#{height}"}
334
- end
335
- end
336
-
337
- if alt = opts['alt']
338
- attrs << %{alt="#{alt}"}
339
- end
340
-
341
- attr_string = attrs.size > 0 ? attrs.join(' ') + ' ' : ''
342
-
343
- if opts['frame'] || containered
344
- classes << 'frame' if opts['frame']
345
- %{<span class="#{classes.join(' ')}">} +
346
- %{<span>} +
347
- %{<img src="#{path}" #{attr_string}/>} +
348
- (alt ? %{<span>#{alt}</span>} : '') +
349
- %{</span>} +
350
- %{</span>}
351
- else
352
- %{<img src="#{path}" #{attr_string}/>}
353
- end
105
+
106
+ # Then we process the data through the chain *backwards*
107
+ filter_chain.reverse.each do |filter|
108
+ data = filter.process(data)
354
109
  end
355
- end
356
-
357
- # Parse any options present on the image tag and extract them into a
358
- # Hash of option names and values.
359
- #
360
- # tag - The String tag contents (the stuff inside the double brackets).
361
- #
362
- # Returns the options Hash:
363
- # key - The String option name.
364
- # val - The String option value or true if it is a binary option.
365
- def parse_image_tag_options(tag)
366
- tag.split('|')[1..-1].inject({}) do |memo, attr|
367
- parts = attr.split('=').map { |x| x.strip }
368
- memo[parts[0]] = (parts.size == 1 ? true : parts[1])
369
- memo
370
- end
371
- end
372
-
373
- # Attempt to process the tag as a file link tag.
374
- #
375
- # tag - The String tag contents (the stuff inside the double
376
- # brackets).
377
- #
378
- # Returns the String HTML if the tag is a valid file link tag or nil
379
- # if it is not.
380
- def process_file_link_tag(tag)
381
- parts = tag.split('|')
382
- return if parts.size.zero?
383
-
384
- name = parts[0].strip
385
- path = parts[1] && parts[1].strip
386
- path = if path && file = find_file(path)
387
- ::File.join @wiki.base_path, file.path
388
- elsif path =~ %r{^https?://}
389
- path
390
- else
391
- nil
392
- end
393
-
394
- if name && path && file
395
- %{<a href="#{::File.join @wiki.base_path, file.path}">#{name}</a>}
396
- elsif name && path
397
- %{<a href="#{path}">#{name}</a>}
398
- else
399
- nil
400
- end
401
- end
402
110
 
403
- # Attempt to process the tag as a page link tag.
404
- #
405
- # tag - The String tag contents (the stuff inside the double
406
- # brackets).
407
- #
408
- # Returns the String HTML if the tag is a valid page link tag or nil
409
- # if it is not.
410
- def process_page_link_tag(tag)
411
- parts = tag.split('|')
412
- parts.reverse! if @format == :mediawiki
413
-
414
- name, page_name = *parts.compact.map(&:strip)
415
- cname = @wiki.page_class.cname(page_name || name)
416
-
417
- if name =~ %r{^https?://} && page_name.nil?
418
- %{<a href="#{name}">#{name}</a>}
419
- else
420
- presence = "absent"
421
- link_name = cname
422
- page, extra = find_page_from_name(cname)
423
- if page
424
- link_name = @wiki.page_class.cname(page.name)
425
- presence = "present"
426
- end
427
- link = ::File.join(@wiki.base_path, page ? page.escaped_url_path : CGI.escape(link_name))
428
-
429
- # //page is invalid
430
- # strip all duplicate forward slashes using helpers.rb trim_leading_slash
431
- # //page => /page
432
- link = trim_leading_slash link
433
-
434
- %{<a class="internal #{presence}" href="#{link}#{extra}">#{name}</a>}
111
+ # Finally, a little bit of cleanup, just because
112
+ data.gsub!(/<p><\/p>/) do
113
+ ''
435
114
  end
436
- end
437
-
438
115
 
439
- # Process the special table of contents tag [[_TOC_]]
440
- #
441
- # data - The String data (with placeholders).
442
- #
443
- # Returns the marked up String data.
444
- def process_toc_tags(data)
445
- data.gsub!("[[_TOC_]]") do
446
- @toc.nil? ? '' : @toc
447
- end
448
116
  data
449
117
  end
450
118
 
@@ -462,254 +130,6 @@ module Gollum
462
130
  end
463
131
  end
464
132
 
465
- # Find a page from a given cname. If the page has an anchor (#) and has
466
- # no match, strip the anchor and try again.
467
- #
468
- # cname - The String canonical page name including path.
469
- #
470
- # Returns a Gollum::Page instance if a page is found, or an Array of
471
- # [Gollum::Page, String extra] if a page without the extra anchor data
472
- # is found.
473
- def find_page_from_name(cname)
474
- slash = cname.rindex('/')
475
-
476
- unless slash.nil?
477
- name = cname[slash+1..-1]
478
- path = cname[0..slash]
479
- page = @wiki.paged(name, path)
480
- else
481
- page = @wiki.paged(cname, '/') || @wiki.page(cname)
482
- end
483
-
484
- if page
485
- return page
486
- end
487
- if pos = cname.index('#')
488
- [@wiki.page(cname[0...pos]), cname[pos..-1]]
489
- end
490
- end
491
-
492
- #########################################################################
493
- #
494
- # Remote code - fetch code from url and replace the contents to a
495
- # code-block that gets run the next parse.
496
- # Acceptable formats:
497
- # ```language:local-file.ext```
498
- # ```language:/abs/other-file.ext```
499
- # ```language:https://example.com/somefile.txt```
500
- #
501
- #########################################################################
502
-
503
- def extract_remote_code data
504
- data.gsub /^[ \t]*``` ?([^:\n\r]+):((http)?[^`\n\r]+)```/ do
505
- language = $1
506
- uri = $2
507
- protocol = $3
508
-
509
- # Detect local file
510
- if protocol.nil?
511
- if file = self.find_file(uri, @wiki.ref)
512
- contents = file.raw_data
513
- else
514
- # How do we communicate a render error?
515
- next "File not found: #{CGI::escapeHTML(uri)}"
516
- end
517
- else
518
- contents = Gollum::RemoteCode.new(uri).contents
519
- end
520
-
521
- "```#{language}\n#{contents}\n```\n"
522
- end
523
- end
524
-
525
- #########################################################################
526
- #
527
- # Code
528
- #
529
- #########################################################################
530
-
531
- # Extract all code blocks into the codemap and replace with placeholders.
532
- #
533
- # data - The raw String data.
534
- #
535
- # Returns the placeholder'd String data.
536
- def extract_code(data)
537
- data.gsub!(/^([ \t]*)(~~~+) ?([^\r\n]+)?\r?\n(.+?)\r?\n\1(~~~+)[ \t\r]*$/m) do
538
- m_indent = $1
539
- m_start = $2 # ~~~
540
- m_lang = $3
541
- m_code = $4
542
- m_end = $5 # ~~~
543
-
544
- # start and finish tilde fence must be the same length
545
- return '' if m_start.length != m_end.length
546
-
547
- lang = m_lang ? m_lang.strip : nil
548
- id = Digest::SHA1.hexdigest("#{lang}.#{m_code}")
549
- cached = check_cache(:code, id)
550
-
551
- # extract lang from { .ruby } or { #stuff .ruby .indent }
552
- # see http://johnmacfarlane.net/pandoc/README.html#delimited-code-blocks
553
-
554
- if lang
555
- lang = lang.match(/\.([^}\s]+)/)
556
- lang = lang[1] unless lang.nil?
557
- end
558
-
559
- @codemap[id] = cached ?
560
- { :output => cached } :
561
- { :lang => lang, :code => m_code, :indent => m_indent }
562
-
563
- "#{m_indent}#{id}" # print the SHA1 ID with the proper indentation
564
- end
565
-
566
- data.gsub!(/^([ \t]*)``` ?([^\r\n]+)?\r?\n(.+?)\r?\n\1```[ \t]*\r?$/m) do
567
- lang = $2 ? $2.strip : nil
568
- id = Digest::SHA1.hexdigest("#{lang}.#{$3}")
569
- cached = check_cache(:code, id)
570
- @codemap[id] = cached ?
571
- { :output => cached } :
572
- { :lang => lang, :code => $3, :indent => $1 }
573
- "#{$1}#{id}" # print the SHA1 ID with the proper indentation
574
- end
575
- data
576
- end
577
-
578
- # Remove the leading space from a code block. Leading space
579
- # is only removed if every single line in the block has leading
580
- # whitespace.
581
- #
582
- # code - The code block to remove spaces from
583
- # regex - A regex to match whitespace
584
- def remove_leading_space(code, regex)
585
- if code.lines.all? { |line| line =~ /\A\r?\n\Z/ || line =~ regex }
586
- code.gsub!(regex) do
587
- ''
588
- end
589
- end
590
- end
591
-
592
- # Process all code from the codemap and replace the placeholders with the
593
- # final HTML.
594
- #
595
- # data - The String data (with placeholders).
596
- # encoding - Encoding Constant or String.
597
- #
598
- # Returns the marked up String data.
599
- def process_code(data, encoding = nil)
600
- return data if data.nil? || data.size.zero? || @codemap.size.zero?
601
-
602
- blocks = []
603
- @codemap.each do |id, spec|
604
- next if spec[:output] # cached
605
-
606
- code = spec[:code]
607
-
608
- remove_leading_space(code, /^#{spec[:indent]}/m)
609
- remove_leading_space(code, /^( |\t)/m)
610
-
611
- blocks << [spec[:lang], code]
612
- end
613
-
614
- highlighted = []
615
- blocks.each do |lang, code|
616
- encoding ||= 'utf-8'
617
- begin
618
- # must set startinline to true for php to be highlighted without <?
619
- # http://pygments.org/docs/lexers/
620
- hl_code = Pygments.highlight(code, :lexer => lang, :options => {:encoding => encoding.to_s, :startinline => true})
621
- rescue
622
- hl_code = code
623
- end
624
- highlighted << hl_code
625
- end
626
-
627
- @codemap.each do |id, spec|
628
- body = spec[:output] || begin
629
- if (body = highlighted.shift.to_s).size > 0
630
- update_cache(:code, id, body)
631
- body
632
- else
633
- "<pre><code>#{CGI.escapeHTML(spec[:code])}</code></pre>"
634
- end
635
- end
636
- data.gsub!(id) do
637
- body
638
- end
639
- end
640
-
641
- data
642
- end
643
-
644
- #########################################################################
645
- #
646
- # Sequence Diagrams
647
- #
648
- #########################################################################
649
-
650
- # Extract all sequence diagram blocks into the wsdmap and replace with
651
- # placeholders.
652
- #
653
- # data - The raw String data.
654
- #
655
- # Returns the placeholder'd String data.
656
- def extract_wsd(data)
657
- data.gsub(/^\{\{\{\{\{\{ ?(.+?)\r?\n(.+?)\r?\n\}\}\}\}\}\}\r?$/m) do
658
- id = Digest::SHA1.hexdigest($2)
659
- @wsdmap[id] = { :style => $1, :code => $2 }
660
- id
661
- end
662
- end
663
-
664
- # Process all diagrams from the wsdmap and replace the placeholders with
665
- # the final HTML.
666
- #
667
- # data - The String data (with placeholders).
668
- #
669
- # Returns the marked up String data.
670
- def process_wsd(data)
671
- @wsdmap.each do |id, spec|
672
- style = spec[:style]
673
- code = spec[:code]
674
- data.gsub!(id) do
675
- Gollum::WebSequenceDiagram.new(code, style).to_tag
676
- end
677
- end
678
- data
679
- end
680
-
681
- #########################################################################
682
- #
683
- # Metadata
684
- #
685
- #########################################################################
686
-
687
- # Extract metadata for data and build metadata table. Metadata consists of
688
- # key/value pairs in "key:value" format found between markers. Each
689
- # key/value pair must be on its own line. Internal whitespace in keys and
690
- # values is preserved, but external whitespace is ignored.
691
- #
692
- # Because ri and ruby 1.8.7 are awesome, the markers can't
693
- # be included in this documentation without triggering
694
- # `Unhandled special: Special: type=17`
695
- # Please read the source code for the exact markers
696
- #
697
- # Returns the String of formatted data with metadata removed.
698
- def extract_metadata(data)
699
- @metadata ||= {}
700
- # The markers are `<!-- ---` and `-->`
701
- data.gsub(/\<\!--+\s+---(.*?)--+\>/m) do
702
- # Split untrusted input on newlines, then remove bits that look like
703
- # HTML elements before parsing each line.
704
- $1.split("\n").each do |line|
705
- line.gsub!(/<[^>]*>/, '')
706
- k, v = line.split(':', 2)
707
- @metadata[k.strip] = (v ? v.strip : '') if k
708
- end
709
- ''
710
- end
711
- end
712
-
713
133
  # Hook for getting the formatted value of extracted tag data.
714
134
  #
715
135
  # type - Symbol value identifying what type of data is being extracted.