gollum-lib 1.0.9 → 2.0.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.

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.