junebug-wiki 0.0.25 → 0.0.26

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.
@@ -1,3 +1,8 @@
1
+ = 0.0.26 2006-12-08
2
+
3
+ * Feed cleanup, fixes id issue with google reader
4
+ * Removed dependency on Redcloth. Added latest (128) revision of Redcloth to ext dir to fix autolinking issue.
5
+
1
6
  = 0.0.25 2006-12-01
2
7
 
3
8
  * Feed page url wrong. Fixes #6983. Thanks Robert Gogolok
@@ -19,6 +19,12 @@ lib/junebug/controllers.rb
19
19
  lib/junebug/ext/acts_as_versioned.rb
20
20
  lib/junebug/ext/diff.rb
21
21
  lib/junebug/ext/mosquito.rb
22
+ lib/junebug/ext/redcloth.rb
23
+ lib/junebug/ext/redcloth/all_formats.rb
24
+ lib/junebug/ext/redcloth/base.rb
25
+ lib/junebug/ext/redcloth/docbook.rb
26
+ lib/junebug/ext/redcloth/markdown.rb
27
+ lib/junebug/ext/redcloth/textile.rb
22
28
  lib/junebug/generator.rb
23
29
  lib/junebug/helpers.rb
24
30
  lib/junebug/models.rb
@@ -1,3 +1,18 @@
1
+ = 0.0.26 2006-12-18
2
+
3
+ A couple of changes to the config.yml parameters were made as part of the feed cleanup. If you have an existing Junebug wiki, you may need to make the following changes:
4
+
5
+ * feedurl is now the url of the public feed, whether through a proxy, or using a feed hosting service.
6
+ * siteurl is the base url of the wiki. The feed generator will use this to point to the feed entries to the wiki.
7
+
8
+ Both of these parameters are now optional, so if you just serve your site locally, just leave them commented out.
9
+
10
+ Example: for www.junebugwiki.com, which is served through an apache proxy, I'm using the following:
11
+
12
+ feedurl: http://www.junebugwiki.com/all/feed
13
+ siteurl: http://www.junebugwiki.com
14
+
15
+
1
16
  = 0.0.23 2006-11-28
2
17
 
3
18
  Robert Gogolok rightly pointed out that the 'url' parameter in config.yml was confusing, so I renamed it 'feedurl'. You will need to make this change to any existing junebug wikis.
data/Rakefile CHANGED
@@ -49,7 +49,6 @@ hoe = Hoe.new(GEM_NAME, VERS) do |p|
49
49
  p.extra_deps = [
50
50
  ['mongrel', '>=0.3.13.3'],
51
51
  ['camping', '>=1.5'],
52
- ['RedCloth', '>=3.0.4'],
53
52
  ['daemons', '>=1.0.3'],
54
53
  ['sqlite3-ruby', '>=1.1.0.1'],
55
54
  ['activerecord', '>=1.14.4']
@@ -2,7 +2,7 @@
2
2
  startpage: Welcome_to_Junebug
3
3
 
4
4
  # server configuration
5
- host: 0.0.0.0
5
+ host: 127.0.0.1
6
6
  port: 3301
7
7
 
8
8
  # if you want to have the wiki root running on something other than /
@@ -30,8 +30,16 @@ dbconnection:
30
30
  # host: localhost
31
31
 
32
32
  # configuring rss
33
- # you should only need to change this is you have an external feed manager like feedburner
34
33
  feedtitle: Wiki Updates
35
- feedurl: http://localhost:3301
36
- feed: http://localhost:3301/all/feed
34
+ #
35
+ # Optional feed parameters:
36
+ #
37
+ # Example proxy config:
38
+ #feedurl: http://www.mywiki.com/all/feed
39
+ #siteurl: http://www.mywiki.com
40
+ #
41
+ # Example feedburner config:
42
+ #feedurl: http://feeds.feedburner.com/MyWiki
43
+ #siteurl: http://www.mywiki.com
44
+
37
45
 
@@ -128,7 +128,9 @@ module Junebug::Controllers
128
128
  class Feed < R '/all/feed'
129
129
  def get
130
130
  @headers['Content-Type'] = 'application/xml'
131
- return Junebug::Views.feed
131
+ @skip_layout = true
132
+ render :feed
133
+ #return Junebug::Views.feed
132
134
  end
133
135
  end
134
136
 
@@ -0,0 +1,5 @@
1
+ $:.unshift(File.dirname(__FILE__))
2
+
3
+ require 'redcloth/base'
4
+ require 'redcloth/textile'
5
+ require 'redcloth/markdown'
@@ -0,0 +1,4 @@
1
+ $:.unshift(File.dirname(__FILE__) + "/../")
2
+
3
+ require 'redcloth'
4
+ require 'redcloth/docbook'
@@ -0,0 +1,674 @@
1
+ class RedCloth < String
2
+
3
+ VERSION = '3.0.4'
4
+ DEFAULT_RULES = [] # let each class add to this array
5
+ TEXTILE_RULES = [:refs_textile, :block_textile_table, :block_textile_lists, :block_textile_defs,
6
+ :block_textile_prefix, :inline_textile_image, :inline_textile_link,
7
+ :inline_textile_code, :inline_textile_span, :glyphs_textile,
8
+ :inline_textile_autolink_urls, :inline_textile_autolink_emails]
9
+ MARKDOWN_RULES = [:refs_markdown, :block_markdown_setext, :block_markdown_atx, :block_markdown_rule,
10
+ :block_markdown_bq, :block_markdown_lists,
11
+ :inline_markdown_reflink, :inline_markdown_link]
12
+ DOCBOOK_RULES = [:refs_docbook, :block_docbook_table, :block_docbook_lists, :block_docbook_simple_lists,
13
+ :block_docbook_defs, :block_docbook_prefix, :inline_docbook_image, :inline_docbook_link,
14
+ :inline_docbook_code, :inline_docbook_glyphs, :inline_docbook_span,
15
+ :inline_docbook_wiki_words, :inline_docbook_wiki_links, :inline_docbook_autolink_urls,
16
+ :inline_docbook_autolink_emails]
17
+ @@escape_keyword ||= "redcloth"
18
+
19
+ #
20
+ # Two accessor for setting security restrictions.
21
+ #
22
+ # This is a nice thing if you're using RedCloth for
23
+ # formatting in public places (e.g. Wikis) where you
24
+ # don't want users to abuse HTML for bad things.
25
+ #
26
+ # If +:filter_html+ is set, HTML which wasn't
27
+ # created by the Textile processor will be escaped.
28
+ #
29
+ # If +:filter_styles+ is set, it will also disable
30
+ # the style markup specifier. ('{color: red}')
31
+ #
32
+ # If +:filter_classes+ is set, it will also disable
33
+ # class attributes. ('!(classname)image!')
34
+ #
35
+ # If +:filter_ids+ is set, it will also disable
36
+ # id attributes. ('!(classname#id)image!')
37
+ #
38
+ attr_accessor :filter_html, :filter_styles, :filter_classes, :filter_ids
39
+
40
+ #
41
+ # Accessor for toggling hard breaks.
42
+ #
43
+ # If +:hard_breaks+ is set, single newlines will
44
+ # be converted to HTML break tags. This is the
45
+ # default behavior for traditional RedCloth.
46
+ #
47
+ attr_accessor :hard_breaks
48
+
49
+ # Accessor for toggling lite mode.
50
+ #
51
+ # In lite mode, block-level rules are ignored. This means
52
+ # that tables, paragraphs, lists, and such aren't available.
53
+ # Only the inline markup for bold, italics, entities and so on.
54
+ #
55
+ # r = RedCloth.new( "And then? She *fell*!", [:lite_mode] )
56
+ # r.to_html
57
+ # #=> "And then? She <strong>fell</strong>!"
58
+ #
59
+ attr_accessor :lite_mode
60
+
61
+ #
62
+ # Accessor for toggling span caps.
63
+ #
64
+ # Textile places `span' tags around capitalized
65
+ # words by default, but this wreaks havoc on Wikis.
66
+ # If +:no_span_caps+ is set, this will be
67
+ # suppressed.
68
+ #
69
+ attr_accessor :no_span_caps
70
+
71
+ #
72
+ # Establishes the markup predence.
73
+ #
74
+ attr_accessor :rules
75
+
76
+ # Returns a new RedCloth object, based on _string_ and
77
+ # enforcing all the included _restrictions_.
78
+ #
79
+ # r = RedCloth.new( "h1. A <b>bold</b> man", [:filter_html] )
80
+ # r.to_html
81
+ # #=>"<h1>A &lt;b&gt;bold&lt;/b&gt; man</h1>"
82
+ #
83
+ def initialize( string, restrictions = [] )
84
+ restrictions.each { |r| method( "#{ r }=" ).call( true ) }
85
+ super( string )
86
+ end
87
+
88
+ #
89
+ # Generates HTML from the Textile contents.
90
+ #
91
+ # r = RedCloth.new( "And then? She *fell*!" )
92
+ # r.to_html( true )
93
+ # #=>"And then? She <strong>fell</strong>!"
94
+ #
95
+ def to_html( *rules )
96
+ rules = DEFAULT_RULES if rules.empty?
97
+ # make our working copy
98
+ text = self.dup
99
+
100
+ return "" if text == ""
101
+
102
+ @urlrefs = {}
103
+ @shelf = []
104
+ @rules = rules.collect do |rule|
105
+ case rule
106
+ when :markdown
107
+ MARKDOWN_RULES
108
+ when :textile
109
+ TEXTILE_RULES
110
+ else
111
+ rule
112
+ end
113
+ end.flatten
114
+
115
+ # standard clean up
116
+ @pre_list = []
117
+ pre_process text
118
+ DEFAULT_RULES.each {|ruleset| send("#{ruleset}_pre_process", text) if private_methods.include? "#{ruleset}_pre_process"}
119
+ incoming_entities text
120
+ clean_white_space text
121
+
122
+ # start processor
123
+ no_textile text
124
+ rip_offtags text
125
+ hard_break text
126
+ unless @lite_mode
127
+ refs text
128
+ blocks text
129
+ end
130
+ inline text
131
+ smooth_offtags text
132
+ retrieve text
133
+
134
+ post_process text
135
+ DEFAULT_RULES.each {|ruleset| send("#{ruleset}_post_process", text) if private_methods.include? "#{ruleset}_post_process"}
136
+
137
+ clean_html text if filter_html
138
+
139
+ return text.strip
140
+
141
+ end
142
+
143
+ #######
144
+ private
145
+ #######
146
+ #
147
+ # Regular expressions to convert to HTML.
148
+ #
149
+ LB = "0docbook0line0break0"
150
+ NB = "0docbook0no0break0\n\n"
151
+ A_HLGN = /(?:(?:<>|<|>|\=|[()]+)+)/
152
+ A_VLGN = /[\-^~]/
153
+ C_CLAS = '(?:\([^)]+\))'
154
+ C_LNGE = '(?:\[[^\]]+\])'
155
+ C_STYL = '(?:\{[^}]+\})'
156
+ S_CSPN = '(?:\\\\\d+)'
157
+ S_RSPN = '(?:/\d+)'
158
+ A = "(?:#{A_HLGN}?#{A_VLGN}?|#{A_VLGN}?#{A_HLGN}?)"
159
+ S = "(?:#{S_CSPN}?#{S_RSPN}|#{S_RSPN}?#{S_CSPN}?)"
160
+ C = "(?:#{C_CLAS}?#{C_STYL}?#{C_LNGE}?|#{C_STYL}?#{C_LNGE}?#{C_CLAS}?|#{C_LNGE}?#{C_STYL}?#{C_CLAS}?)"
161
+ PUNCT = Regexp::quote( '!"#$%&\'*+,-./:;=?@\\^_`|~' )
162
+ PUNCT_NOQ = Regexp::quote( '!"#$&\',./:;=?@\\`|' )
163
+ PUNCT_Q = Regexp::quote( '*-_+^~%' )
164
+ HYPERLINK = '(\S+?)([^\w\s/;=\?]*?)(?=\s|<|$)'
165
+
166
+ TABLE_RE = /^(?:caption ?\{(.*?)\}\. ?\n)?^(?:id ?\{(.*?)\}\. ?\n)?^(?:table(_?#{S}#{A}#{C})\. ?\n)?^(#{A}#{C}\.? ?\|.*?\|)(\n\n|\Z)/m
167
+ LISTS_RE = /^([#*_0-9]+?#{C} .*?)$(?![^#*])/m
168
+ LISTS_CONTENT_RE = /^([#*]+)([_0-9]*)(#{A}#{C}) (.*)$/m
169
+ DEFS_RE = /^(-#{C}\s.*?\:\=.*?)$(?![^-])/m
170
+ DEFS_CONTENT_RE = /^(-)(#{A}#{C})\s+(.*?):=(.*)$/m
171
+ BACKTICK_CODE_RE = /(.*?)
172
+ ```
173
+ (?:\|(\w+?)\|)?
174
+ (.*?[^\\])
175
+ ```
176
+ (.*?)/mx
177
+ CODE_RE = /(.*?)
178
+ @@?
179
+ (?:\|(\w+?)\|)?
180
+ (.*?[^\\])
181
+ @@?
182
+ (.*?)/x
183
+ BLOCKS_GROUP_RE = /\n{2,}(?! )/m
184
+ BLOCK_RE = /^(([a-z]+)(\d*))(#{A}#{C})\.(?::(\S+))? (.*)$/
185
+ SETEXT_RE = /\A(.+?)\n([=-])[=-]* *$/m
186
+ ATX_RE = /\A(\#{1,6}) # $1 = string of #'s
187
+ [ ]*
188
+ (.+?) # $2 = Header text
189
+ [ ]*
190
+ \#* # optional closing #'s (not counted)
191
+ $/x
192
+ LINK_RE = /
193
+ ([\s\[{(]|[#{PUNCT}])? # $pre
194
+ " # start
195
+ (#{C}) # $atts
196
+ ([^"]+?) # $text
197
+ \s?
198
+ (?:\(([^)]+?)\)(?="))? # $title
199
+ ":
200
+ ([^\s<]+?) # $url
201
+ (\/)? # $slash
202
+ ([^\w\/;]*?) # $post
203
+ (?=<|\s|$)
204
+ /x
205
+ IMAGE_RE = /
206
+ (<p>|.|^) # start of line?
207
+ \! # opening
208
+ (\<|\=|\>)? # optional alignment atts
209
+ (#{C}) # optional style,class atts
210
+ (?:\. )? # optional dot-space
211
+ ([^\s(!]+?) # presume this is the src
212
+ \s? # optional space
213
+ (?:\(((?:[^\(\)]|\([^\)]+\))+?)\))? # optional title
214
+ \! # closing
215
+ (?::#{ HYPERLINK })? # optional href
216
+ /x
217
+
218
+ # Text markup tags, don't conflict with block tags
219
+ SIMPLE_HTML_TAGS = [
220
+ 'tt', 'b', 'i', 'big', 'small', 'em', 'strong', 'dfn', 'code',
221
+ 'samp', 'kbd', 'var', 'cite', 'abbr', 'acronym', 'a', 'img', 'br',
222
+ 'br', 'map', 'q', 'sub', 'sup', 'span', 'bdo'
223
+ ]
224
+
225
+ QTAGS = [
226
+ ['**', 'b'],
227
+ ['*', 'strong'],
228
+ ['??', 'cite', :limit],
229
+ ['-', 'del', :limit],
230
+ ['__', 'i'],
231
+ ['_', 'em', :limit],
232
+ ['%', 'span', :limit],
233
+ ['+', 'ins', :limit],
234
+ ['^', 'sup'],
235
+ ['~', 'sub']
236
+ ]
237
+ QTAGS.collect! do |rc, ht, rtype|
238
+ rcq = Regexp::quote rc
239
+ re =
240
+ case rtype
241
+ when :limit
242
+ /(\W)
243
+ (#{rcq})
244
+ (#{C})
245
+ (?::(\S+?))?
246
+ (\S.*?\S|\S)
247
+ #{rcq}
248
+ (?=\W)/x
249
+ else
250
+ /(#{rcq})
251
+ (#{C})
252
+ (?::(\S+))?
253
+ (\S.*?\S|\S)
254
+ #{rcq}/xm
255
+ end
256
+ escaped_re =
257
+ case rtype
258
+ when :limit
259
+ /(\W)
260
+ (#{@@escape_keyword}#{rcq})
261
+ (#{C})
262
+ (?::(\S+?))?
263
+ (\S.*?\S|\S)
264
+ #{rcq}#{@@escape_keyword}
265
+ (?=\W)/x
266
+ else
267
+ /(#{@@escape_keyword}#{rcq})
268
+ (#{C})
269
+ (?::(\S+))?
270
+ (\S.*?\S|\S)
271
+ #{rcq}#{@@escape_keyword}/xm
272
+ end
273
+ [rc, ht, re, rtype, escaped_re]
274
+ end
275
+
276
+ # Elements to handle
277
+ GLYPHS = [
278
+ # [ /([^\s\[{(>])?\'([dmst]\b|ll\b|ve\b|\s|:|$)/, '\1&#8217;\2' ], # single closing
279
+ [ /([^\s\[{(>#{PUNCT_Q}][#{PUNCT_Q}]*)\'/, '\1&#8217;' ], # single closing
280
+ [ /\'(?=[#{PUNCT_Q}]*(s\b|[\s#{PUNCT_NOQ}]))/, '&#8217;' ], # single closing
281
+ [ /\'/, '&#8216;' ], # single opening
282
+ # [ /([^\s\[{(])?"(\s|:|$)/, '\1&#8221;\2' ], # double closing
283
+ [ /([^\s\[{(>#{PUNCT_Q}][#{PUNCT_Q}]*)"/, '\1&#8221;' ], # double closing
284
+ [ /"(?=[#{PUNCT_Q}]*[\s#{PUNCT_NOQ}])/, '&#8221;' ], # double closing
285
+ [ /"/, '&#8220;' ], # double opening
286
+ [ /\b( )?\.{3}/, '\1&#8230;' ], # ellipsis
287
+ [ /\b([A-Z][A-Z0-9]{2,})\b(?:[(]([^)]*)[)])/, '<acronym title="\2">\1</acronym>' ], # 3+ uppercase acronym
288
+ [ /(^|[^"][>\s])([A-Z][A-Z0-9 ]+[A-Z0-9])([^<A-Za-z0-9]|$)/, '\1<span class="caps">\2</span>\3', :no_span_caps ], # 3+ uppercase caps
289
+ [ /(\.\s)?\s?--\s?/, '\1&#8212;' ], # em dash
290
+ [ /(^|\s)->(\s|$)/, ' &rarr; ' ], # right arrow
291
+ [ /(^|\s)-(\s|$)/, ' &#8211; ' ], # en dash
292
+ [ /(\d+) ?x ?(\d+)/, '\1&#215;\2' ], # dimension sign
293
+ [ /\b ?[(\[]TM[\])]/i, '&#8482;' ], # trademark
294
+ [ /\b ?[(\[]R[\])]/i, '&#174;' ], # registered
295
+ [ /\b ?[(\[]C[\])]/i, '&#169;' ] # copyright
296
+ ]
297
+
298
+ H_ALGN_VALS = {
299
+ '<' => 'left',
300
+ '=' => 'center',
301
+ '>' => 'right',
302
+ '<>' => 'justify'
303
+ }
304
+
305
+ V_ALGN_VALS = {
306
+ '^' => 'top',
307
+ '-' => 'middle',
308
+ '~' => 'bottom'
309
+ }
310
+
311
+ OFFTAGS = /(code|pre|kbd|notextile)/i
312
+ OFFTAG_MATCH = /(?:(<\/#{ OFFTAGS }>)|(<#{ OFFTAGS }[^>]*>))(.*?)(?=<\/?#{ OFFTAGS }|\Z)/mi
313
+ OFFTAG_OPEN = /<#{ OFFTAGS }/
314
+ OFFTAG_CLOSE = /<\/?#{ OFFTAGS }/
315
+
316
+ HASTAG_MATCH = /(<\/?\w[^\n]*?>)/m
317
+ ALLTAG_MATCH = /(<\/?\w[^\n]*?>)|.*?(?=<\/?\w[^\n]*?>|$)/m
318
+
319
+ def pre_process( text )
320
+ text.gsub!( /={2}\`\`\`={2}/, "XXXpreformatted_backticksXXX" )
321
+ end
322
+
323
+ def post_process( text )
324
+ text.gsub!( /XXXpreformatted_backticksXXX/, '```' )
325
+ text.gsub!( LB, "\n" )
326
+ text.gsub!( NB, "" )
327
+ text.gsub!( /<\/?notextile>/, '' )
328
+ text.gsub!( /x%x%/, '&#38;' )
329
+ text << "</div>" if @div_atts
330
+ end
331
+
332
+ # Search and replace for glyphs (quotes, dashes, other symbols)
333
+ def pgl( text )
334
+ GLYPHS.each do |re, resub, tog|
335
+ next if tog and method( tog ).call
336
+ text.gsub! re, resub
337
+ end
338
+ end
339
+
340
+ # Parses attribute lists and builds an HTML attribute string
341
+ def pba( text_in, element = "" )
342
+
343
+ return '' unless text_in
344
+
345
+ style = []
346
+ text = text_in.dup
347
+ if element == 'td'
348
+ colspan = $1 if text =~ /\\(\d+)/
349
+ rowspan = $1 if text =~ /\/(\d+)/
350
+ style << "vertical-align:#{ v_align( $& ) };" if text =~ A_VLGN
351
+ end
352
+
353
+ style << "#{ $1 };" if not filter_styles and
354
+ text.sub!( /\{([^}]*)\}/, '' )
355
+
356
+ lang = $1 if
357
+ text.sub!( /\[([^)]+?)\]/, '' )
358
+
359
+ cls = $1 if
360
+ text.sub!( /\(([^()]+?)\)/, '' )
361
+
362
+ style << "padding-left:#{ $1.length }em;" if
363
+ text.sub!( /([(]+)/, '' )
364
+
365
+ style << "padding-right:#{ $1.length }em;" if text.sub!( /([)]+)/, '' )
366
+
367
+ style << "text-align:#{ h_align( $& ) };" if text =~ A_HLGN
368
+
369
+ cls, id = $1, $2 if cls =~ /^(.*?)#(.*)$/
370
+
371
+ atts = ''
372
+ atts << " style=\"#{ style.join }\"" unless style.empty?
373
+ atts << " class=\"#{ cls }\"" unless cls.to_s.empty? or filter_classes
374
+ atts << " lang=\"#{ lang }\"" if lang
375
+ atts << " id=\"#{ id }\"" if id and not filter_ids
376
+ atts << " colspan=\"#{ colspan }\"" if colspan
377
+ atts << " rowspan=\"#{ rowspan }\"" if rowspan
378
+
379
+ atts
380
+ end
381
+
382
+ #
383
+ # Flexible HTML escaping
384
+ #
385
+ def htmlesc( str, mode )
386
+ str.gsub!( '&', '&amp;' )
387
+ str.gsub!( '"', '&quot;' ) if mode != :NoQuotes
388
+ str.gsub!( "'", '&#039;' ) if mode == :Quotes
389
+ str.gsub!( '<', '&lt;')
390
+ str.gsub!( '>', '&gt;')
391
+ end
392
+
393
+ def hard_break( text )
394
+ text.gsub!( /(.)\n(?!\n|\Z| *([#*=]+(\s|$)|[{|]))/, "\\1<br />" ) if hard_breaks
395
+ end
396
+
397
+ def lT( text )
398
+ text =~ /\#$/ ? 'o' : 'u'
399
+ end
400
+
401
+ BLOCK_GROUP_SPLITTER = "XXX_BLOCK_GROUP_XXX\n\n"
402
+ def blocks( text, deep_code = false )
403
+ @current_class ||= nil
404
+
405
+ # Find all occurences of div(class). and process them as blocks
406
+ text.gsub!( /^div\((.*?)\)\.\s*(.*?)(?=div\([^\)]+\)\.\s*)/m ) do |blk|
407
+ block_class = (@current_class == $1) ? nil : %{ class=#{$1.inspect}}
408
+ @current_class = $1
409
+ BLOCK_GROUP_SPLITTER + ( ($2.strip.empty? || block_class.nil?) ? $2 : textile_p('div', block_class, nil, "\n\n#{$2.strip}\n\n") )
410
+ end
411
+
412
+ # Take care of the very last div
413
+ text.sub!( /div\((.*?)\)\.\s*(.*)/m ) do |blk|
414
+ block_class = (@current_class == $1) ? nil : %{ class=#{$1.inspect}}
415
+ @current_class = $1
416
+ BLOCK_GROUP_SPLITTER + ( ($2.strip.empty? || block_class.nil?) ? $2 : textile_p('div', block_class, nil, "\n\n#{$2.strip}\n\n") )
417
+ end
418
+
419
+ # Handle the text now that the placeholders for divs are set, splitting at BLOCK_GROUP_SPLITTER
420
+ text.replace(text.strip.split(BLOCK_GROUP_SPLITTER.strip).map do |chunk|
421
+ block_groups(chunk, deep_code)
422
+ end.join)
423
+ end
424
+
425
+ def block_groups( text, deep_code = false )
426
+ text.replace text.split( BLOCKS_GROUP_RE ).collect { |blk| blk(blk, deep_code) }.join("\n")
427
+ end
428
+
429
+ # Surrounds blocks with paragraphs and shelves them when necessary
430
+ def blk( text, deep_code = false )
431
+ return text if text =~ /<[0-9]+>/
432
+
433
+ plain = text !~ /\A[#*> ]/
434
+
435
+ # skip blocks that are complex HTML
436
+ if text =~ /^<\/?(\w+).*>/ and not SIMPLE_HTML_TAGS.include? $1
437
+ text
438
+ else
439
+ # search for indentation levels
440
+ text.strip!
441
+ if text.empty?
442
+ text
443
+ else
444
+ code_blk = nil
445
+ text.gsub!( /((?:\n(?:\n^ +[^\n]*)+)+)/m ) do |iblk|
446
+ flush_left iblk
447
+ blocks iblk, plain
448
+ iblk.gsub( /^(\S)/, "\\1" )
449
+ if plain
450
+ code_blk = iblk; ""
451
+ else
452
+ iblk
453
+ end
454
+ end
455
+ block_applied = 0
456
+ @rules.each do |rule_name|
457
+ block_applied += 1 if ( rule_name.to_s.match /^block_/ and method( rule_name ).call( text ) )
458
+ end
459
+ if block_applied.zero?
460
+ if deep_code
461
+ text = "\t<pre><code>#{ text }</code></pre>\n"
462
+ else
463
+ text = "\t<p>#{ text }</p>\n"
464
+ end
465
+ end
466
+ # hard_break text
467
+ text << "\n#{ code_blk }"
468
+ end
469
+ return text
470
+ end
471
+
472
+ end
473
+
474
+ def refs( text )
475
+ @rules.each do |rule_name|
476
+ method( rule_name ).call( text ) if rule_name.to_s.match /^refs_/
477
+ end
478
+ end
479
+
480
+ def check_refs( text )
481
+ ret = @urlrefs[text.downcase] if text
482
+ ret || [text, nil]
483
+ end
484
+
485
+ # Puts text in storage and returns is placeholder
486
+ # e.g. shelve("some text") => <1>
487
+ def shelve( val )
488
+ @shelf << val
489
+ " <#{ @shelf.length }>"
490
+ end
491
+
492
+ # Retrieves text from storage using its placeholder
493
+ # e.g. retrieve("<1>") => "some text"
494
+ def retrieve( text )
495
+ @shelf.each_with_index do |r, i|
496
+ text.gsub!( " <#{ i + 1 }>" ){|m| r }
497
+ end
498
+ end
499
+
500
+ def incoming_entities( text )
501
+ ## turn any incoming ampersands into a dummy character for now.
502
+ ## This uses a negative lookahead for alphanumerics followed by a semicolon,
503
+ ## implying an incoming html entity, to be skipped
504
+
505
+ text.gsub!( /&(?![#a-z0-9]+;)/i, "x%x%" )
506
+ end
507
+
508
+ def clean_white_space( text )
509
+ # normalize line breaks
510
+ text.gsub!( /\r\n/, "\n" )
511
+ text.gsub!( /\r/, "\n" )
512
+ text.gsub!( /\t/, ' ' )
513
+ text.gsub!( /^ +$/, '' )
514
+ text.gsub!( /\n{3,}/, "\n\n" )
515
+ text.gsub!( /"$/, "\" " )
516
+
517
+ # if entire document is indented, flush
518
+ # to the left side
519
+ flush_left text
520
+ end
521
+
522
+ def flush_left( text )
523
+ indt = 0
524
+ if text =~ /^ /
525
+ while text !~ /^ {#{indt}}\S/
526
+ indt += 1
527
+ end unless text.empty?
528
+ if indt.nonzero?
529
+ text.gsub!( /^ {#{indt}}/, '' )
530
+ end
531
+ end
532
+ end
533
+
534
+ def footnote_ref( text )
535
+ text.gsub!( /\b\[([0-9]+?)\](\s)?/,
536
+ '<sup><a href="#fn\1">\1</a></sup>\2' )
537
+ end
538
+
539
+ def rip_offtags( text )
540
+ if text =~ /<.*>/
541
+ ## strip and encode <pre> content
542
+ codepre, used_offtags = 0, {}
543
+ text.gsub!( OFFTAG_MATCH ) do |line|
544
+ if $3
545
+ offtag, aftertag = $4, $5
546
+ codepre += 1
547
+ used_offtags[offtag] = true
548
+ if codepre - used_offtags.length > 0
549
+ htmlesc( line, :NoQuotes ) unless used_offtags['notextile']
550
+ @pre_list.last << line
551
+ line = ""
552
+ else
553
+ htmlesc( aftertag, :NoQuotes ) if aftertag and not used_offtags['notextile']
554
+ line = "<redpre##{ @pre_list.length }>"
555
+ @pre_list << "#{ $3 }#{ aftertag }"
556
+ end
557
+ elsif $1 and codepre > 0
558
+ if codepre - used_offtags.length > 0
559
+ htmlesc( line, :NoQuotes ) unless used_offtags['notextile']
560
+ @pre_list.last << line
561
+ line = ""
562
+ end
563
+ codepre -= 1 unless codepre.zero?
564
+ used_offtags = {} if codepre.zero?
565
+ end
566
+ line
567
+ end
568
+ end
569
+ text
570
+ end
571
+
572
+ def smooth_offtags( text )
573
+ unless @pre_list.empty?
574
+ ## replace <pre> content
575
+ text.gsub!( /<redpre#(\d+)>/ ) { @pre_list[$1.to_i] }
576
+ end
577
+ end
578
+
579
+ def inline( text )
580
+ [/^inline_/, /^glyphs_/].each do |meth_re|
581
+ @rules.each do |rule_name|
582
+ method( rule_name ).call( text ) if rule_name.to_s.match( meth_re )
583
+ end
584
+ end
585
+ end
586
+
587
+ def h_align( text )
588
+ H_ALGN_VALS[text]
589
+ end
590
+
591
+ def v_align( text )
592
+ V_ALGN_VALS[text]
593
+ end
594
+
595
+ # HTML cleansing stuff
596
+ BASIC_TAGS = {
597
+ 'a' => ['href', 'title'],
598
+ 'img' => ['src', 'alt', 'title'],
599
+ 'br' => [],
600
+ 'i' => nil,
601
+ 'u' => nil,
602
+ 'b' => nil,
603
+ 'pre' => nil,
604
+ 'kbd' => nil,
605
+ 'code' => ['lang'],
606
+ 'cite' => nil,
607
+ 'strong' => nil,
608
+ 'em' => nil,
609
+ 'ins' => nil,
610
+ 'sup' => nil,
611
+ 'sub' => nil,
612
+ 'del' => nil,
613
+ 'table' => nil,
614
+ 'tr' => nil,
615
+ 'td' => ['colspan', 'rowspan'],
616
+ 'th' => nil,
617
+ 'ol' => ['start'],
618
+ 'ul' => nil,
619
+ 'li' => nil,
620
+ 'p' => nil,
621
+ 'h1' => nil,
622
+ 'h2' => nil,
623
+ 'h3' => nil,
624
+ 'h4' => nil,
625
+ 'h5' => nil,
626
+ 'h6' => nil,
627
+ 'blockquote' => ['cite']
628
+ }
629
+
630
+ def clean_html( text, tags = BASIC_TAGS )
631
+ text.gsub!( /<!\[CDATA\[/, '' )
632
+ text.gsub!( /<(\/*)(\w+)([^>]*)>/ ) do
633
+ raw = $~
634
+ tag = raw[2].downcase
635
+ if tags.has_key? tag
636
+ pcs = [tag]
637
+ tags[tag].each do |prop|
638
+ ['"', "'", ''].each do |q|
639
+ q2 = ( q != '' ? q : '\s' )
640
+ if raw[3] =~ /#{prop}\s*=\s*#{q}([^#{q2}]+)#{q}/i
641
+ attrv = $1
642
+ next if (prop == 'src' or prop == 'href') and not attrv =~ %r{^(http|https|ftp):}
643
+ pcs << "#{prop}=\"#{attrv.gsub('"', '\\"')}\""
644
+ break
645
+ end
646
+ end
647
+ end if tags[tag]
648
+ "<#{raw[1]}#{pcs.join " "}>"
649
+ else
650
+ " "
651
+ end
652
+ end
653
+ end
654
+
655
+ AUTO_LINK_RE = /
656
+ ( # leading text
657
+ <\w+.*?>| # leading HTML tag, or
658
+ [^=!:'"\/]| # leading punctuation, or
659
+ ^ # beginning of line
660
+ )
661
+ (
662
+ (?:http[s]?:\/\/)| # protocol spec, or
663
+ (?:www\.) # www.*
664
+ )
665
+ (
666
+ ([\w]+[=?&:%\/\.\~\-]*)* # url segment
667
+ \w+[\/]? # url tail
668
+ (?:\#\w*)? # trailing anchor
669
+ )
670
+ ([[:punct:]]|\s|<|$) # trailing text
671
+ /x
672
+
673
+ end
674
+