gf-Soks 1.0.4

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