RedCloth 3.0.0 → 3.0.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of RedCloth might be problematic. Click here for more details.

Files changed (4) hide show
  1. data/bin/redcloth +1 -1
  2. data/lib/rctodb.rb +1107 -0
  3. data/lib/redcloth.rb +63 -39
  4. metadata +3 -2
@@ -1,3 +1,3 @@
1
- #!/usr/local/bin/ruby
1
+ #!/usr/local/bin/ruby18
2
2
  require 'redcloth'
3
3
  puts RedCloth.new( ARGF.read ).to_html
@@ -0,0 +1,1107 @@
1
+ # vim:ts=2:sw=4:
2
+ #
3
+ # rctodb.rb -- experimental hacking up of redcloth.rb to
4
+ # produce DocBook output.
5
+ #
6
+ # David A. Black
7
+ # 2004-2005
8
+ #
9
+ # I started this originally to help with PDF output for
10
+ # hieraki by Tobias Luetke. why then asked me to put
11
+ # it on the RubyForge site for RedCloth.
12
+ #
13
+ # My approach has been to hack at the to_html stuff
14
+ # until it produces DocBook instead of HTML. There's
15
+ # room for refinement....
16
+ #
17
+ # Simple usage:
18
+ #
19
+ # require 'rctodb.rb'
20
+ # puts RedCloth.new(File.read("redcloth_file")).to_docbook
21
+ #
22
+ # License:: same as redcloth.rb
23
+
24
+ require 'uri'
25
+
26
+ # = RedCloth
27
+ #
28
+ # RedCloth is a Ruby library for converting Textile and/or Markdown
29
+ # into HTML. You can use either format, intermingled or separately.
30
+ # You can also extend RedCloth to honor your own custom text stylings.
31
+ #
32
+ # RedCloth users are encouraged to use Textile if they are generating
33
+ # HTML and to use Markdown if others will be viewing the plain text.
34
+ #
35
+ # == What is Textile?
36
+ #
37
+ # Textile is a simple formatting style for text
38
+ # documents, loosely based on some HTML conventions.
39
+ #
40
+ # == Sample Textile Text
41
+ #
42
+ # h2. This is a title
43
+ #
44
+ # h3. This is a subhead
45
+ #
46
+ # This is a bit of paragraph.
47
+ #
48
+ # bq. This is a blockquote.
49
+ #
50
+ # = Writing Textile
51
+ #
52
+ # A Textile document consists of paragraphs. Paragraphs
53
+ # can be specially formatted by adding a small instruction
54
+ # to the beginning of the paragraph.
55
+ #
56
+ # h[n]. Header of size [n].
57
+ # bq. Blockquote.
58
+ # # Numeric list.
59
+ # * Bulleted list.
60
+ #
61
+ # == Quick Phrase Modifiers
62
+ #
63
+ # Quick phrase modifiers are also included, to allow formatting
64
+ # of small portions of text within a paragraph.
65
+ #
66
+ # \_emphasis\_
67
+ # \_\_italicized\_\_
68
+ # \*strong\*
69
+ # \*\*bold\*\*
70
+ # ??citation??
71
+ # -deleted text-
72
+ # +inserted text+
73
+ # ^superscript^
74
+ # ~subscript~
75
+ # @code@
76
+ # %(classname)span%
77
+ #
78
+ # ==notextile== (leave text alone)
79
+ #
80
+ # == Links
81
+ #
82
+ # To make a hypertext link, put the link text in "quotation
83
+ # marks" followed immediately by a colon and the URL of the link.
84
+ #
85
+ # Optional: text in (parentheses) following the link text,
86
+ # but before the closing quotation mark, will become a Title
87
+ # attribute for the link, visible as a tool tip when a cursor is above it.
88
+ #
89
+ # Example:
90
+ #
91
+ # "This is a link (This is a title) ":http://www.textism.com
92
+ #
93
+ # Will become:
94
+ #
95
+ # <a href="http://www.textism.com" title="This is a title">This is a link</a>
96
+ #
97
+ # == Images
98
+ #
99
+ # To insert an image, put the URL for the image inside exclamation marks.
100
+ #
101
+ # Optional: text that immediately follows the URL in (parentheses) will
102
+ # be used as the Alt text for the image. Images on the web should always
103
+ # have descriptive Alt text for the benefit of readers using non-graphical
104
+ # browsers.
105
+ #
106
+ # Optional: place a colon followed by a URL immediately after the
107
+ # closing ! to make the image into a link.
108
+ #
109
+ # Example:
110
+ #
111
+ # !http://www.textism.com/common/textist.gif(Textist)!
112
+ #
113
+ # Will become:
114
+ #
115
+ # <img src="http://www.textism.com/common/textist.gif" alt="Textist" />
116
+ #
117
+ # With a link:
118
+ #
119
+ # !/common/textist.gif(Textist)!:http://textism.com
120
+ #
121
+ # Will become:
122
+ #
123
+ # <a href="http://textism.com"><img src="/common/textist.gif" alt="Textist" /></a>
124
+ #
125
+ # == Defining Acronyms
126
+ #
127
+ # HTML allows authors to define acronyms via the tag. The definition appears as a
128
+ # tool tip when a cursor hovers over the acronym. A crucial aid to clear writing,
129
+ # this should be used at least once for each acronym in documents where they appear.
130
+ #
131
+ # To quickly define an acronym in Textile, place the full text in (parentheses)
132
+ # immediately following the acronym.
133
+ #
134
+ # Example:
135
+ #
136
+ # ACLU(American Civil Liberties Union)
137
+ #
138
+ # Will become:
139
+ #
140
+ # <acronym title="American Civil Liberties Union">ACLU</acronym>
141
+ #
142
+ # == Adding Tables
143
+ #
144
+ # In Textile, simple tables can be added by seperating each column by
145
+ # a pipe.
146
+ #
147
+ # |a|simple|table|row|
148
+ # |And|Another|table|row|
149
+ #
150
+ # Attributes are defined by style definitions in parentheses.
151
+ #
152
+ # table(border:1px solid black).
153
+ # (background:#ddd;color:red). |{}| | | |
154
+ #
155
+ # == Using RedCloth
156
+ #
157
+ # RedCloth is simply an extension of the String class, which can handle
158
+ # Textile formatting. Use it like a String and output HTML with its
159
+ # RedCloth#to_html method.
160
+ #
161
+ # doc = RedCloth.new "
162
+ #
163
+ # h2. Test document
164
+ #
165
+ # Just a simple test."
166
+ #
167
+ # puts doc.to_html
168
+ #
169
+ # By default, RedCloth uses both Textile and Markdown formatting, with
170
+ # Textile formatting taking precedence. If you want to turn off Markdown
171
+ # formatting, to boost speed and limit the processor:
172
+ #
173
+ # class RedCloth::Textile.new( str )
174
+
175
+ class RedClothDocBook < String
176
+
177
+ VERSION = '3.0.0'
178
+
179
+ #
180
+ # Two accessor for setting security restrictions.
181
+ #
182
+ # This is a nice thing if you're using RedCloth for
183
+ # formatting in public places (e.g. Wikis) where you
184
+ # don't want users to abuse HTML for bad things.
185
+ #
186
+ # If +:filter_html+ is set, HTML which wasn't
187
+ # created by the Textile processor will be escaped.
188
+ #
189
+ # If +:filter_styles+ is set, it will also disable
190
+ # the style markup specifier. ('{color: red}')
191
+ #
192
+ attr_accessor :filter_html, :filter_styles
193
+
194
+ #
195
+ # Accessor for toggling hard breaks.
196
+ #
197
+ # If +:hard_breaks+ is set, single newlines will
198
+ # be converted to HTML break tags. This is the
199
+ # default behavior for traditional RedCloth.
200
+ #
201
+ attr_accessor :hard_breaks
202
+
203
+ #
204
+ # Establishes the markup predence. Available rules include:
205
+ #
206
+ # == Textile Rules
207
+ #
208
+ # The following textile rules can be set individually. Or add the complete
209
+ # set of rules with the single :textile rule, which supplies the rule set in
210
+ # the following precedence:
211
+ #
212
+ # refs_textile:: Textile references (i.e. [hobix]http://hobix.com/)
213
+ # block_textile_table:: Textile table block structures
214
+ # block_textile_lists:: Textile list structures
215
+ # block_textile_prefix:: Textile blocks with prefixes (i.e. bq., h2., etc.)
216
+ # inline_textile_image:: Textile inline images
217
+ # inline_textile_link:: Textile inline links
218
+ # inline_textile_span:: Textile inline spans
219
+ # inline_textile_glyphs:: Textile entities (such as em-dashes and smart quotes)
220
+ #
221
+ # == Markdown
222
+ #
223
+ # refs_markdown:: Markdown references (for example: [hobix]: http://hobix.com/)
224
+ # block_markdown_setext:: Markdown setext headers
225
+ # block_markdown_atx:: Markdown atx headers
226
+ # block_markdown_rule:: Markdown horizontal rules
227
+ # block_markdown_bq:: Markdown blockquotes
228
+ # block_markdown_lists:: Markdown lists
229
+ # inline_markdown_link:: Markdown links
230
+ attr_accessor :rules
231
+
232
+ # Returns a new RedCloth object, based on _string_ and
233
+ # enforcing all the included _restrictions_.
234
+ #
235
+ # r = RedCloth.new( "h1. A <b>bold</b> man", [:filter_html] )
236
+ # r.to_html
237
+ # #=>"<h1>A &lt;b&gt;bold&lt;/b&gt; man</h1>"
238
+ #
239
+ def initialize( string, restrictions = [] )
240
+ restrictions.each { |r| method( "#{ r }=" ).call( true ) }
241
+ @rules = [:textile, :markdown]
242
+ @levels = []
243
+ super( string )
244
+
245
+ end
246
+
247
+ #
248
+ # Generates HTML from the Textile contents.
249
+ #
250
+ # r = RedCloth.new( "And then? She *fell*!" )
251
+ # r.to_html( true )
252
+ # #=>"And then? She <strong>fell</strong>!"
253
+ #
254
+ def to_docbook( *rules )
255
+ rules = @rules if rules.empty?
256
+ # make our working copy
257
+ text = self.dup
258
+
259
+ @urlrefs = {}
260
+ @shelf = []
261
+ textile_rules = [:refs_textile, :block_textile_table, :block_textile_lists,
262
+ :block_textile_prefix, :inline_textile_image, :inline_textile_link,
263
+ :inline_textile_code, :inline_textile_span, :inline_textile_glyphs]
264
+ markdown_rules = [:refs_markdown, :block_markdown_setext, :block_markdown_atx, :block_markdown_rule,
265
+ :block_markdown_bq, :block_markdown_lists,
266
+ :inline_markdown_reflink, :inline_markdown_link]
267
+ @rules = rules.collect do |rule|
268
+ case rule
269
+ when :markdown
270
+ markdown_rules
271
+ when :textile
272
+ textile_rules
273
+ else
274
+ rule
275
+ end
276
+ end.flatten
277
+
278
+ # standard clean up
279
+ incoming_entities text
280
+ clean_white_space text
281
+
282
+ # start processor
283
+ pre_list = rip_offtags text
284
+ refs text
285
+ blocks text
286
+ inline text
287
+ smooth_offtags text, pre_list
288
+
289
+ retrieve text
290
+
291
+ orphans text
292
+
293
+ text.gsub!( /<\/?notextile>/, '' )
294
+ text.gsub!( /x%x%/, '&#38;' )
295
+ text.strip!
296
+
297
+ @levels.reverse.each do |level|
298
+ text << "<\/#{level}>"
299
+ end
300
+
301
+ text
302
+
303
+ end
304
+
305
+ #######
306
+ private
307
+ #######
308
+ #
309
+ # Mapping of 8-bit ASCII codes to HTML numerical entity equivalents.
310
+ # (from PyTextile)
311
+ #
312
+ TEXTILE_TAGS =
313
+
314
+ [[128, 8364], [129, 0], [130, 8218], [131, 402], [132, 8222], [133, 8230],
315
+ [134, 8224], [135, 8225], [136, 710], [137, 8240], [138, 352], [139, 8249],
316
+ [140, 338], [141, 0], [142, 0], [143, 0], [144, 0], [145, 8216], [146, 8217],
317
+ [147, 8220], [148, 8221], [149, 8226], [150, 8211], [151, 8212], [152, 732],
318
+ [153, 8482], [154, 353], [155, 8250], [156, 339], [157, 0], [158, 0], [159, 376]].
319
+
320
+ collect! do |a, b|
321
+ [a.chr, ( b.zero? and "" or "&#{ b };" )]
322
+ end
323
+
324
+ #
325
+ # Regular expressions to convert to HTML.
326
+ #
327
+ A_HLGN = /(?:(?:<>|<|>|\=|[()]+)+)/
328
+ A_VLGN = /[\-^~]/
329
+ C_CLAS = '(?:\([^)]+\))'
330
+ C_LNGE = '(?:\[[^\]]+\])'
331
+ C_STYL = '(?:\{[^}]+\})'
332
+ S_CSPN = '(?:\\\\\d+)'
333
+ S_RSPN = '(?:/\d+)'
334
+ A = "(?:#{A_HLGN}?#{A_VLGN}?|#{A_VLGN}?#{A_HLGN}?)"
335
+ S = "(?:#{S_CSPN}?#{S_RSPN}|#{S_RSPN}?#{S_CSPN}?)"
336
+ C = "(?:#{C_CLAS}?#{C_STYL}?#{C_LNGE}?|#{C_STYL}?#{C_LNGE}?#{C_CLAS}?|#{C_LNGE}?#{C_STYL}?#{C_CLAS}?)"
337
+ # PUNCT = Regexp::quote( '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~' )
338
+ PUNCT = Regexp::quote( '!"#$%&\'*+,-./:;=?@\\^_`|~' )
339
+ HYPERLINK = '(\S+?)([^\w\s/;=\?]*?)(?=\s|<|$)'
340
+
341
+ # Text markup tags, don't conflict with block tags
342
+ SIMPLE_HTML_TAGS = [
343
+ 'tt', 'b', 'i', 'big', 'small', 'em', 'strong', 'dfn', 'code',
344
+ 'samp', 'kbd', 'var', 'cite', 'abbr', 'acronym', 'a', 'img', 'br',
345
+ 'br', 'map', 'q', 'sub', 'sup', 'span', 'bdo'
346
+ ]
347
+
348
+ # Elements to handle
349
+ GLYPHS = [
350
+ # [ /([^\s\[{(>])?\'([dmst]\b|ll\b|ve\b|\s|:|$)/, '\1&#8217;\2' ], # single closing
351
+ [ /([^\s\[{(>])\'/, '\1&#8217;' ], # single closing
352
+ [ /\'(?=\s|s\b|[#{PUNCT}])/, '&#8217;' ], # single closing
353
+ [ /\'/, '&#8216;' ], # single opening
354
+ # [ /([^\s\[{(])?"(\s|:|$)/, '\1&#8221;\2' ], # double closing
355
+ [ /([^\s\[{(>])"/, '\1&#8221;' ], # double closing
356
+ [ /"(?=\s|[#{PUNCT}])/, '&#8221;' ], # double closing
357
+ [ /"/, '&#8220;' ], # double opening
358
+ [ /\b( )?\.{3}/, '\1&#8230;' ], # ellipsis
359
+ [ /\b([A-Z][A-Z0-9]{2,})\b(?:[(]([^)]*)[)])/, '<acronym title="\2">\1</acronym>' ], # 3+ uppercase acronym
360
+ ### Taking out this caps one
361
+ #[ /(^|[^"][>\s])([A-Z][A-Z0-9 ]{2,})([^<a-z0-9]|$)/, '\1<span class="caps">\2</span>\3' ], # 3+ uppercase caps
362
+ [ /(\.\s)?\s?--\s?/, '\1&#8212;' ], # em dash
363
+ [ /\s->\s/, ' &rarr; ' ], # en dash
364
+ [ /\s-\s/, ' &#8211; ' ], # en dash
365
+ [ /(\d+) ?x ?(\d+)/, '\1&#215;\2' ], # dimension sign
366
+ [ /\b ?[(\[]TM[\])]/i, '&#8482;' ], # trademark
367
+ [ /\b ?[(\[]R[\])]/i, '&#174;' ], # registered
368
+ [ /\b ?[(\[]C[\])]/i, '&#169;' ] # copyright
369
+ ]
370
+
371
+ H_ALGN_VALS = {
372
+ '<' => 'left',
373
+ '=' => 'center',
374
+ '>' => 'right',
375
+ '<>' => 'justify'
376
+ }
377
+
378
+ V_ALGN_VALS = {
379
+ '^' => 'top',
380
+ '-' => 'middle',
381
+ '~' => 'bottom'
382
+ }
383
+
384
+ QTAGS = [
385
+ ['**', 'b'],
386
+ ['*', 'strong'],
387
+ ['??', 'cite'],
388
+ ['-', 'del'],
389
+ ['__', 'italic'],
390
+ ['_', 'em'],
391
+ ['%', 'phrase'],
392
+ ['+', 'ins'],
393
+ ['^', 'superscript'],
394
+ ['~', 'subscript']
395
+ ].collect do |rc, ht|
396
+ ttr = Regexp.quote(rc)
397
+ punct = PUNCT.sub( Regexp::quote(rc), '' )
398
+ re = /(^|[\s\>#{punct}{(\[])
399
+ #{ttr}
400
+ (#{C})
401
+ (?::(\S+?))?
402
+ ([^\s#{ttr}]+?(?:[^\n]|\n(?!\n))*?)
403
+ ([#{punct}]*?)
404
+ #{ttr}
405
+ (?=[\s\])}<#{punct}]|$)/xm
406
+ [re, ht]
407
+ end
408
+
409
+ #
410
+ # Flexible HTML escaping
411
+ #
412
+ def htmlesc( str, mode )
413
+ str.gsub!( '&', '&amp;' )
414
+ str.gsub!( '"', '&quot;' ) if mode != :NoQuotes
415
+ str.gsub!( "'", '&#039;' ) if mode == :Quotes
416
+ str.gsub!( '<', '&lt;')
417
+ str.gsub!( '>', '&gt;')
418
+ end
419
+
420
+ # Search and replace for Textile glyphs (quotes, dashes, other symbols)
421
+ def pgl( text )
422
+ GLYPHS.each do |re, resub|
423
+ text.gsub! re, resub
424
+ end
425
+ end
426
+
427
+ # Parses Textile attribute lists and builds an HTML attribute string
428
+ def pba( text_in, element = "" )
429
+
430
+ return '' unless text_in
431
+
432
+ text = text_in.dup
433
+
434
+ rowspan = $1 if text =~ /\/(\d+)/
435
+
436
+ atts = ""
437
+
438
+ atts << "#{ $1 };" if not @filter_styles and
439
+ text.sub!( /\{([^}]*)\}/, '' )
440
+
441
+ lang = $1 if
442
+ text.sub!( /\[([^)]+?)\]/, '' )
443
+
444
+ cls = $1 if
445
+ text.sub!( /\(([^()]+?)\)/, '' )
446
+
447
+ atts << "padding-left:#{ $1.length }em;" if
448
+ text.sub!( /([(]+)/, '' )
449
+
450
+ atts << "padding-right:#{ $1.length }em;" if text.sub!( /([)]+)/, '' )
451
+
452
+ atts << " align=\"#{H_ALGN_VALS[$&]}\"" if text =~ A_HLGN
453
+ atts << " vlign=\"#{V_ALGN_VALS[$&]}\"" if text =~ A_VLGN
454
+
455
+ cls, id = $1, $2 if cls =~ /^(.*?)#(.*)$/
456
+
457
+ atts << " class=\"#{ cls }\"" unless cls.to_s.empty?
458
+ atts << " lang=\"#{ lang }\"" if lang
459
+ atts << " id=\"#{ id }\"" if id
460
+ atts << " morerows=\"#{ rowspan.to_i - 1 }\"" if rowspan
461
+
462
+ atts
463
+ end
464
+
465
+ TABLE_RE = /^(?:table(_?#{S}#{A}#{C})\. ?\n)?^(#{A}#{C}\.? ?\|.*?\|)(\n\n|\Z)/m
466
+
467
+
468
+ # Parses a Textile table block, building HTML from the result.
469
+ def block_textile_table( text )
470
+ text.gsub!( TABLE_RE ) do |matches|
471
+
472
+ tatts, fullrow = $~[1..2]
473
+ tatts = pba( tatts, 'table' )
474
+ tatts << " frame=\"all\""
475
+
476
+ rows = []
477
+
478
+ fullrow.split( /\|$/m ).each do |row|
479
+
480
+ ratts, row = pba( $1, 'row' ), $2 if row =~ /^(#{A}#{C}\. )(.*)/m
481
+
482
+ cells = []
483
+ headrow = nil
484
+ row.split( '|' ).each do |cell|
485
+ headrow = true if /^_/.match(cell)
486
+ catts = ''
487
+ catts, cell = pba( $1, 'entry' ), $2 if cell =~ /^(_?#{S}#{A}#{C}\. ?)(.*)/
488
+
489
+ unless cell.strip.empty?
490
+ cells << "\t\t\t<entry#{ catts }>#{ cell }</entry>"
491
+ end
492
+ end
493
+ rows << "\t<thead>" if headrow
494
+ rows << "\t\t<row#{ ratts }>\n#{ cells.join( "\n" ) }\n\t\t</row>"
495
+ rows << "\t</thead>" if headrow
496
+ end
497
+ "\t<table #{ tatts }>\n#{ rows.join( "\n" ) }\n\t</table>\n\n"
498
+ end
499
+ end
500
+
501
+ # Parses a Textile table block, building HTML from the result.
502
+ def block_textile_tablex( text )
503
+ text.gsub!( TABLE_RE ) do |matches|
504
+
505
+ tatts, fullrow = $~[1..2]
506
+ tatts = pba( tatts, 'table' )
507
+ rows = []
508
+
509
+ fullrow.
510
+ split( /\|$/m ).
511
+ delete_if { |x| x.empty? }.
512
+ each do |row|
513
+
514
+ ratts, row = pba( $1, 'tr' ), $2 if row =~ /^(#{A}#{C}\. )(.*)/m
515
+
516
+ cells = []
517
+ row.split( '|' ).each do |cell|
518
+ ctyp = 'd'
519
+ ctyp = 'h' if cell =~ /^_/
520
+
521
+ catts = ''
522
+ catts, cell = pba( $1, 'td' ), $2 if cell =~ /^(_?#{S}#{A}#{C}\. ?)(.*)/
523
+
524
+ unless cell.strip.empty?
525
+ cells << "\t\t\t<t#{ ctyp }#{ catts }>#{ cell }</t#{ ctyp }>"
526
+ end
527
+ end
528
+ rows << "\t\t<tr#{ ratts }>\n#{ cells.join( "\n" ) }\n\t\t</tr>"
529
+ end
530
+ "\t<table#{ tatts }>\n#{ rows.join( "\n" ) }\n\t</table>\n\n"
531
+ end
532
+ end
533
+
534
+ LISTS_RE = /^([#*]+?#{C} .*?)$(?![^#*])/m
535
+ LISTS_CONTENT_RE = /^([#*]+)(#{A}#{C}) (.*)$/m
536
+
537
+ # Parses Textile lists and generates HTML
538
+ def block_textile_lists( text )
539
+ text.gsub!( LISTS_RE ) do |match|
540
+ lines = match.split( /\n/ )
541
+ last_line = -1
542
+ depth = []
543
+ lines.each_with_index do |line, line_id|
544
+ if line =~ LISTS_CONTENT_RE
545
+ tl,atts,content = $~[1..3]
546
+ if depth.last
547
+ if depth.last.length > tl.length
548
+ (depth.length - 1).downto(0) do |i|
549
+ break if depth[i].length == tl.length
550
+ lines[line_id - 1] << "</listitem>\n\t</#{ lT( depth[i] ) }>\n\t"
551
+ depth.pop
552
+ end
553
+ end
554
+ if depth.last.length == tl.length
555
+ lines[line_id - 1] << '</listitem>'
556
+ end
557
+ end
558
+ unless depth.last == tl
559
+ depth << tl
560
+ atts = pba( atts )
561
+ lines[line_id] = "\t<#{ lT(tl) }#{ atts }>\n\t<listitem>#{ content }"
562
+ else
563
+ lines[line_id] = "\t\t<listitem>#{ content }"
564
+ end
565
+ last_line = line_id
566
+
567
+ else
568
+ last_line = line_id
569
+ end
570
+ if line_id - last_line > 1 or line_id == lines.length - 1
571
+ depth.delete_if do |v|
572
+ lines[last_line] << "</listitem>\n\t</#{ lT( v ) }>"
573
+ end
574
+ end
575
+ end
576
+ lines.join( "\n" )
577
+ end
578
+ end
579
+
580
+ CODE_RE = /
581
+ (^|[\s>#{PUNCT}{(\[]) # 1 open bracket?
582
+ @ # opening
583
+ (?:\|(\w+?)\|)? # 2 language
584
+ (\S(?:[^\n]|\n(?!\n))*?) # 3 code
585
+ @ # closing
586
+ (?=[\s\]}\)<#{PUNCT}]|$) # 4 closing bracket?
587
+ /x
588
+
589
+ def inline_textile_code( text )
590
+ text.gsub!( CODE_RE ) do |m|
591
+ before,lang,code,after = $~[1..4]
592
+ lang = " lang=\"#{ lang }\"" if lang
593
+ "#{ before }<code#{ lang }>#{ code }</code>#{ after }"
594
+ end
595
+ end
596
+
597
+ def lT( text )
598
+ text =~ /\#$/ ? 'orderedlist' : 'itemizedlist'
599
+ end
600
+
601
+ def hard_break( text )
602
+ text.gsub!( /(.)\n(?! *[#*\s|])/, "\\1<br />" ) if @hard_breaks
603
+ end
604
+
605
+ BLOCKS_GROUP_RE = /(#{
606
+ ['#', '*', '>'].collect do |sym|
607
+ sym = Regexp::quote( sym )
608
+ '(?:\n*[' + sym + ' ](?:[^\n]|\n+[' + sym + ' ]|\n(?!\n|\Z))+)'
609
+ end.join '|'
610
+ })|((?:[^\n]+|\n+ +|\n(?![#*\n]|\Z))+)/m
611
+
612
+ NON_TAG_LINE_RE = '(^[^<]*\n)'
613
+ WIDOW_RE = /(?=\A)(#{NON_TAG_LINE_RE}+)/
614
+ ORPHAN_RE = /(#{NON_TAG_LINE_RE}+\Z)/
615
+
616
+ def blocks( text, deep_code = false )
617
+ text.gsub!( BLOCKS_GROUP_RE ) do |blk|
618
+
619
+ plain = $2 ? true : false
620
+
621
+ #blk.gsub!(WIDOW_RE) { |x| "<widow>#{x}</widow>" }
622
+ # skip blocks that are complex HTML
623
+ if blk =~ /^<\/?(\w+).*>/ and not SIMPLE_HTML_TAGS.include? $1
624
+ blk
625
+ else
626
+ # search for indentation levels
627
+ blk.strip!
628
+ if blk.empty?
629
+ blk
630
+ else
631
+ code_blk = nil
632
+ blk.gsub!( /((?:\n(?:\n^ +[^\n]*)+)+)/m ) do |iblk|
633
+ flush_left iblk
634
+ blocks iblk, plain
635
+ iblk.gsub( /^(\S)/, "\t\\1" )
636
+ if plain
637
+ code_blk = iblk; ""
638
+ else
639
+ iblk
640
+ end
641
+ end
642
+
643
+ block_applied = nil
644
+ @rules.each do |rule_name|
645
+ break if block_applied = ( rule_name.to_s.match(/^block_/) and method( rule_name ).call( blk ) )
646
+ end
647
+ unless block_applied
648
+ if deep_code
649
+ blk = "\t<programlisting>#{ blk }</programlisting>"
650
+ else
651
+ blk = "\t<para>#{ blk }</para>"
652
+ end
653
+ end
654
+ # hard_break blk
655
+ blk + "\n#{ code_blk }"
656
+ end
657
+ end
658
+
659
+ end
660
+ end
661
+
662
+ def textile_bq( tag, atts, cite, content )
663
+ cite, cite_title = check_refs( cite )
664
+ cite = " cite=\"#{ cite }\"" if cite
665
+ "\t<blockquote#{ cite }>\n\t\t<para#{ atts }>#{ content }</para>\n\t</blockquote>"
666
+ end
667
+
668
+ def textile_p( tag, atts, cite, content )
669
+
670
+ str = ""
671
+ if @levels[-1]
672
+ str << "<\/#{@levels[-1]}>\n"
673
+ @levels.pop
674
+ end
675
+ level = (/\d+/.match(tag)[0]).to_i - 1
676
+ @levels.push("sect#{level}")
677
+
678
+ str << "\t<sect#{level}><title>#{ content }</title>\n"
679
+ end
680
+
681
+ alias textile_h1 textile_p
682
+ alias textile_h2 textile_p
683
+ alias textile_h3 textile_p
684
+ alias textile_h4 textile_p
685
+ alias textile_h5 textile_p
686
+ alias textile_h6 textile_p
687
+
688
+ def textile_fn_( tag, num, atts, cite, content )
689
+ atts << " id=\"fn#{ num }\""
690
+ content = "<sup>#{ num }</sup> #{ content }"
691
+ "\t<para#{ atts }>#{ content }</para>"
692
+ end
693
+
694
+ BLOCK_RE = /^(([a-z]+)(\d*))(#{A}#{C})\.(?::(\S+))? (.*)$/m
695
+
696
+ def block_textile_prefix( text )
697
+ if text =~ BLOCK_RE
698
+ tag,tagpre,num,atts,cite,content = $~[1..6]
699
+ atts = pba( atts )
700
+
701
+ # pass to prefix handler
702
+ if respond_to? "textile_#{ tag }", true
703
+ text.gsub!( $&, method( "textile_#{ tag }" ).call( tag, atts, cite, content ) )
704
+ elsif respond_to? "textile_#{ tagpre }_", true
705
+ text.gsub!( $&, method( "textile_#{ tagpre }_" ).call( tagpre, num, atts, cite, content ) )
706
+ end
707
+ end
708
+ end
709
+
710
+ SETEXT_RE = /\A(.+?)\n([=-])[=-]* *$/m
711
+ def block_markdown_setext( text )
712
+ if text =~ SETEXT_RE
713
+ tag = if $2 == "="; "h1"; else; "h2"; end
714
+ blk, cont = "<#{ tag }>#{ $1 }</#{ tag }>", $'
715
+ blocks cont
716
+ text.replace( blk + cont )
717
+ end
718
+ end
719
+
720
+ ATX_RE = /\A(\#{1,6}) # $1 = string of #'s
721
+ [ ]*
722
+ (.+?) # $2 = Header text
723
+ [ ]*
724
+ \#* # optional closing #'s (not counted)
725
+ $/x
726
+ def block_markdown_atx( text )
727
+ if text =~ ATX_RE
728
+ tag = "h#{ $1.length }"
729
+ blk, cont = "<#{ tag }>#{ $2 }</#{ tag }>\n\n", $'
730
+ blocks cont
731
+ text.replace( blk + cont )
732
+ end
733
+ end
734
+
735
+ MARKDOWN_BQ_RE = /\A(^ *> ?.+$(.+\n)*\n*)+/m
736
+
737
+ def block_markdown_bq( text )
738
+ text.gsub!( MARKDOWN_BQ_RE ) do |blk|
739
+ blk.gsub!( /^ *> ?/, '' )
740
+ flush_left blk
741
+ blocks blk
742
+ blk.gsub!( /^(\S)/, "\t\\1" )
743
+ "<blockquote>\n#{ blk }\n</blockquote>\n\n"
744
+ end
745
+ end
746
+
747
+ MARKDOWN_RULE_RE = /^#{
748
+ ['*', '-', '_'].collect { |ch| '( ?' + Regexp::quote( ch ) + ' ?){3,}' }.join( '|' )
749
+ }$/
750
+
751
+ def block_markdown_rule( text )
752
+ text.gsub!( MARKDOWN_RULE_RE ) do |blk|
753
+ "<hr />"
754
+ end
755
+ end
756
+
757
+ # todo
758
+ def block_markdown_lists( text )
759
+ end
760
+
761
+ def inline_markdown_link( text )
762
+ end
763
+
764
+ def inline_textile_span( text )
765
+ QTAGS.each do |ttr, ht|
766
+ text.gsub!(ttr) do |m|
767
+ start,atts,cite,content,tend = $~[1..5]
768
+ atts = pba( atts )
769
+ if ht == 'b'
770
+ atts << " role=\"bold\""
771
+ httmp = "emphasis"
772
+ elsif ht == 'strong'
773
+ atts << " role=\"strong\""
774
+ httmp = "emphasis"
775
+ elsif ht == 'italic'
776
+ httmp = "emphasis"
777
+ else
778
+ httmp = ht
779
+ end
780
+
781
+ atts << " cite=\"#{ cite }\"" if cite
782
+
783
+ "#{ start }<#{ httmp}#{ atts }>#{ content }#{ tend }</#{ httmp }>"
784
+
785
+ end
786
+ end
787
+ end
788
+
789
+ LINK_RE = /
790
+ ([\s\[{(]|[#{PUNCT}])? # $pre
791
+ " # start
792
+ (#{C}) # $atts
793
+ ([^"]+?) # $text
794
+ \s?
795
+ (?:\(([^)]+?)\)(?="))? # $title
796
+ ":
797
+ (\S+?) # $url
798
+ (\/)? # $slash
799
+ ([^\w\/;]*?) # $post
800
+ (?=<|\s|$)
801
+ /x
802
+
803
+ def inline_textile_link( text )
804
+ text.gsub!( LINK_RE ) do |m|
805
+ pre,atts,text,title,url,slash,post = $~[1..7]
806
+
807
+ url, url_title = check_refs( url )
808
+ title ||= url_title
809
+
810
+ atts = pba( atts )
811
+ atts = " url=\"#{ url }#{ slash }\"#{ atts }"
812
+ atts << " alt=\"#{title}\"" if title
813
+ atts = shelve( atts ) if atts
814
+
815
+ link = "#{ pre }<ulink#{ atts }>#{ text }</ulink>#{ post }"
816
+ end
817
+ end
818
+
819
+ MARKDOWN_REFLINK_RE = /
820
+ \[([^\[\]]+)\] # $text
821
+ [ ]? # opt. space
822
+ (?:\n[ ]*)? # one optional newline followed by spaces
823
+ \[(.*?)\] # $id
824
+ /x
825
+
826
+ def inline_markdown_reflink( text )
827
+ text.gsub!( MARKDOWN_REFLINK_RE ) do |m|
828
+ text, id = $~[1..2]
829
+
830
+ if id.empty?
831
+ url, title = check_refs( text )
832
+ else
833
+ url, title = check_refs( id )
834
+ end
835
+
836
+ atts = " href=\"#{ url }\""
837
+ atts << " title=\"#{ title }\"" if title
838
+ atts = shelve( atts )
839
+
840
+ "<a#{ atts }>#{ text }</a>"
841
+ end
842
+ end
843
+
844
+ MARKDOWN_LINK_RE = /
845
+ \[([^\[\]]+)\] # $text
846
+ \( # open paren
847
+ [ \t]* # opt space
848
+ <?(.+?)>? # $href
849
+ [ \t]* # opt space
850
+ (?: # whole title
851
+ (['"]) # $quote
852
+ (.*?) # $title
853
+ \3 # matching quote
854
+ )? # title is optional
855
+ \)
856
+ /x
857
+
858
+ def inline_markdown_link( text )
859
+ text.gsub!( MARKDOWN_LINK_RE ) do |m|
860
+ text, url, quote, title = $~[1..4]
861
+
862
+ atts = " href=\"#{ url }\""
863
+ atts << " title=\"#{ title }\"" if title
864
+ atts = shelve( atts )
865
+
866
+ "<a#{ atts }>#{ text }</a>"
867
+ end
868
+ end
869
+
870
+ TEXTILE_REFS_RE = /(^ *)\[([^\n]+?)\](#{HYPERLINK})(?=\s|$)/
871
+ MARKDOWN_REFS_RE = /(^ *)\[([^\n]+?)\]:\s+<?(#{HYPERLINK})>?(?:\s+"((?:[^"]|\\")+)")?(?=\s|$)/m
872
+
873
+ def refs( text )
874
+ @rules.each do |rule_name|
875
+ method( rule_name ).call( text ) if rule_name.to_s.match /^refs_/
876
+ end
877
+ end
878
+
879
+ def refs_textile( text )
880
+ text.gsub!( TEXTILE_REFS_RE ) do |m|
881
+ flag, url = $~[2..3]
882
+ @urlrefs[flag.downcase] = [url, nil]
883
+ nil
884
+ end
885
+ end
886
+
887
+ def refs_markdown( text )
888
+ text.gsub!( MARKDOWN_REFS_RE ) do |m|
889
+ flag, url = $~[2..3]
890
+ title = $~[6]
891
+ @urlrefs[flag.downcase] = [url, title]
892
+ nil
893
+ end
894
+ end
895
+
896
+ def check_refs( text )
897
+ ret = @urlrefs[text.downcase] if text
898
+ ret || [text, nil]
899
+ end
900
+
901
+ IMAGE_RE = /
902
+ (<p>|.|^) # start of line?
903
+ \! # opening
904
+ (\<|\=|\>)? # optional alignment atts
905
+ (#{C}) # optional style,class atts
906
+ (?:\. )? # optional dot-space
907
+ ([^\s(!]+?) # presume this is the src
908
+ \s? # optional space
909
+ (?:\(((?:[^\(\)]|\([^\)]+\))+?)\))? # optional title
910
+ \! # closing
911
+ (?::#{ HYPERLINK })? # optional href
912
+ /x
913
+
914
+ def inline_textile_image( text )
915
+ text.gsub!( IMAGE_RE ) do |m|
916
+ stln,algn,atts,url,title,href,href_a1,href_a2 = $~[1..8]
917
+ atts = pba( atts )
918
+ atts = " src=\"#{ url }\"#{ atts }"
919
+ atts << " title=\"#{ title }\"" if title
920
+ atts << " alt=\"#{ title }\""
921
+ # size = @getimagesize($url);
922
+ # if($size) $atts.= " $size[3]";
923
+
924
+ href, alt_title = check_refs( href ) if href
925
+ url, url_title = check_refs( url )
926
+
927
+ out = ''
928
+ out << "<a#{ shelve( " href=\"#{ href }\"" ) }>" if href
929
+ out << "<img#{ shelve( atts ) } />"
930
+ out << "</a>#{ href_a1 }#{ href_a2 }" if href
931
+
932
+ if algn
933
+ algn = h_align( algn )
934
+ if stln == "<p>"
935
+ out = "<p style=\"float:#{ algn }\">#{ out }"
936
+ else
937
+ out = "#{ stln }<div style=\"float:#{ algn }\">#{ out }</div>"
938
+ end
939
+ else
940
+ out = stln + out
941
+ end
942
+
943
+ out
944
+ end
945
+ end
946
+
947
+ def shelve( val )
948
+ @shelf << val
949
+ " <#{ @shelf.length }>"
950
+ end
951
+
952
+ def retrieve( text )
953
+ @shelf.each_with_index do |r, i|
954
+ text.gsub!( " <#{ i + 1 }>", r )
955
+ end
956
+ end
957
+
958
+ def incoming_entities( text )
959
+ ## turn any incoming ampersands into a dummy character for now.
960
+ ## This uses a negative lookahead for alphanumerics followed by a semicolon,
961
+ ## implying an incoming html entity, to be skipped
962
+
963
+ text.gsub!( /&(?![#a-z0-9]+;)/i, "x%x%" )
964
+ end
965
+
966
+ def clean_white_space( text )
967
+ # normalize line breaks
968
+ text.gsub!( /\r\n/, "\n" )
969
+ text.gsub!( /\r/, "\n" )
970
+ text.gsub!( /\t/, ' ' )
971
+ text.gsub!( /^ +$/, '' )
972
+ text.gsub!( /\n{3,}/, "\n\n" )
973
+ text.gsub!( /"$/, "\" " )
974
+
975
+ # if entire document is indented, flush
976
+ # to the left side
977
+ flush_left text
978
+ end
979
+
980
+ def flush_left( text )
981
+ indt = 0
982
+ while text !~ /^ {#{indt}}\S/
983
+ indt += 1
984
+ end
985
+ if indt.nonzero?
986
+ text.gsub!( /^ {#{indt}}/, '' )
987
+ end
988
+ end
989
+
990
+ def footnote_ref( text )
991
+ text.gsub!( /\b\[([0-9]+?)\](\s)?/,
992
+ '<sup><a href="#fn\1">\1</a></sup>\2' )
993
+ end
994
+
995
+ OFFTAGS = /(code|pre|kbd|notextile)/
996
+ OFFTAG_MATCH = /(?:(<\/#{ OFFTAGS }>)|(<#{ OFFTAGS }[^>]*>))(.*?)(?=<\/?#{ OFFTAGS }|\Z)/mi
997
+ #OFFTAG_MATCH = /(?:(<\/#{ OFFTAGS }>)|(<#{ OFFTAGS }[^>]*>))(.*?)(?=<\/?(code|pre|kbd|notextile)|\Z)/mi
998
+ OFFTAG_OPEN = /<#{ OFFTAGS }/
999
+ OFFTAG_CLOSE = /<\/?#{ OFFTAGS }/
1000
+ HASTAG_MATCH = /(<\/?\w[^\n]*?>)/m
1001
+ ALLTAG_MATCH = /(<\/?\w[^\n]*?>)|.*?(?=<\/?\w[^\n]*?>|$)/m
1002
+
1003
+ def orphans(text)
1004
+ # text.gsub!(/(\A|#{OFFTAG_CLOSE})(\n\n+)((?:[^<\n]+\n)+)/) { "#{$1}<orphan>#{$2}</orphan>\n" }
1005
+ # text.gsub!(/(#{OFFTAG_CLOSE}[^<]*\n)((?:[^<\n]+\n)+)/) { "#{$1}<orphan>#{$2}</orphan>\n" }
1006
+ text
1007
+ end
1008
+
1009
+ def inline_textile_glyphs( text, level = 0 )
1010
+ if text !~ HASTAG_MATCH
1011
+ pgl text
1012
+ footnote_ref text
1013
+ else
1014
+ codepre = 0
1015
+ text.gsub!( ALLTAG_MATCH ) do |line|
1016
+ ## matches are off if we're between <code>, <pre> etc.
1017
+ if $1
1018
+ if @filter_html
1019
+ htmlesc( line, :NoQuotes )
1020
+ elsif line =~ OFFTAG_OPEN
1021
+ codepre += 1
1022
+ elsif line =~ OFFTAG_CLOSE
1023
+ codepre -= 1
1024
+ codepre = 0 if codepre < 0
1025
+ end
1026
+ elsif codepre.zero?
1027
+ inline_textile_glyphs( line, level + 1 )
1028
+ else
1029
+ htmlesc( line, :NoQuotes )
1030
+ end
1031
+ ## p [level, codepre, orig_line, line]
1032
+
1033
+ line
1034
+ end
1035
+ end
1036
+ end
1037
+
1038
+ #OFFTAG_MATCH = /(?:(<\/#{ OFFTAGS }>)|(<#{ OFFTAGS }[^>]*>))(.*?)(?=<\/?#{ OFFTAGS }|\Z)/mi
1039
+ #OFFTAG_MATCH = /(?:(<\/(code|pre)>)|(<(code|pre)[^>]*>))(.*?)(?=<\/?(code|pre)|\Z)/mi
1040
+
1041
+ def rip_offtags( text )
1042
+ pre_list = []
1043
+ if text =~ /<.*>/
1044
+ ## strip and encode <pre> content
1045
+ codepre, used_offtags = 0, {}
1046
+ text.gsub!( OFFTAG_MATCH ) do |line|
1047
+ if $3
1048
+ offtag, aftertag = $4, $5
1049
+ codepre += 1
1050
+ used_offtags[offtag] = true
1051
+ if codepre - used_offtags.length > 0
1052
+ htmlesc( line, :NoQuotes ) unless used_offtags['notextile']
1053
+ pre_list.last << line
1054
+ line = ""
1055
+ else
1056
+ htmlesc( aftertag, :NoQuotes ) if aftertag and not used_offtags['notextile']
1057
+ line = "<redpre##{ pre_list.length }>"
1058
+ pre_list << "#{ $3 }#{ aftertag }"
1059
+ end
1060
+ elsif $1 and codepre > 0
1061
+ if codepre - used_offtags.length > 0
1062
+ htmlesc( line, :NoQuotes ) unless used_offtags['notextile']
1063
+ pre_list.last << line
1064
+ line = ""
1065
+ end
1066
+ codepre -= 1 unless codepre.zero?
1067
+ used_offtags = {} if codepre.zero?
1068
+ end
1069
+
1070
+ ## Set 'orphan' text apart by an extra newline,
1071
+ ## to ensure that it gets processed
1072
+ # line.sub!(/(.*<\/#{OFFTAGS}>\n)([^\n])/mi,"\\1\n\\3")
1073
+ #line.sub!(/([^<\n]\n)+(?=[^\n]*#{OFFTAG_MATCH})/im, "<orphan>#{$1}</orphan>")
1074
+ #line.sub!(/([^<]+\n)+(?=.*#{OFFTAG_MATCH})/i, "<orphan>#{$1}</orphan>")
1075
+ #line.sub!(/([^<>]*[^>]\n)+/i, "<orphan>#{$1}</orphan>")
1076
+ line
1077
+ end
1078
+ end
1079
+ pre_list
1080
+ end
1081
+
1082
+ def smooth_offtags( text, pre_list )
1083
+ unless pre_list.empty?
1084
+ ## replace <pre> content
1085
+ text.gsub!( /<redpre#(\d+)>/ ) { pre_list[$1.to_i] }
1086
+ end
1087
+ end
1088
+ def inline( text )
1089
+ @rules.each do |rule_name|
1090
+ method( rule_name ).call( text ) if rule_name.to_s.match /^inline_/
1091
+ end
1092
+ end
1093
+
1094
+ def h_align( text )
1095
+ H_ALGN_VALS[text]
1096
+ end
1097
+
1098
+ def v_align( text )
1099
+ V_ALGN_VALS[text]
1100
+ end
1101
+
1102
+ def textile_popup_help( name, windowW, windowH )
1103
+ ' <a target="_blank" href="http://hobix.com/textile/#' + helpvar + '" onclick="window.open(this.href, \'popupwindow\', \'width=' + windowW + ',height=' + windowH + ',scrollbars,resizable\'); return false;">' + name + '</a><br />'
1104
+ end
1105
+
1106
+ end
1107
+
@@ -14,7 +14,6 @@
14
14
  # Textism for PHP: http://www.textism.com/tools/textile/
15
15
  #
16
16
  #
17
- require 'uri'
18
17
 
19
18
  # = RedCloth
20
19
  #
@@ -167,7 +166,8 @@ require 'uri'
167
166
 
168
167
  class RedCloth < String
169
168
 
170
- VERSION = '3.0.0'
169
+ VERSION = '3.0.1'
170
+ DEFAULT_RULES = [:textile, :markdown]
171
171
 
172
172
  #
173
173
  # Two accessor for setting security restrictions.
@@ -231,7 +231,6 @@ class RedCloth < String
231
231
  #
232
232
  def initialize( string, restrictions = [] )
233
233
  restrictions.each { |r| method( "#{ r }=" ).call( true ) }
234
- @rules = [:textile, :markdown]
235
234
  super( string )
236
235
  end
237
236
 
@@ -243,7 +242,7 @@ class RedCloth < String
243
242
  # #=>"And then? She <strong>fell</strong>!"
244
243
  #
245
244
  def to_html( *rules )
246
- rules = @rules if rules.empty?
245
+ rules = DEFAULT_RULES if rules.empty?
247
246
  # make our working copy
248
247
  text = self.dup
249
248
 
@@ -251,7 +250,7 @@ class RedCloth < String
251
250
  @shelf = []
252
251
  textile_rules = [:refs_textile, :block_textile_table, :block_textile_lists,
253
252
  :block_textile_prefix, :inline_textile_image, :inline_textile_link,
254
- :inline_textile_code, :inline_textile_span, :inline_textile_glyphs]
253
+ :inline_textile_code, :inline_textile_glyphs, :inline_textile_span]
255
254
  markdown_rules = [:refs_markdown, :block_markdown_setext, :block_markdown_atx, :block_markdown_rule,
256
255
  :block_markdown_bq, :block_markdown_lists,
257
256
  :inline_markdown_reflink, :inline_markdown_link]
@@ -269,6 +268,7 @@ class RedCloth < String
269
268
  # standard clean up
270
269
  incoming_entities text
271
270
  clean_white_space text
271
+ no_textile text
272
272
 
273
273
  # start processor
274
274
  pre_list = rip_offtags text
@@ -343,7 +343,7 @@ class RedCloth < String
343
343
  [ /\b([A-Z][A-Z0-9]{2,})\b(?:[(]([^)]*)[)])/, '<acronym title="\2">\1</acronym>' ], # 3+ uppercase acronym
344
344
  [ /(^|[^"][>\s])([A-Z][A-Z0-9 ]{2,})([^<a-z0-9]|$)/, '\1<span class="caps">\2</span>\3' ], # 3+ uppercase caps
345
345
  [ /(\.\s)?\s?--\s?/, '\1&#8212;' ], # em dash
346
- [ /\s->\s/, ' &rarr; ' ], # en dash
346
+ [ /\s->\s/, ' &rarr; ' ], # right arrow
347
347
  [ /\s-\s/, ' &#8211; ' ], # en dash
348
348
  [ /(\d+) ?x ?(\d+)/, '\1&#215;\2' ], # dimension sign
349
349
  [ /\b ?[(\[]TM[\])]/i, '&#8482;' ], # trademark
@@ -364,30 +364,36 @@ class RedCloth < String
364
364
  '~' => 'bottom'
365
365
  }
366
366
 
367
- QTAGS = [
367
+ QTAGS_1 = [
368
368
  ['**', 'b'],
369
369
  ['*', 'strong'],
370
370
  ['??', 'cite'],
371
- ['-', 'del'],
372
371
  ['__', 'i'],
372
+ ['^', 'sup'],
373
+ ['~', 'sub']
374
+ ]
375
+ QTAGS_RE_1 = /(#{ QTAGS_1.collect { |rc, ht| Regexp::quote( rc ) }.join( '|' ) })
376
+ (#{C})
377
+ (?::(\S+?))?
378
+ ([^\s\1]+?(?:[^\n]|\n(?!\n))*?)
379
+ \1/xm
380
+
381
+ QTAGS_2 = [
382
+ ['-', 'del'],
373
383
  ['_', 'em'],
374
384
  ['%', 'span'],
375
385
  ['+', 'ins'],
376
386
  ['^', 'sup'],
377
387
  ['~', 'sub']
378
- ].collect do |rc, ht|
379
- ttr = Regexp.quote(rc)
380
- punct = PUNCT.sub( Regexp::quote(rc), '' )
381
- re = /(^|[\s\>#{punct}{(\[])
382
- #{ttr}
383
- (#{C})
384
- (?::(\S+?))?
385
- ([^\s#{ttr}]+?(?:[^\n]|\n(?!\n))*?)
386
- ([#{punct}]*?)
387
- #{ttr}
388
- (?=[\s\])}<#{punct}]|$)/xm
389
- [re, ht]
390
- end
388
+ ]
389
+ QTAGS_RE_2 = /(^|[\s\>#{ PUNCT }{(\[])
390
+ (#{ QTAGS_2.collect { |rc, ht| Regexp::quote( rc ) }.join( '|' ) })
391
+ (#{C})
392
+ (?::(\S+?))?
393
+ ([^\s\2]+?(?:[^\n]|\n(?!\n))*?)
394
+ \2
395
+ (?=[\s\])}<#{ PUNCT }]|$)/xm
396
+ QTAGS = QTAGS_1 + QTAGS_2
391
397
 
392
398
  #
393
399
  # Flexible HTML escaping
@@ -457,6 +463,7 @@ class RedCloth < String
457
463
 
458
464
  tatts, fullrow = $~[1..2]
459
465
  tatts = pba( tatts, 'table' )
466
+ tatts = shelve( tatts ) if tatts
460
467
  rows = []
461
468
 
462
469
  fullrow.
@@ -475,9 +482,11 @@ class RedCloth < String
475
482
  catts, cell = pba( $1, 'td' ), $2 if cell =~ /^(_?#{S}#{A}#{C}\. ?)(.*)/
476
483
 
477
484
  unless cell.strip.empty?
485
+ catts = shelve( catts ) if catts
478
486
  cells << "\t\t\t<t#{ ctyp }#{ catts }>#{ cell }</t#{ ctyp }>"
479
487
  end
480
488
  end
489
+ ratts = shelve( ratts ) if ratts
481
490
  rows << "\t\t<tr#{ ratts }>\n#{ cells.join( "\n" ) }\n\t\t</tr>"
482
491
  end
483
492
  "\t<table#{ tatts }>\n#{ rows.join( "\n" ) }\n\t</table>\n\n"
@@ -511,6 +520,7 @@ class RedCloth < String
511
520
  unless depth.last == tl
512
521
  depth << tl
513
522
  atts = pba( atts )
523
+ atts = shelve( atts ) if atts
514
524
  lines[line_id] = "\t<#{ lT(tl) }l#{ atts }>\n\t<li>#{ content }"
515
525
  else
516
526
  lines[line_id] = "\t\t<li>#{ content }"
@@ -555,16 +565,11 @@ class RedCloth < String
555
565
  text.gsub!( /(.)\n(?! *[#*\s|])/, "\\1<br />" ) if @hard_breaks
556
566
  end
557
567
 
558
- BLOCKS_GROUP_RE = /(#{
559
- ['#', '*', '>'].collect do |sym|
560
- sym = Regexp::quote( sym )
561
- '(?:\n*[' + sym + ' ](?:[^\n]|\n+[' + sym + ' ]|\n(?!\n|\Z))+)'
562
- end.join '|'
563
- })|((?:[^\n]+|\n+ +|\n(?![#*\n]|\Z))+)/m
568
+ BLOCKS_GROUP_RE = /((?:^([#*> ])(?:[^\n]|\n+(?:\2|>)|\n(?!\n|\Z))+))|((?:[^\n]+|\n+ +|\n(?![#*\n]|\Z))+)/m
564
569
 
565
570
  def blocks( text, deep_code = false )
566
571
  text.gsub!( BLOCKS_GROUP_RE ) do |blk|
567
- plain = $2 ? true : false
572
+ plain = $3 ? true : false
568
573
 
569
574
  # skip blocks that are complex HTML
570
575
  if blk =~ /^<\/?(\w+).*>/ and not SIMPLE_HTML_TAGS.include? $1
@@ -609,10 +614,12 @@ class RedCloth < String
609
614
  def textile_bq( tag, atts, cite, content )
610
615
  cite, cite_title = check_refs( cite )
611
616
  cite = " cite=\"#{ cite }\"" if cite
617
+ atts = shelve( atts ) if atts
612
618
  "\t<blockquote#{ cite }>\n\t\t<p#{ atts }>#{ content }</p>\n\t</blockquote>"
613
619
  end
614
620
 
615
621
  def textile_p( tag, atts, cite, content )
622
+ atts = shelve( atts ) if atts
616
623
  "\t<#{ tag }#{ atts }>#{ content }</#{ tag }>"
617
624
  end
618
625
 
@@ -626,6 +633,7 @@ class RedCloth < String
626
633
  def textile_fn_( tag, num, atts, cite, content )
627
634
  atts << " id=\"fn#{ num }\""
628
635
  content = "<sup>#{ num }</sup> #{ content }"
636
+ atts = shelve( atts ) if atts
629
637
  "\t<p#{ atts }>#{ content }</p>"
630
638
  end
631
639
 
@@ -700,16 +708,27 @@ class RedCloth < String
700
708
  end
701
709
 
702
710
  def inline_textile_span( text )
703
- QTAGS.each do |ttr, ht|
704
- text.gsub!(ttr) do |m|
711
+ text.gsub!( QTAGS_RE_1 ) do |m|
705
712
 
706
- start,atts,cite,content,tend = $~[1..5]
707
- atts = pba( atts )
708
- atts << " cite=\"#{ cite }\"" if cite
713
+ qtag,atts,cite,content = $~[1..4]
714
+ ht = QTAGS.assoc( qtag ).last
715
+ atts = pba( atts )
716
+ atts << " cite=\"#{ cite }\"" if cite
717
+ atts = shelve( atts ) if atts
709
718
 
710
- "#{ start }<#{ ht }#{ atts }>#{ content }#{ tend }</#{ ht }>"
719
+ "<#{ ht }#{ atts }>#{ content }</#{ ht }>"
720
+
721
+ end
722
+ text.gsub!( QTAGS_RE_2 ) do |m|
723
+
724
+ sta,qtag,atts,cite,content = $~[1..5]
725
+ ht = QTAGS.assoc( qtag ).last
726
+ atts = pba( atts )
727
+ atts << " cite=\"#{ cite }\"" if cite
728
+ atts = shelve( atts ) if atts
729
+
730
+ "#{ sta }<#{ ht }#{ atts }>#{ content }</#{ ht }>"
711
731
 
712
- end
713
732
  end
714
733
  end
715
734
 
@@ -890,6 +909,13 @@ class RedCloth < String
890
909
  text.gsub!( /&(?![#a-z0-9]+;)/i, "x%x%" )
891
910
  end
892
911
 
912
+ def no_textile( text )
913
+ text.gsub!( /(^|\s)==([^=]+.*?)==(\s|$)?/,
914
+ '\1<notextile>\2</notextile>\3' )
915
+ text.gsub!( /^ *==([^=]+.*?)==/m,
916
+ '\1<notextile>\2</notextile>\3' )
917
+ end
918
+
893
919
  def clean_white_space( text )
894
920
  # normalize line breaks
895
921
  text.gsub!( /\r\n/, "\n" )
@@ -908,7 +934,7 @@ class RedCloth < String
908
934
  indt = 0
909
935
  while text !~ /^ {#{indt}}\S/
910
936
  indt += 1
911
- end
937
+ end unless text.empty?
912
938
  if indt.nonzero?
913
939
  text.gsub!( /^ {#{indt}}/, '' )
914
940
  end
@@ -935,9 +961,7 @@ class RedCloth < String
935
961
  text.gsub!( ALLTAG_MATCH ) do |line|
936
962
  ## matches are off if we're between <code>, <pre> etc.
937
963
  if $1
938
- if @filter_html
939
- htmlesc( line, :NoQuotes )
940
- elsif line =~ OFFTAG_OPEN
964
+ if line =~ OFFTAG_OPEN
941
965
  codepre += 1
942
966
  elsif line =~ OFFTAG_CLOSE
943
967
  codepre -= 1
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.1
3
3
  specification_version: 1
4
4
  name: RedCloth
5
5
  version: !ruby/object:Gem::Version
6
- version: 3.0.0
7
- date: 2004-11-09
6
+ version: 3.0.1
7
+ date: 2005-01-18
8
8
  summary: RedCloth is a module for using Textile and Markdown in Ruby. Textile and Markdown are text formats. A very simple text format. Another stab at making readable text that can be converted to HTML.
9
9
  require_paths:
10
10
  - lib
@@ -48,6 +48,7 @@ files:
48
48
  - tests/lists.yml
49
49
  - tests/markdown.yml
50
50
  - lib/redcloth.rb
51
+ - lib/rctodb.rb
51
52
  - run-tests.rb
52
53
  test_files: []
53
54
  rdoc_options: []