read-only-gollum 1.4.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.
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