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.
- data/bin/redcloth +1 -1
- data/lib/rctodb.rb +1107 -0
- data/lib/redcloth.rb +63 -39
- metadata +3 -2
data/bin/redcloth
CHANGED
data/lib/rctodb.rb
ADDED
@@ -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 <b>bold</b> 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%/, '&' )
|
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’\2' ], # single closing
|
351
|
+
[ /([^\s\[{(>])\'/, '\1’' ], # single closing
|
352
|
+
[ /\'(?=\s|s\b|[#{PUNCT}])/, '’' ], # single closing
|
353
|
+
[ /\'/, '‘' ], # single opening
|
354
|
+
# [ /([^\s\[{(])?"(\s|:|$)/, '\1”\2' ], # double closing
|
355
|
+
[ /([^\s\[{(>])"/, '\1”' ], # double closing
|
356
|
+
[ /"(?=\s|[#{PUNCT}])/, '”' ], # double closing
|
357
|
+
[ /"/, '“' ], # double opening
|
358
|
+
[ /\b( )?\.{3}/, '\1…' ], # 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—' ], # em dash
|
363
|
+
[ /\s->\s/, ' → ' ], # en dash
|
364
|
+
[ /\s-\s/, ' – ' ], # en dash
|
365
|
+
[ /(\d+) ?x ?(\d+)/, '\1×\2' ], # dimension sign
|
366
|
+
[ /\b ?[(\[]TM[\])]/i, '™' ], # trademark
|
367
|
+
[ /\b ?[(\[]R[\])]/i, '®' ], # registered
|
368
|
+
[ /\b ?[(\[]C[\])]/i, '©' ] # 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!( '&', '&' )
|
414
|
+
str.gsub!( '"', '"' ) if mode != :NoQuotes
|
415
|
+
str.gsub!( "'", ''' ) if mode == :Quotes
|
416
|
+
str.gsub!( '<', '<')
|
417
|
+
str.gsub!( '>', '>')
|
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
|
+
|
data/lib/redcloth.rb
CHANGED
@@ -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.
|
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 =
|
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, :
|
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—' ], # em dash
|
346
|
-
[ /\s->\s/, ' → ' ], #
|
346
|
+
[ /\s->\s/, ' → ' ], # right arrow
|
347
347
|
[ /\s-\s/, ' – ' ], # en dash
|
348
348
|
[ /(\d+) ?x ?(\d+)/, '\1×\2' ], # dimension sign
|
349
349
|
[ /\b ?[(\[]TM[\])]/i, '™' ], # trademark
|
@@ -364,30 +364,36 @@ class RedCloth < String
|
|
364
364
|
'~' => 'bottom'
|
365
365
|
}
|
366
366
|
|
367
|
-
|
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
|
-
]
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
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 = $
|
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
|
-
|
704
|
-
text.gsub!(ttr) do |m|
|
711
|
+
text.gsub!( QTAGS_RE_1 ) do |m|
|
705
712
|
|
706
|
-
|
707
|
-
|
708
|
-
|
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
|
-
|
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
|
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.
|
7
|
-
date:
|
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: []
|