gitlab-gollum-lib 1.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.
@@ -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