gitlab-gollum-lib 1.0.0

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