read-only-gollum 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (176) hide show
  1. data/Gemfile +4 -0
  2. data/HISTORY.md +102 -0
  3. data/Home.md +3 -0
  4. data/LICENSE +21 -0
  5. data/README.md +477 -0
  6. data/Rakefile +142 -0
  7. data/bin/read-only-gollum +126 -0
  8. data/docs/sanitization.md +32 -0
  9. data/lib/gollum.rb +41 -0
  10. data/lib/gollum/blob_entry.rb +78 -0
  11. data/lib/gollum/committer.rb +218 -0
  12. data/lib/gollum/file.rb +64 -0
  13. data/lib/gollum/frontend/app.rb +225 -0
  14. data/lib/gollum/frontend/public/css/dialog.css +141 -0
  15. data/lib/gollum/frontend/public/css/editor.css +537 -0
  16. data/lib/gollum/frontend/public/css/gollum.css +660 -0
  17. data/lib/gollum/frontend/public/css/ie7.css +69 -0
  18. data/lib/gollum/frontend/public/css/template.css +381 -0
  19. data/lib/gollum/frontend/public/images/icon-sprite.png +0 -0
  20. data/lib/gollum/frontend/public/javascript/editor/gollum.editor.js +1096 -0
  21. data/lib/gollum/frontend/public/javascript/editor/langs/asciidoc.js +167 -0
  22. data/lib/gollum/frontend/public/javascript/editor/langs/creole.js +104 -0
  23. data/lib/gollum/frontend/public/javascript/editor/langs/markdown.js +211 -0
  24. data/lib/gollum/frontend/public/javascript/editor/langs/org.js +173 -0
  25. data/lib/gollum/frontend/public/javascript/editor/langs/pod.js +111 -0
  26. data/lib/gollum/frontend/public/javascript/editor/langs/rdoc.js +74 -0
  27. data/lib/gollum/frontend/public/javascript/editor/langs/textile.js +175 -0
  28. data/lib/gollum/frontend/public/javascript/gollum.dialog.js +263 -0
  29. data/lib/gollum/frontend/public/javascript/gollum.js +161 -0
  30. data/lib/gollum/frontend/public/javascript/gollum.placeholder.js +54 -0
  31. data/lib/gollum/frontend/public/javascript/jquery.color.js +123 -0
  32. data/lib/gollum/frontend/public/javascript/jquery.js +7179 -0
  33. data/lib/gollum/frontend/templates/compare.mustache +38 -0
  34. data/lib/gollum/frontend/templates/create.mustache +17 -0
  35. data/lib/gollum/frontend/templates/edit.mustache +17 -0
  36. data/lib/gollum/frontend/templates/editor.mustache +116 -0
  37. data/lib/gollum/frontend/templates/error.mustache +8 -0
  38. data/lib/gollum/frontend/templates/history.mustache +58 -0
  39. data/lib/gollum/frontend/templates/layout.mustache +28 -0
  40. data/lib/gollum/frontend/templates/page.mustache +37 -0
  41. data/lib/gollum/frontend/templates/pages.mustache +35 -0
  42. data/lib/gollum/frontend/templates/search.mustache +36 -0
  43. data/lib/gollum/frontend/templates/searchbar.mustache +10 -0
  44. data/lib/gollum/frontend/views/compare.rb +94 -0
  45. data/lib/gollum/frontend/views/create.rb +48 -0
  46. data/lib/gollum/frontend/views/edit.rb +52 -0
  47. data/lib/gollum/frontend/views/editable.rb +13 -0
  48. data/lib/gollum/frontend/views/error.rb +7 -0
  49. data/lib/gollum/frontend/views/history.rb +44 -0
  50. data/lib/gollum/frontend/views/layout.rb +20 -0
  51. data/lib/gollum/frontend/views/page.rb +57 -0
  52. data/lib/gollum/frontend/views/pages.rb +19 -0
  53. data/lib/gollum/frontend/views/search.rb +20 -0
  54. data/lib/gollum/git_access.rb +248 -0
  55. data/lib/gollum/markup.rb +489 -0
  56. data/lib/gollum/page.rb +430 -0
  57. data/lib/gollum/pagination.rb +61 -0
  58. data/lib/gollum/sanitization.rb +174 -0
  59. data/lib/gollum/tex.rb +89 -0
  60. data/lib/gollum/web_sequence_diagram.rb +43 -0
  61. data/lib/gollum/wiki.rb +636 -0
  62. data/read-only-gollum.gemspec +224 -0
  63. data/templates/formatting.html +92 -0
  64. data/test/examples/empty.git/HEAD +1 -0
  65. data/test/examples/empty.git/config +5 -0
  66. data/test/examples/empty.git/description +1 -0
  67. data/test/examples/empty.git/hooks/applypatch-msg.sample +15 -0
  68. data/test/examples/empty.git/hooks/commit-msg.sample +24 -0
  69. data/test/examples/empty.git/hooks/post-commit.sample +8 -0
  70. data/test/examples/empty.git/hooks/post-receive.sample +15 -0
  71. data/test/examples/empty.git/hooks/post-update.sample +8 -0
  72. data/test/examples/empty.git/hooks/pre-applypatch.sample +14 -0
  73. data/test/examples/empty.git/hooks/pre-commit.sample +46 -0
  74. data/test/examples/empty.git/hooks/pre-rebase.sample +169 -0
  75. data/test/examples/empty.git/hooks/prepare-commit-msg.sample +36 -0
  76. data/test/examples/empty.git/hooks/update.sample +128 -0
  77. data/test/examples/empty.git/info/exclude +6 -0
  78. data/test/examples/empty.git/objects/info/.gitkeep +0 -0
  79. data/test/examples/empty.git/objects/pack/.gitkeep +0 -0
  80. data/test/examples/empty.git/refs/heads/.gitkeep +0 -0
  81. data/test/examples/lotr.git/COMMIT_EDITMSG +1 -0
  82. data/test/examples/lotr.git/HEAD +1 -0
  83. data/test/examples/lotr.git/ORIG_HEAD +1 -0
  84. data/test/examples/lotr.git/config +12 -0
  85. data/test/examples/lotr.git/description +1 -0
  86. data/test/examples/lotr.git/index +0 -0
  87. data/test/examples/lotr.git/info/exclude +6 -0
  88. data/test/examples/lotr.git/logs/HEAD +3 -0
  89. data/test/examples/lotr.git/logs/refs/heads/master +3 -0
  90. data/test/examples/lotr.git/objects/06/131480411710c92a82fe2d1e76932c70feb2e5 +0 -0
  91. data/test/examples/lotr.git/objects/0a/de1e2916346d4c1f2fb63b863fd3c16808fe44 +0 -0
  92. data/test/examples/lotr.git/objects/0e/d8cbe0a25235bd867e65193c7d837c66b328ef +3 -0
  93. data/test/examples/lotr.git/objects/12/629d666c5e3178f82f533f543d61b53dc78c0b +0 -0
  94. data/test/examples/lotr.git/objects/1d/b89ebba7e2c14d93b94ff98cfa3708a4f0d4e3 +2 -0
  95. data/test/examples/lotr.git/objects/24/49c2681badfd3c189e8ed658dacffe8ba48fe5 +0 -0
  96. data/test/examples/lotr.git/objects/25/4bdc1ba27d8b8a794538a8522d9a2b56ec2dd9 +0 -0
  97. data/test/examples/lotr.git/objects/2c/b9156ad383914561a8502fc70f5a1d887e48ad +4 -0
  98. data/test/examples/lotr.git/objects/5d/cac289a8603188d2c5caf481dcba2985126aaa +0 -0
  99. data/test/examples/lotr.git/objects/60/f12f4254f58801b9ee7db7bca5fa8aeefaa56b +0 -0
  100. data/test/examples/lotr.git/objects/71/4323c104239440a5c66ab12a67ed07a83c404f +0 -0
  101. data/test/examples/lotr.git/objects/84/0ec5b1ba1320e8ec443f28f99566f615d5af10 +0 -0
  102. data/test/examples/lotr.git/objects/93/6b83ee0dd8837adb82511e40d5e4ebe59bb675 +0 -0
  103. data/test/examples/lotr.git/objects/94/523d7ae48aeba575099dd12926420d8fd0425d +2 -0
  104. data/test/examples/lotr.git/objects/96/97dc65e095658bbd1b8e8678e08881e86d32f1 +0 -0
  105. data/test/examples/lotr.git/objects/a3/1ca2a7c352c92531a8b99815d15843b259e814 +0 -0
  106. data/test/examples/lotr.git/objects/a6/59b3763b822dd97544621fd0beef162ea37b14 +4 -0
  107. data/test/examples/lotr.git/objects/a8/ad3c09dd842a3517085bfadd37718856dee813 +0 -0
  108. data/test/examples/lotr.git/objects/aa/b61fe89d56f8614c0a8151da34f939dcedfa68 +0 -0
  109. data/test/examples/lotr.git/objects/bc/4b5fc0ce2c2ba3acef6647e4f67256ee45ab60 +0 -0
  110. data/test/examples/lotr.git/objects/c3/b43e9f08966b088e7a0192e436b7a884542e05 +0 -0
  111. data/test/examples/lotr.git/objects/dc/596d6b2dd89ab05c66f4abd7d5eb706bc17f19 +0 -0
  112. data/test/examples/lotr.git/objects/ec/da3205bee14520aab5a7bb307392064b938e83 +0 -0
  113. data/test/examples/lotr.git/objects/f4/84ebb1f40f8eb20d1bcd8d1d71934d2b8ae961 +0 -0
  114. data/test/examples/lotr.git/objects/fa/e7ef5344202bba4129abdc13060d9297d99465 +3 -0
  115. data/test/examples/lotr.git/objects/info/packs +2 -0
  116. data/test/examples/lotr.git/objects/pack/pack-dcbeaf3f6ff6c5eb08ea2b0a2d83626e8763546b.idx +0 -0
  117. data/test/examples/lotr.git/objects/pack/pack-dcbeaf3f6ff6c5eb08ea2b0a2d83626e8763546b.pack +0 -0
  118. data/test/examples/lotr.git/packed-refs +2 -0
  119. data/test/examples/lotr.git/refs/heads/master +1 -0
  120. data/test/examples/lotr.git/refs/remotes/origin/HEAD +1 -0
  121. data/test/examples/page_file_dir.git/COMMIT_EDITMSG +1 -0
  122. data/test/examples/page_file_dir.git/HEAD +1 -0
  123. data/test/examples/page_file_dir.git/config +6 -0
  124. data/test/examples/page_file_dir.git/description +1 -0
  125. data/test/examples/page_file_dir.git/index +0 -0
  126. data/test/examples/page_file_dir.git/info/exclude +6 -0
  127. data/test/examples/page_file_dir.git/logs/HEAD +1 -0
  128. data/test/examples/page_file_dir.git/logs/refs/heads/master +1 -0
  129. data/test/examples/page_file_dir.git/objects/0c/7d27db1f575263efdcab3dc650f4502a2dbcbf +0 -0
  130. data/test/examples/page_file_dir.git/objects/22/b404803c966dd92865614d86ff22ca12e50c1e +0 -0
  131. data/test/examples/page_file_dir.git/objects/25/7cc5642cb1a054f08cc83f2d943e56fd3ebe99 +0 -0
  132. data/test/examples/page_file_dir.git/objects/57/16ca5987cbf97d6bb54920bea6adde242d87e6 +0 -0
  133. data/test/examples/page_file_dir.git/objects/5b/43e14e0a15fb6f08feab1773d1c0991e9f71e2 +0 -0
  134. data/test/examples/page_file_dir.git/refs/heads/master +1 -0
  135. data/test/examples/revert.git/COMMIT_EDITMSG +1 -0
  136. data/test/examples/revert.git/HEAD +1 -0
  137. data/test/examples/revert.git/config +12 -0
  138. data/test/examples/revert.git/description +1 -0
  139. data/test/examples/revert.git/index +0 -0
  140. data/test/examples/revert.git/info/exclude +6 -0
  141. data/test/examples/revert.git/logs/HEAD +2 -0
  142. data/test/examples/revert.git/logs/refs/heads/master +2 -0
  143. data/test/examples/revert.git/objects/20/2ced67cea93c7b6bd2928aa1daef8d1d55a20d +0 -0
  144. data/test/examples/revert.git/objects/41/76394bfa11222363c66ce7e84b5f154095b6d9 +0 -0
  145. data/test/examples/revert.git/objects/6a/69f92020f5df77af6e8813ff1232493383b708 +0 -0
  146. data/test/examples/revert.git/objects/b4/785957bc986dc39c629de9fac9df46972c00fc +0 -0
  147. data/test/examples/revert.git/objects/f4/03b791119f8232b7cb0ba455c624ac6435f433 +0 -0
  148. data/test/examples/revert.git/objects/info/packs +2 -0
  149. data/test/examples/revert.git/objects/pack/pack-a561f8437234f74d0bacb9e0eebe52d207f5770d.idx +0 -0
  150. data/test/examples/revert.git/objects/pack/pack-a561f8437234f74d0bacb9e0eebe52d207f5770d.pack +0 -0
  151. data/test/examples/revert.git/packed-refs +2 -0
  152. data/test/examples/revert.git/refs/heads/master +1 -0
  153. data/test/examples/revert.git/refs/remotes/origin/HEAD +1 -0
  154. data/test/examples/yubiwa.git/HEAD +1 -0
  155. data/test/examples/yubiwa.git/config +5 -0
  156. data/test/examples/yubiwa.git/description +1 -0
  157. data/test/examples/yubiwa.git/info/exclude +6 -0
  158. data/test/examples/yubiwa.git/objects/10/fa2ddc4e3b4009d8a453aace10bd6148c1ad00 +0 -0
  159. data/test/examples/yubiwa.git/objects/52/4b82874327ea7cbf730389964ba7cb3de966de +0 -0
  160. data/test/examples/yubiwa.git/objects/58/3fc201cb457fb3f1480f3e1e5999b119633835 +0 -0
  161. data/test/examples/yubiwa.git/objects/87/bc1dd46ab3d3874d4e898d45dd512cc20a7cc8 +1 -0
  162. data/test/examples/yubiwa.git/objects/89/64ed1b4e21aa90e831763bbce9034bfda81b70 +0 -0
  163. data/test/examples/yubiwa.git/objects/9f/f6dd0660da5fba2d3374adb2b84fa653bb538b +0 -0
  164. data/test/examples/yubiwa.git/objects/ac/e97abf2b177815a1972d7db22f229f58c83309 +0 -0
  165. data/test/examples/yubiwa.git/objects/b1/f443863a4816628807fbf86141ebef055dda34 +0 -0
  166. data/test/examples/yubiwa.git/refs/heads/master +1 -0
  167. data/test/helper.rb +66 -0
  168. data/test/test_app.rb +169 -0
  169. data/test/test_committer.rb +64 -0
  170. data/test/test_file.rb +27 -0
  171. data/test/test_git_access.rb +52 -0
  172. data/test/test_markup.rb +628 -0
  173. data/test/test_page.rb +166 -0
  174. data/test/test_page_revert.rb +45 -0
  175. data/test/test_wiki.rb +462 -0
  176. metadata +470 -0
@@ -0,0 +1,489 @@
1
+ require 'digest/sha1'
2
+ require 'cgi'
3
+ require 'pygments'
4
+ require 'base64'
5
+
6
+ module Gollum
7
+
8
+ class Markup
9
+ # Initialize a new Markup object.
10
+ #
11
+ # page - The Gollum::Page.
12
+ #
13
+ # Returns a new Gollum::Markup object, ready for rendering.
14
+ def initialize(page)
15
+ @wiki = page.wiki
16
+ @name = page.filename
17
+ @data = page.text_data
18
+ @version = page.version.id if page.version
19
+ @format = page.format
20
+ @dir = ::File.dirname(page.path)
21
+ @tagmap = {}
22
+ @codemap = {}
23
+ @texmap = {}
24
+ @wsdmap = {}
25
+ @premap = {}
26
+ end
27
+
28
+ # Render the content with Gollum wiki syntax on top of the file's own
29
+ # markup language.
30
+ #
31
+ # no_follow - Boolean that determines if rel="nofollow" is added to all
32
+ # <a> tags.
33
+ # encoding - Encoding Constant or String.
34
+ #
35
+ # Returns the formatted String content.
36
+ def render(no_follow = false, encoding = nil)
37
+ sanitize = no_follow ?
38
+ @wiki.history_sanitizer :
39
+ @wiki.sanitizer
40
+
41
+ data = extract_tex(@data.dup)
42
+ data = extract_code(data)
43
+ data = extract_wsd(data)
44
+ data = extract_tags(data)
45
+ begin
46
+ data = GitHub::Markup.render(@name, data)
47
+ if data.nil?
48
+ raise "There was an error converting #{@name} to HTML."
49
+ end
50
+ rescue Object => e
51
+ data = %{<p class="gollum-error">#{e.message}</p>}
52
+ end
53
+ data = process_tags(data)
54
+ data = process_code(data, encoding)
55
+ if sanitize || block_given?
56
+ doc = Nokogiri::HTML::DocumentFragment.parse(data)
57
+ doc = sanitize.clean_node!(doc) if sanitize
58
+ yield doc if block_given?
59
+ data = doc.to_html
60
+ end
61
+ data = process_tex(data)
62
+ data = process_wsd(data)
63
+ data.gsub!(/<p><\/p>/, '')
64
+ data
65
+ end
66
+
67
+ #########################################################################
68
+ #
69
+ # TeX
70
+ #
71
+ #########################################################################
72
+
73
+ # Extract all TeX into the texmap and replace with placeholders.
74
+ #
75
+ # data - The raw String data.
76
+ #
77
+ # Returns the placeholder'd String data.
78
+ def extract_tex(data)
79
+ data.gsub(/\\\[\s*(.*?)\s*\\\]/m) do
80
+ tag = CGI.escapeHTML($1)
81
+ id = Digest::SHA1.hexdigest(tag)
82
+ @texmap[id] = [:block, tag]
83
+ id
84
+ end.gsub(/\\\(\s*(.*?)\s*\\\)/m) do
85
+ tag = CGI.escapeHTML($1)
86
+ id = Digest::SHA1.hexdigest(tag)
87
+ @texmap[id] = [:inline, tag]
88
+ id
89
+ end
90
+ end
91
+
92
+ # Process all TeX from the texmap and replace the placeholders with the
93
+ # final markup.
94
+ #
95
+ # data - The String data (with placeholders).
96
+ #
97
+ # Returns the marked up String data.
98
+ def process_tex(data)
99
+ @texmap.each do |id, spec|
100
+ type, tex = *spec
101
+ out = %{<img src="#{::File.join(@wiki.base_path, '_tex.png')}?type=#{type}&data=#{Base64.encode64(tex).chomp}" alt="#{CGI.escapeHTML(tex)}">}
102
+ data.gsub!(id, out)
103
+ end
104
+ data
105
+ end
106
+
107
+ #########################################################################
108
+ #
109
+ # Tags
110
+ #
111
+ #########################################################################
112
+
113
+ # Extract all tags into the tagmap and replace with placeholders.
114
+ #
115
+ # data - The raw String data.
116
+ #
117
+ # Returns the placeholder'd String data.
118
+ def extract_tags(data)
119
+ data.gsub!(/(.?)\[\[(.+?)\]\]([^\[]?)/m) do
120
+ if $1 == "'" && $3 != "'"
121
+ "[[#{$2}]]#{$3}"
122
+ elsif $2.include?('][')
123
+ if $2[0..4] == 'file:'
124
+ pre = $1
125
+ post = $3
126
+ parts = $2.split('][')
127
+ parts[0][0..4] = ""
128
+ link = "#{parts[1]}|#{parts[0].sub(/\.org/,'')}"
129
+ id = Digest::SHA1.hexdigest(link)
130
+ @tagmap[id] = link
131
+ "#{pre}#{id}#{post}"
132
+ else
133
+ $&
134
+ end
135
+ else
136
+ id = Digest::SHA1.hexdigest($2)
137
+ @tagmap[id] = $2
138
+ "#{$1}#{id}#{$3}"
139
+ end
140
+ end
141
+ data
142
+ end
143
+
144
+ # Process all tags from the tagmap and replace the placeholders with the
145
+ # final markup.
146
+ #
147
+ # data - The String data (with placeholders).
148
+ #
149
+ # Returns the marked up String data.
150
+ def process_tags(data)
151
+ @tagmap.each do |id, tag|
152
+ data.gsub!(id, process_tag(tag))
153
+ end
154
+ data
155
+ end
156
+
157
+ # Process a single tag into its final HTML form.
158
+ #
159
+ # tag - The String tag contents (the stuff inside the double
160
+ # brackets).
161
+ #
162
+ # Returns the String HTML version of the tag.
163
+ def process_tag(tag)
164
+ if html = process_image_tag(tag)
165
+ html
166
+ elsif html = process_file_link_tag(tag)
167
+ html
168
+ else
169
+ process_page_link_tag(tag)
170
+ end
171
+ end
172
+
173
+ # Attempt to process the tag as an image tag.
174
+ #
175
+ # tag - The String tag contents (the stuff inside the double brackets).
176
+ #
177
+ # Returns the String HTML if the tag is a valid image tag or nil
178
+ # if it is not.
179
+ def process_image_tag(tag)
180
+ parts = tag.split('|')
181
+ return if parts.size.zero?
182
+
183
+ name = parts[0].strip
184
+ path = if file = find_file(name)
185
+ ::File.join @wiki.base_path, file.path
186
+ elsif name =~ /^https?:\/\/.+(jpg|png|gif|svg|bmp)$/i
187
+ name
188
+ end
189
+
190
+ if path
191
+ opts = parse_image_tag_options(tag)
192
+
193
+ containered = false
194
+
195
+ classes = [] # applied to whatever the outermost container is
196
+ attrs = [] # applied to the image
197
+
198
+ align = opts['align']
199
+ if opts['float']
200
+ containered = true
201
+ align ||= 'left'
202
+ if %w{left right}.include?(align)
203
+ classes << "float-#{align}"
204
+ end
205
+ elsif %w{top texttop middle absmiddle bottom absbottom baseline}.include?(align)
206
+ attrs << %{align="#{align}"}
207
+ elsif align
208
+ if %w{left center right}.include?(align)
209
+ containered = true
210
+ classes << "align-#{align}"
211
+ end
212
+ end
213
+
214
+ if width = opts['width']
215
+ if width =~ /^\d+(\.\d+)?(em|px)$/
216
+ attrs << %{width="#{width}"}
217
+ end
218
+ end
219
+
220
+ if height = opts['height']
221
+ if height =~ /^\d+(\.\d+)?(em|px)$/
222
+ attrs << %{height="#{height}"}
223
+ end
224
+ end
225
+
226
+ if alt = opts['alt']
227
+ attrs << %{alt="#{alt}"}
228
+ end
229
+
230
+ attr_string = attrs.size > 0 ? attrs.join(' ') + ' ' : ''
231
+
232
+ if opts['frame'] || containered
233
+ classes << 'frame' if opts['frame']
234
+ %{<span class="#{classes.join(' ')}">} +
235
+ %{<span>} +
236
+ %{<img src="#{path}" #{attr_string}/>} +
237
+ (alt ? %{<span>#{alt}</span>} : '') +
238
+ %{</span>} +
239
+ %{</span>}
240
+ else
241
+ %{<img src="#{path}" #{attr_string}/>}
242
+ end
243
+ end
244
+ end
245
+
246
+ # Parse any options present on the image tag and extract them into a
247
+ # Hash of option names and values.
248
+ #
249
+ # tag - The String tag contents (the stuff inside the double brackets).
250
+ #
251
+ # Returns the options Hash:
252
+ # key - The String option name.
253
+ # val - The String option value or true if it is a binary option.
254
+ def parse_image_tag_options(tag)
255
+ tag.split('|')[1..-1].inject({}) do |memo, attr|
256
+ parts = attr.split('=').map { |x| x.strip }
257
+ memo[parts[0]] = (parts.size == 1 ? true : parts[1])
258
+ memo
259
+ end
260
+ end
261
+
262
+ # Attempt to process the tag as a file link tag.
263
+ #
264
+ # tag - The String tag contents (the stuff inside the double
265
+ # brackets).
266
+ #
267
+ # Returns the String HTML if the tag is a valid file link tag or nil
268
+ # if it is not.
269
+ def process_file_link_tag(tag)
270
+ parts = tag.split('|')
271
+ return if parts.size.zero?
272
+
273
+ name = parts[0].strip
274
+ path = parts[1] && parts[1].strip
275
+ path = if path && file = find_file(path)
276
+ ::File.join @wiki.base_path, file.path
277
+ elsif path =~ %r{^https?://}
278
+ path
279
+ else
280
+ nil
281
+ end
282
+
283
+ if name && path && file
284
+ %{<a href="#{::File.join @wiki.base_path, file.path}">#{name}</a>}
285
+ elsif name && path
286
+ %{<a href="#{path}">#{name}</a>}
287
+ else
288
+ nil
289
+ end
290
+ end
291
+
292
+ # Attempt to process the tag as a page link tag.
293
+ #
294
+ # tag - The String tag contents (the stuff inside the double
295
+ # brackets).
296
+ #
297
+ # Returns the String HTML if the tag is a valid page link tag or nil
298
+ # if it is not.
299
+ def process_page_link_tag(tag)
300
+ parts = tag.split('|')
301
+ parts.reverse! if @format == :mediawiki
302
+
303
+ name, page_name = *parts.compact.map(&:strip)
304
+ cname = @wiki.page_class.cname(page_name || name)
305
+
306
+ if name =~ %r{^https?://} && page_name.nil?
307
+ %{<a href="#{name}">#{name}</a>}
308
+ else
309
+ presence = "absent"
310
+ link_name = cname
311
+ page, extra = find_page_from_name(cname)
312
+ if page
313
+ link_name = @wiki.page_class.cname(page.name)
314
+ presence = "present"
315
+ end
316
+ link = ::File.join(@wiki.base_path, CGI.escape(link_name))
317
+ %{<a class="internal #{presence}" href="#{link}#{extra}">#{name}</a>}
318
+ end
319
+ end
320
+
321
+ # Find the given file in the repo.
322
+ #
323
+ # name - The String absolute or relative path of the file.
324
+ #
325
+ # Returns the Gollum::File or nil if none was found.
326
+ def find_file(name)
327
+ if name =~ /^\//
328
+ @wiki.file(name[1..-1], @version)
329
+ else
330
+ path = @dir == '.' ? name : ::File.join(@dir, name)
331
+ @wiki.file(path, @version)
332
+ end
333
+ end
334
+
335
+ # Find a page from a given cname. If the page has an anchor (#) and has
336
+ # no match, strip the anchor and try again.
337
+ #
338
+ # cname - The String canonical page name.
339
+ #
340
+ # Returns a Gollum::Page instance if a page is found, or an Array of
341
+ # [Gollum::Page, String extra] if a page without the extra anchor data
342
+ # is found.
343
+ def find_page_from_name(cname)
344
+ if page = @wiki.page(cname)
345
+ return page
346
+ end
347
+ if pos = cname.index('#')
348
+ [@wiki.page(cname[0...pos]), cname[pos..-1]]
349
+ end
350
+ end
351
+
352
+ #########################################################################
353
+ #
354
+ # Code
355
+ #
356
+ #########################################################################
357
+
358
+ # Extract all code blocks into the codemap and replace with placeholders.
359
+ #
360
+ # data - The raw String data.
361
+ #
362
+ # Returns the placeholder'd String data.
363
+ def extract_code(data)
364
+ data.gsub!(/^([ \t]*)``` ?([^\r\n]+)?\r?\n(.+?)\r?\n\1```\r?$/m) do
365
+ id = Digest::SHA1.hexdigest("#{$2}.#{$3}")
366
+ cached = check_cache(:code, id)
367
+ @codemap[id] = cached ?
368
+ { :output => cached } :
369
+ { :lang => $2, :code => $3, :indent => $1 }
370
+ "#{$1}#{id}" # print the SHA1 ID with the proper indentation
371
+ end
372
+ data
373
+ end
374
+
375
+ # Remove the leading space from a code block. Leading space
376
+ # is only removed if every single line in the block has leading
377
+ # whitespace.
378
+ #
379
+ # code - The code block to remove spaces from
380
+ # regex - A regex to match whitespace
381
+ def remove_leading_space(code, regex)
382
+ if code.lines.all? { |line| line =~ /\A\r?\n\Z/ || line =~ regex }
383
+ code.gsub!(regex, '')
384
+ end
385
+ end
386
+
387
+ # Process all code from the codemap and replace the placeholders with the
388
+ # final HTML.
389
+ #
390
+ # data - The String data (with placeholders).
391
+ # encoding - Encoding Constant or String.
392
+ #
393
+ # Returns the marked up String data.
394
+ def process_code(data, encoding = nil)
395
+ return data if data.nil? || data.size.zero? || @codemap.size.zero?
396
+
397
+ blocks = []
398
+ @codemap.each do |id, spec|
399
+ next if spec[:output] # cached
400
+
401
+ code = spec[:code]
402
+
403
+ remove_leading_space(code, /^#{spec[:indent]}/m)
404
+ remove_leading_space(code, /^( |\t)/m)
405
+
406
+ blocks << [spec[:lang], code]
407
+ end
408
+
409
+ highlighted = begin
410
+ encoding ||= 'utf-8'
411
+ blocks.map { |lang, code|
412
+ Pygments.highlight(code, :lexer => lang, :options => {:encoding => encoding.to_s})
413
+ }
414
+ rescue ::RubyPython::PythonError
415
+ []
416
+ end
417
+
418
+ @codemap.each do |id, spec|
419
+ body = spec[:output] || begin
420
+ if (body = highlighted.shift.to_s).size > 0
421
+ update_cache(:code, id, body)
422
+ body
423
+ else
424
+ "<pre><code>#{CGI.escapeHTML(spec[:code])}</code></pre>"
425
+ end
426
+ end
427
+ data.gsub!(id, body)
428
+ end
429
+
430
+ data
431
+ end
432
+
433
+ #########################################################################
434
+ #
435
+ # Sequence Diagrams
436
+ #
437
+ #########################################################################
438
+
439
+ # Extract all sequence diagram blocks into the wsdmap and replace with
440
+ # placeholders.
441
+ #
442
+ # data - The raw String data.
443
+ #
444
+ # Returns the placeholder'd String data.
445
+ def extract_wsd(data)
446
+ data.gsub(/^\{\{\{ ?(.+?)\r?\n(.+?)\r?\n\}\}\}\r?$/m) do
447
+ id = Digest::SHA1.hexdigest($2)
448
+ @wsdmap[id] = { :style => $1, :code => $2 }
449
+ id
450
+ end
451
+ end
452
+
453
+ # Process all diagrams from the wsdmap and replace the placeholders with
454
+ # the final HTML.
455
+ #
456
+ # data - The String data (with placeholders).
457
+ #
458
+ # Returns the marked up String data.
459
+ def process_wsd(data)
460
+ @wsdmap.each do |id, spec|
461
+ style = spec[:style]
462
+ code = spec[:code]
463
+ data.gsub!(id, Gollum::WebSequenceDiagram.new(code, style).to_tag)
464
+ end
465
+ data
466
+ end
467
+
468
+ # Hook for getting the formatted value of extracted tag data.
469
+ #
470
+ # type - Symbol value identifying what type of data is being extracted.
471
+ # id - String SHA1 hash of original extracted tag data.
472
+ #
473
+ # Returns the String cached formatted data, or nil.
474
+ def check_cache(type, id)
475
+ end
476
+
477
+ # Hook for caching the formatted value of extracted tag data.
478
+ #
479
+ # type - Symbol value identifying what type of data is being extracted.
480
+ # id - String SHA1 hash of original extracted tag data.
481
+ # data - The String formatted value to be cached.
482
+ #
483
+ # Returns nothing.
484
+ def update_cache(type, id, data)
485
+ end
486
+ end
487
+
488
+ MarkupGFM = Markup
489
+ end