fnando-kitabu 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. data/History.txt +59 -0
  2. data/License.txt +20 -0
  3. data/README.markdown +216 -0
  4. data/Rakefile +68 -0
  5. data/TODO.txt +2 -0
  6. data/app_generators/kitabu/USAGE +5 -0
  7. data/app_generators/kitabu/kitabu_generator.rb +68 -0
  8. data/app_generators/kitabu/templates/Rakefile +3 -0
  9. data/app_generators/kitabu/templates/config.yml +8 -0
  10. data/app_generators/kitabu/templates/css/active4d.css +114 -0
  11. data/app_generators/kitabu/templates/css/blackboard.css +88 -0
  12. data/app_generators/kitabu/templates/css/dawn.css +121 -0
  13. data/app_generators/kitabu/templates/css/eiffel.css +121 -0
  14. data/app_generators/kitabu/templates/css/idle.css +62 -0
  15. data/app_generators/kitabu/templates/css/iplastic.css +80 -0
  16. data/app_generators/kitabu/templates/css/lazy.css +73 -0
  17. data/app_generators/kitabu/templates/css/mac_classic.css +123 -0
  18. data/app_generators/kitabu/templates/css/slush_poppies.css +85 -0
  19. data/app_generators/kitabu/templates/css/sunburst.css +180 -0
  20. data/app_generators/kitabu/templates/layouts/boom/layout.css +469 -0
  21. data/app_generators/kitabu/templates/layouts/boom/layout.html +37 -0
  22. data/app_generators/kitabu/templates/user.css +0 -0
  23. data/bin/kitabu +17 -0
  24. data/kitabu.gemspec +79 -0
  25. data/lib/kitabu/base.rb +332 -0
  26. data/lib/kitabu/blackcloth.rb +165 -0
  27. data/lib/kitabu/redcloth.rb +963 -0
  28. data/lib/kitabu/tasks.rb +119 -0
  29. data/lib/kitabu.rb +18 -0
  30. data/themes/active4d.css +114 -0
  31. data/themes/all_hallows_eve.css +72 -0
  32. data/themes/amy.css +147 -0
  33. data/themes/blackboard.css +88 -0
  34. data/themes/brilliance_black.css +605 -0
  35. data/themes/brilliance_dull.css +599 -0
  36. data/themes/cobalt.css +149 -0
  37. data/themes/dawn.css +121 -0
  38. data/themes/eiffel.css +121 -0
  39. data/themes/espresso_libre.css +109 -0
  40. data/themes/idle.css +62 -0
  41. data/themes/iplastic.css +80 -0
  42. data/themes/lazy.css +73 -0
  43. data/themes/mac_classic.css +123 -0
  44. data/themes/magicwb_amiga.css +104 -0
  45. data/themes/pastels_on_dark.css +188 -0
  46. data/themes/slush_poppies.css +85 -0
  47. data/themes/spacecadet.css +51 -0
  48. data/themes/sunburst.css +180 -0
  49. data/themes/twilight.css +137 -0
  50. data/themes/zenburnesque.css +91 -0
  51. metadata +144 -0
@@ -0,0 +1,963 @@
1
+ class RedCloth < String
2
+
3
+ VERSION = '3.0.4'
4
+ DEFAULT_RULES = [:textile, :markdown]
5
+
6
+ #
7
+ # Two accessor for setting security restrictions.
8
+ #
9
+ # This is a nice thing if you're using RedCloth for
10
+ # formatting in public places (e.g. Wikis) where you
11
+ # don't want users to abuse HTML for bad things.
12
+ #
13
+ # If +:filter_html+ is set, HTML which wasn't
14
+ # created by the Textile processor will be escaped.
15
+ #
16
+ # If +:filter_styles+ is set, it will also disable
17
+ # the style markup specifier. ('{color: red}')
18
+ #
19
+ attr_accessor :filter_html, :filter_styles
20
+
21
+ #
22
+ # Accessor for toggling hard breaks.
23
+ #
24
+ # If +:hard_breaks+ is set, single newlines will
25
+ # be converted to HTML break tags. This is the
26
+ # default behavior for traditional RedCloth.
27
+ #
28
+ attr_accessor :hard_breaks
29
+
30
+ # Accessor for toggling lite mode.
31
+ #
32
+ # In lite mode, block-level rules are ignored. This means
33
+ # that tables, paragraphs, lists, and such aren't available.
34
+ # Only the inline markup for bold, italics, entities and so on.
35
+ #
36
+ # r = RedCloth.new( "And then? She *fell*!", [:lite_mode] )
37
+ # r.to_html
38
+ # #=> "And then? She <strong>fell</strong>!"
39
+ #
40
+ attr_accessor :lite_mode
41
+
42
+ #
43
+ # Accessor for toggling span caps.
44
+ #
45
+ # Textile places `span' tags around capitalized
46
+ # words by default, but this wreaks havoc on Wikis.
47
+ # If +:no_span_caps+ is set, this will be
48
+ # suppressed.
49
+ #
50
+ attr_accessor :no_span_caps
51
+
52
+ #
53
+ # Establishes the markup predence. Available rules include:
54
+ #
55
+ # == Textile Rules
56
+ #
57
+ # The following textile rules can be set individually. Or add the complete
58
+ # set of rules with the single :textile rule, which supplies the rule set in
59
+ # the following precedence:
60
+ #
61
+ # refs_textile:: Textile references (i.e. [hobix]http://hobix.com/)
62
+ # block_textile_table:: Textile table block structures
63
+ # block_textile_lists:: Textile list structures
64
+ # block_textile_prefix:: Textile blocks with prefixes (i.e. bq., h2., etc.)
65
+ # inline_textile_image:: Textile inline images
66
+ # inline_textile_link:: Textile inline links
67
+ # inline_textile_span:: Textile inline spans
68
+ # glyphs_textile:: Textile entities (such as em-dashes and smart quotes)
69
+ #
70
+ # == Markdown
71
+ #
72
+ # refs_markdown:: Markdown references (for example: [hobix]: http://hobix.com/)
73
+ # block_markdown_setext:: Markdown setext headers
74
+ # block_markdown_atx:: Markdown atx headers
75
+ # block_markdown_rule:: Markdown horizontal rules
76
+ # block_markdown_bq:: Markdown blockquotes
77
+ # block_markdown_lists:: Markdown lists
78
+ # inline_markdown_link:: Markdown links
79
+ attr_accessor :rules
80
+
81
+ # Returns a new RedCloth object, based on _string_ and
82
+ # enforcing all the included _restrictions_.
83
+ #
84
+ # r = RedCloth.new( "h1. A <b>bold</b> man", [:filter_html] )
85
+ # r.to_html
86
+ # #=>"<h1>A &lt;b&gt;bold&lt;/b&gt; man</h1>"
87
+ #
88
+ def initialize( string, restrictions = [] )
89
+ restrictions.each { |r| method( "#{ r }=" ).call( true ) }
90
+ super( string )
91
+ end
92
+
93
+ #
94
+ # Generates HTML from the Textile contents.
95
+ #
96
+ # r = RedCloth.new( "And then? She *fell*!" )
97
+ # r.to_html( true )
98
+ # #=>"And then? She <strong>fell</strong>!"
99
+ #
100
+ def to_html( *rules )
101
+ rules = DEFAULT_RULES if rules.empty?
102
+ # make our working copy
103
+ text = self.dup
104
+
105
+ @urlrefs = {}
106
+ @shelf = []
107
+ textile_rules = [:refs_textile, :block_textile_table, :block_textile_lists,
108
+ :block_textile_prefix, :inline_textile_image, :inline_textile_link,
109
+ :inline_textile_code, :inline_textile_span, :glyphs_textile]
110
+ markdown_rules = [:refs_markdown, :block_markdown_setext, :block_markdown_atx, :block_markdown_rule,
111
+ :block_markdown_bq, :block_markdown_lists,
112
+ :inline_markdown_reflink, :inline_markdown_link]
113
+ @rules = rules.collect do |rule|
114
+ case rule
115
+ when :markdown
116
+ markdown_rules
117
+ when :textile
118
+ textile_rules
119
+ else
120
+ rule
121
+ end
122
+ end.flatten
123
+
124
+ # standard clean up
125
+ incoming_entities text
126
+ clean_white_space text
127
+
128
+ # start processor
129
+ @pre_list = []
130
+ rip_offtags text
131
+ no_textile text
132
+ hard_break text
133
+ unless @lite_mode
134
+ refs text
135
+ blocks text
136
+ end
137
+ inline text
138
+ smooth_offtags text
139
+
140
+ retrieve text
141
+
142
+ text.gsub!( /<\/?notextile>/, '' )
143
+ text.gsub!( /x%x%/, '&#38;' )
144
+ clean_html text if filter_html
145
+ text.strip!
146
+ text
147
+
148
+ end
149
+
150
+ #######
151
+ private
152
+ #######
153
+ #
154
+ # Mapping of 8-bit ASCII codes to HTML numerical entity equivalents.
155
+ # (from PyTextile)
156
+ #
157
+ TEXTILE_TAGS =
158
+
159
+ [[128, 8364], [129, 0], [130, 8218], [131, 402], [132, 8222], [133, 8230],
160
+ [134, 8224], [135, 8225], [136, 710], [137, 8240], [138, 352], [139, 8249],
161
+ [140, 338], [141, 0], [142, 0], [143, 0], [144, 0], [145, 8216], [146, 8217],
162
+ [147, 8220], [148, 8221], [149, 8226], [150, 8211], [151, 8212], [152, 732],
163
+ [153, 8482], [154, 353], [155, 8250], [156, 339], [157, 0], [158, 0], [159, 376]].
164
+
165
+ collect! do |a, b|
166
+ [a.chr, ( b.zero? and "" or "&#{ b };" )]
167
+ end
168
+
169
+ #
170
+ # Regular expressions to convert to HTML.
171
+ #
172
+ A_HLGN = /(?:(?:<>|<|>|\=|[()]+)+)/
173
+ A_VLGN = /[\-^~]/
174
+ C_CLAS = '(?:\([^)]+\))'
175
+ C_LNGE = '(?:\[[^\]]+\])'
176
+ C_STYL = '(?:\{[^}]+\})'
177
+ S_CSPN = '(?:\\\\\d+)'
178
+ S_RSPN = '(?:/\d+)'
179
+ A = "(?:#{A_HLGN}?#{A_VLGN}?|#{A_VLGN}?#{A_HLGN}?)"
180
+ S = "(?:#{S_CSPN}?#{S_RSPN}|#{S_RSPN}?#{S_CSPN}?)"
181
+ C = "(?:#{C_CLAS}?#{C_STYL}?#{C_LNGE}?|#{C_STYL}?#{C_LNGE}?#{C_CLAS}?|#{C_LNGE}?#{C_STYL}?#{C_CLAS}?)"
182
+ # PUNCT = Regexp::quote( '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~' )
183
+ PUNCT = Regexp::quote( '!"#$%&\'*+,-./:;=?@\\^_`|~' )
184
+ PUNCT_NOQ = Regexp::quote( '!"#$&\',./:;=?@\\`|' )
185
+ PUNCT_Q = Regexp::quote( '*-_+^~%' )
186
+ HYPERLINK = '(\S+?)([^\w\s/;=\?]*?)(?=\s|<|$)'
187
+
188
+ # Text markup tags, don't conflict with block tags
189
+ SIMPLE_HTML_TAGS = [
190
+ 'tt', 'b', 'i', 'big', 'small', 'em', 'strong', 'dfn', 'code',
191
+ 'samp', 'kbd', 'var', 'cite', 'abbr', 'acronym', 'a', 'img', 'br',
192
+ 'br', 'map', 'q', 'sub', 'sup', 'span', 'bdo'
193
+ ]
194
+
195
+ QTAGS = [
196
+ ['**', 'b'],
197
+ ['*', 'strong'],
198
+ ['??', 'cite', :limit],
199
+ ['-', 'del', :limit],
200
+ ['__', 'i'],
201
+ ['_', 'em', :limit],
202
+ ['%', 'span', :limit],
203
+ ['+', 'ins', :limit],
204
+ ['^', 'sup'],
205
+ ['~', 'sub']
206
+ ]
207
+ QTAGS.collect! do |rc, ht, rtype|
208
+ rcq = Regexp::quote rc
209
+ re =
210
+ case rtype
211
+ when :limit
212
+ /(\W)
213
+ (#{rcq})
214
+ (#{C})
215
+ (?::(\S+?))?
216
+ (\S.*?\S|\S)
217
+ #{rcq}
218
+ (?=\W)/x
219
+ else
220
+ /(#{rcq})
221
+ (#{C})
222
+ (?::(\S+))?
223
+ (\S.*?\S|\S)
224
+ #{rcq}/xm
225
+ end
226
+ [rc, ht, re, rtype]
227
+ end
228
+
229
+ # Elements to handle
230
+ GLYPHS = [
231
+ # [ /([^\s\[{(>])?\'([dmst]\b|ll\b|ve\b|\s|:|$)/, '\1&#8217;\2' ], # single closing
232
+ [ /([^\s\[{(>#{PUNCT_Q}][#{PUNCT_Q}]*)\'/, '\1&#8217;' ], # single closing
233
+ [ /\'(?=[#{PUNCT_Q}]*(s\b|[\s#{PUNCT_NOQ}]))/, '&#8217;' ], # single closing
234
+ [ /\'/, '&#8216;' ], # single opening
235
+ [ /</, '&lt;' ], # less-than
236
+ [ />/, '&gt;' ], # greater-than
237
+ # [ /([^\s\[{(])?"(\s|:|$)/, '\1&#8221;\2' ], # double closing
238
+ [ /([^\s\[{(>#{PUNCT_Q}][#{PUNCT_Q}]*)"/, '\1&#8221;' ], # double closing
239
+ [ /"(?=[#{PUNCT_Q}]*[\s#{PUNCT_NOQ}])/, '&#8221;' ], # double closing
240
+ [ /"/, '&#8220;' ], # double opening
241
+ [ /\b( )?\.{3}/, '\1&#8230;' ], # ellipsis
242
+ [ /\b([A-Z][A-Z0-9]{2,})\b(?:[(]([^)]*)[)])/, '<acronym title="\2">\1</acronym>' ], # 3+ uppercase acronym
243
+ [ /(^|[^"][>\s])([A-Z][A-Z0-9 ]+[A-Z0-9])([^<A-Za-z0-9]|$)/, '\1<span class="caps">\2</span>\3', :no_span_caps ], # 3+ uppercase caps
244
+ [ /(\.\s)?\s?--\s?/, '\1&#8212;' ], # em dash
245
+ [ /\s->\s/, ' &rarr; ' ], # right arrow
246
+ [ /\s-\s/, ' &#8211; ' ], # en dash
247
+ [ /(\d+) ?x ?(\d+)/, '\1&#215;\2' ], # dimension sign
248
+ [ /\b ?[(\[]TM[\])]/i, '&#8482;' ], # trademark
249
+ [ /\b ?[(\[]R[\])]/i, '&#174;' ], # registered
250
+ [ /\b ?[(\[]C[\])]/i, '&#169;' ] # copyright
251
+ ]
252
+
253
+ H_ALGN_VALS = {
254
+ '<' => 'left',
255
+ '=' => 'center',
256
+ '>' => 'right',
257
+ '<>' => 'justify'
258
+ }
259
+
260
+ V_ALGN_VALS = {
261
+ '^' => 'top',
262
+ '-' => 'middle',
263
+ '~' => 'bottom'
264
+ }
265
+
266
+ #
267
+ # Flexible HTML escaping
268
+ #
269
+ def htmlesc( str, mode )
270
+ str.gsub!( '&', '&amp;' )
271
+ str.gsub!( '"', '&quot;' ) if mode != :NoQuotes
272
+ str.gsub!( "'", '&#039;' ) if mode == :Quotes
273
+ str.gsub!( '<', '&lt;')
274
+ str.gsub!( '>', '&gt;')
275
+ end
276
+
277
+ # Search and replace for Textile glyphs (quotes, dashes, other symbols)
278
+ def pgl( text )
279
+ GLYPHS.each do |re, resub, tog|
280
+ next if tog and method( tog ).call
281
+ text.gsub! re, resub
282
+ end
283
+ end
284
+
285
+ # Parses Textile attribute lists and builds an HTML attribute string
286
+ def pba( text_in, element = "" )
287
+
288
+ return '' unless text_in
289
+
290
+ style = []
291
+ text = text_in.dup
292
+ if element == 'td'
293
+ colspan = $1 if text =~ /\\(\d+)/
294
+ rowspan = $1 if text =~ /\/(\d+)/
295
+ style << "vertical-align:#{ v_align( $& ) };" if text =~ A_VLGN
296
+ end
297
+
298
+ style << "#{ $1 };" if not filter_styles and
299
+ text.sub!( /\{([^}]*)\}/, '' )
300
+
301
+ lang = $1 if
302
+ text.sub!( /\[([^)]+?)\]/, '' )
303
+
304
+ cls = $1 if
305
+ text.sub!( /\(([^()]+?)\)/, '' )
306
+
307
+ style << "padding-left:#{ $1.length }em;" if
308
+ text.sub!( /([(]+)/, '' )
309
+
310
+ style << "padding-right:#{ $1.length }em;" if text.sub!( /([)]+)/, '' )
311
+
312
+ style << "text-align:#{ h_align( $& ) };" if text =~ A_HLGN
313
+
314
+ cls, id = $1, $2 if cls =~ /^(.*?)#(.*)$/
315
+
316
+ atts = ''
317
+ atts << " style=\"#{ style.join }\"" unless style.empty?
318
+ atts << " class=\"#{ cls }\"" unless cls.to_s.empty?
319
+ atts << " lang=\"#{ lang }\"" if lang
320
+ atts << " id=\"#{ id }\"" if id
321
+ atts << " colspan=\"#{ colspan }\"" if colspan
322
+ atts << " rowspan=\"#{ rowspan }\"" if rowspan
323
+
324
+ atts
325
+ end
326
+
327
+ TABLE_RE = /^(?:table(_?#{S}#{A}#{C})\. ?\n)?^(#{A}#{C}\.? ?\|.*?\|)(\n\n|\Z)/m
328
+
329
+ # Parses a Textile table block, building HTML from the result.
330
+ def block_textile_table( text )
331
+ text.gsub!( TABLE_RE ) do |matches|
332
+
333
+ tatts, fullrow = $~[1..2]
334
+ tatts = pba( tatts, 'table' )
335
+ tatts = shelve( tatts ) if tatts
336
+ rows = []
337
+
338
+ fullrow.
339
+ split( /\|$/m ).
340
+ delete_if { |x| x.empty? }.
341
+ each do |row|
342
+
343
+ ratts, row = pba( $1, 'tr' ), $2 if row =~ /^(#{A}#{C}\. )(.*)/m
344
+
345
+ cells = []
346
+ row.split( '|' ).each do |cell|
347
+ ctyp = 'd'
348
+ ctyp = 'h' if cell =~ /^_/
349
+
350
+ catts = ''
351
+ catts, cell = pba( $1, 'td' ), $2 if cell =~ /^(_?#{S}#{A}#{C}\. ?)(.*)/
352
+
353
+ unless cell.strip.empty?
354
+ catts = shelve( catts ) if catts
355
+ cells << "\t\t\t<t#{ ctyp }#{ catts }>#{ cell }</t#{ ctyp }>"
356
+ end
357
+ end
358
+ ratts = shelve( ratts ) if ratts
359
+ rows << "\t\t<tr#{ ratts }>\n#{ cells.join( "\n" ) }\n\t\t</tr>"
360
+ end
361
+ "\t<table#{ tatts }>\n#{ rows.join( "\n" ) }\n\t</table>\n\n"
362
+ end
363
+ end
364
+
365
+ LISTS_RE = /^([#*]+?#{C} .*?)$(?![^#*])/m
366
+ LISTS_CONTENT_RE = /^([#*]+)(#{A}#{C}) (.*)$/m
367
+
368
+ # Parses Textile lists and generates HTML
369
+ def block_textile_lists( text )
370
+ text.gsub!( LISTS_RE ) do |match|
371
+ lines = match.split( /\n/ )
372
+ last_line = -1
373
+ depth = []
374
+ lines.each_with_index do |line, line_id|
375
+ if line =~ LISTS_CONTENT_RE
376
+ tl,atts,content = $~[1..3]
377
+ if depth.last
378
+ if depth.last.length > tl.length
379
+ (depth.length - 1).downto(0) do |i|
380
+ break if depth[i].length == tl.length
381
+ lines[line_id - 1] << "</li>\n\t</#{ lT( depth[i] ) }l>\n\t"
382
+ depth.pop
383
+ end
384
+ end
385
+ if depth.last and depth.last.length == tl.length
386
+ lines[line_id - 1] << '</li>'
387
+ end
388
+ end
389
+ unless depth.last == tl
390
+ depth << tl
391
+ atts = pba( atts )
392
+ atts = shelve( atts ) if atts
393
+ lines[line_id] = "\t<#{ lT(tl) }l#{ atts }>\n\t<li>#{ content }"
394
+ else
395
+ lines[line_id] = "\t\t<li>#{ content }"
396
+ end
397
+ last_line = line_id
398
+
399
+ else
400
+ last_line = line_id
401
+ end
402
+ if line_id - last_line > 1 or line_id == lines.length - 1
403
+ depth.delete_if do |v|
404
+ lines[last_line] << "</li>\n\t</#{ lT( v ) }l>"
405
+ end
406
+ end
407
+ end
408
+ lines.join( "\n" )
409
+ end
410
+ end
411
+
412
+ CODE_RE = /(\W)
413
+ @
414
+ (?:\|(\w+?)\|)?
415
+ (.+?)
416
+ @
417
+ (?=\W)/x
418
+
419
+ def inline_textile_code( text )
420
+ text.gsub!( CODE_RE ) do |m|
421
+ before,lang,code,after = $~[1..4]
422
+ lang = " lang=\"#{ lang }\"" if lang
423
+ rip_offtags( "#{ before }<code#{ lang }>#{ code }</code>#{ after }" )
424
+ end
425
+ end
426
+
427
+ def lT( text )
428
+ text =~ /\#$/ ? 'o' : 'u'
429
+ end
430
+
431
+ def hard_break( text )
432
+ text.gsub!( /(.)\n(?!\Z| *([#*=]+(\s|$)|[{|]))/, "\\1<br />" ) if hard_breaks
433
+ end
434
+
435
+ BLOCKS_GROUP_RE = /\n{2,}(?! )/m
436
+
437
+ def blocks( text, deep_code = false )
438
+ text.replace( text.split( BLOCKS_GROUP_RE ).collect do |blk|
439
+ plain = blk !~ /\A[#*> ]/
440
+
441
+ # skip blocks that are complex HTML
442
+ if blk =~ /^<\/?(\w+).*>/ and not SIMPLE_HTML_TAGS.include? $1
443
+ blk
444
+ else
445
+ # search for indentation levels
446
+ blk.strip!
447
+ if blk.empty?
448
+ blk
449
+ else
450
+ code_blk = nil
451
+ blk.gsub!( /((?:\n(?:\n^ +[^\n]*)+)+)/m ) do |iblk|
452
+ flush_left iblk
453
+ blocks iblk, plain
454
+ iblk.gsub( /^(\S)/, "\t\\1" )
455
+ if plain
456
+ code_blk = iblk; ""
457
+ else
458
+ iblk
459
+ end
460
+ end
461
+
462
+ block_applied = 0
463
+ @rules.each do |rule_name|
464
+ block_applied += 1 if ( rule_name.to_s.match /^block_/ and method( rule_name ).call( blk ) )
465
+ end
466
+ if block_applied.zero?
467
+ if deep_code
468
+ blk = "\t<pre><code>#{ blk }</code></pre>"
469
+ else
470
+ blk = "\t<p>#{ blk }</p>"
471
+ end
472
+ end
473
+ # hard_break blk
474
+ blk + "\n#{ code_blk }"
475
+ end
476
+ end
477
+
478
+ end.join( "\n\n" ) )
479
+ end
480
+
481
+ def textile_bq( tag, atts, cite, content )
482
+ cite, cite_title = check_refs( cite )
483
+ cite = " cite=\"#{ cite }\"" if cite
484
+ atts = shelve( atts ) if atts
485
+ "\t<blockquote#{ cite }>\n\t\t<p#{ atts }>#{ content }</p>\n\t</blockquote>"
486
+ end
487
+
488
+ def textile_p( tag, atts, cite, content )
489
+ atts = shelve( atts ) if atts
490
+ "\t<#{ tag }#{ atts }>#{ content }</#{ tag }>"
491
+ end
492
+
493
+ alias textile_h1 textile_p
494
+ alias textile_h2 textile_p
495
+ alias textile_h3 textile_p
496
+ alias textile_h4 textile_p
497
+ alias textile_h5 textile_p
498
+ alias textile_h6 textile_p
499
+
500
+ def textile_fn_( tag, num, atts, cite, content )
501
+ atts << " id=\"fn#{ num }\""
502
+ content = "<sup>#{ num }</sup> #{ content }"
503
+ atts = shelve( atts ) if atts
504
+ "\t<p#{ atts }>#{ content }</p>"
505
+ end
506
+
507
+ BLOCK_RE = /^(([a-z]+)(\d*))(#{A}#{C})\.(?::(\S+))? (.*)$/m
508
+
509
+ def block_textile_prefix( text )
510
+ if text =~ BLOCK_RE
511
+ tag,tagpre,num,atts,cite,content = $~[1..6]
512
+ atts = pba( atts )
513
+
514
+ # pass to prefix handler
515
+ if respond_to? "textile_#{ tag }", true
516
+ text.gsub!( $&, method( "textile_#{ tag }" ).call( tag, atts, cite, content ) )
517
+ elsif respond_to? "textile_#{ tagpre }_", true
518
+ text.gsub!( $&, method( "textile_#{ tagpre }_" ).call( tagpre, num, atts, cite, content ) )
519
+ end
520
+ end
521
+ end
522
+
523
+ SETEXT_RE = /\A(.+?)\n([=-])[=-]* *$/m
524
+ def block_markdown_setext( text )
525
+ if text =~ SETEXT_RE
526
+ tag = if $2 == "="; "h1"; else; "h2"; end
527
+ blk, cont = "<#{ tag }>#{ $1 }</#{ tag }>", $'
528
+ blocks cont
529
+ text.replace( blk + cont )
530
+ end
531
+ end
532
+
533
+ ATX_RE = /\A(\#{1,6}) # $1 = string of #'s
534
+ [ ]*
535
+ (.+?) # $2 = Header text
536
+ [ ]*
537
+ \#* # optional closing #'s (not counted)
538
+ $/x
539
+ def block_markdown_atx( text )
540
+ if text =~ ATX_RE
541
+ tag = "h#{ $1.length }"
542
+ blk, cont = "<#{ tag }>#{ $2 }</#{ tag }>\n\n", $'
543
+ blocks cont
544
+ text.replace( blk + cont )
545
+ end
546
+ end
547
+
548
+ MARKDOWN_BQ_RE = /\A(^ *> ?.+$(.+\n)*\n*)+/m
549
+
550
+ def block_markdown_bq( text )
551
+ text.gsub!( MARKDOWN_BQ_RE ) do |blk|
552
+ blk.gsub!( /^ *> ?/, '' )
553
+ flush_left blk
554
+ blocks blk
555
+ blk.gsub!( /^(\S)/, "\t\\1" )
556
+ "<blockquote>\n#{ blk }\n</blockquote>\n\n"
557
+ end
558
+ end
559
+
560
+ MARKDOWN_RULE_RE = /^(#{
561
+ ['*', '-', '_'].collect { |ch| '( ?' + Regexp::quote( ch ) + ' ?){3,}' }.join( '|' )
562
+ })$/
563
+
564
+ def block_markdown_rule( text )
565
+ text.gsub!( MARKDOWN_RULE_RE ) do |blk|
566
+ "<hr />"
567
+ end
568
+ end
569
+
570
+ # XXX TODO XXX
571
+ def block_markdown_lists( text )
572
+ end
573
+
574
+ def inline_textile_span( text )
575
+ QTAGS.each do |qtag_rc, ht, qtag_re, rtype|
576
+ text.gsub!( qtag_re ) do |m|
577
+
578
+ case rtype
579
+ when :limit
580
+ sta,qtag,atts,cite,content = $~[1..5]
581
+ else
582
+ qtag,atts,cite,content = $~[1..4]
583
+ sta = ''
584
+ end
585
+ atts = pba( atts )
586
+ atts << " cite=\"#{ cite }\"" if cite
587
+ atts = shelve( atts ) if atts
588
+
589
+ "#{ sta }<#{ ht }#{ atts }>#{ content }</#{ ht }>"
590
+
591
+ end
592
+ end
593
+ end
594
+
595
+ LINK_RE = /
596
+ ([\s\[{(]|[#{PUNCT}])? # $pre
597
+ " # start
598
+ (#{C}) # $atts
599
+ ([^"]+?) # $text
600
+ \s?
601
+ (?:\(([^)]+?)\)(?="))? # $title
602
+ ":
603
+ (\S+?) # $url
604
+ (\/)? # $slash
605
+ ([^\w\/;]*?) # $post
606
+ (?=<|\s|$)
607
+ /x
608
+
609
+ def inline_textile_link( text )
610
+ text.gsub!( LINK_RE ) do |m|
611
+ pre,atts,text,title,url,slash,post = $~[1..7]
612
+
613
+ url, url_title = check_refs( url )
614
+ title ||= url_title
615
+
616
+ atts = pba( atts )
617
+ atts = " href=\"#{ url }#{ slash }\"#{ atts }"
618
+ atts << " title=\"#{ title }\"" if title
619
+ atts = shelve( atts ) if atts
620
+
621
+ "#{ pre }<a#{ atts }>#{ text }</a>#{ post }"
622
+ end
623
+ end
624
+
625
+ MARKDOWN_REFLINK_RE = /
626
+ \[([^\[\]]+)\] # $text
627
+ [ ]? # opt. space
628
+ (?:\n[ ]*)? # one optional newline followed by spaces
629
+ \[(.*?)\] # $id
630
+ /x
631
+
632
+ def inline_markdown_reflink( text )
633
+ text.gsub!( MARKDOWN_REFLINK_RE ) do |m|
634
+ text, id = $~[1..2]
635
+
636
+ if id.empty?
637
+ url, title = check_refs( text )
638
+ else
639
+ url, title = check_refs( id )
640
+ end
641
+
642
+ atts = " href=\"#{ url }\""
643
+ atts << " title=\"#{ title }\"" if title
644
+ atts = shelve( atts )
645
+
646
+ "<a#{ atts }>#{ text }</a>"
647
+ end
648
+ end
649
+
650
+ MARKDOWN_LINK_RE = /
651
+ \[([^\[\]]+)\] # $text
652
+ \( # open paren
653
+ [ \t]* # opt space
654
+ <?(.+?)>? # $href
655
+ [ \t]* # opt space
656
+ (?: # whole title
657
+ (['"]) # $quote
658
+ (.*?) # $title
659
+ \3 # matching quote
660
+ )? # title is optional
661
+ \)
662
+ /x
663
+
664
+ def inline_markdown_link( text )
665
+ text.gsub!( MARKDOWN_LINK_RE ) do |m|
666
+ text, url, quote, title = $~[1..4]
667
+
668
+ atts = " href=\"#{ url }\""
669
+ atts << " title=\"#{ title }\"" if title
670
+ atts = shelve( atts )
671
+
672
+ "<a#{ atts }>#{ text }</a>"
673
+ end
674
+ end
675
+
676
+ TEXTILE_REFS_RE = /(^ *)\[([^\n]+?)\](#{HYPERLINK})(?=\s|$)/
677
+ MARKDOWN_REFS_RE = /(^ *)\[([^\n]+?)\]:\s+<?(#{HYPERLINK})>?(?:\s+"((?:[^"]|\\")+)")?(?=\s|$)/m
678
+
679
+ def refs( text )
680
+ @rules.each do |rule_name|
681
+ method( rule_name ).call( text ) if rule_name.to_s.match /^refs_/
682
+ end
683
+ end
684
+
685
+ def refs_textile( text )
686
+ text.gsub!( TEXTILE_REFS_RE ) do |m|
687
+ flag, url = $~[2..3]
688
+ @urlrefs[flag.downcase] = [url, nil]
689
+ nil
690
+ end
691
+ end
692
+
693
+ def refs_markdown( text )
694
+ text.gsub!( MARKDOWN_REFS_RE ) do |m|
695
+ flag, url = $~[2..3]
696
+ title = $~[6]
697
+ @urlrefs[flag.downcase] = [url, title]
698
+ nil
699
+ end
700
+ end
701
+
702
+ def check_refs( text )
703
+ ret = @urlrefs[text.downcase] if text
704
+ ret || [text, nil]
705
+ end
706
+
707
+ IMAGE_RE = /
708
+ (<p>|.|^) # start of line?
709
+ \! # opening
710
+ (\<|\=|\>)? # optional alignment atts
711
+ (#{C}) # optional style,class atts
712
+ (?:\. )? # optional dot-space
713
+ ([^\s(!]+?) # presume this is the src
714
+ \s? # optional space
715
+ (?:\(((?:[^\(\)]|\([^\)]+\))+?)\))? # optional title
716
+ \! # closing
717
+ (?::#{ HYPERLINK })? # optional href
718
+ /x
719
+
720
+ def inline_textile_image( text )
721
+ text.gsub!( IMAGE_RE ) do |m|
722
+ stln,algn,atts,url,title,href,href_a1,href_a2 = $~[1..8]
723
+ atts = pba( atts )
724
+ atts = " src=\"#{ url }\"#{ atts }"
725
+ atts << " title=\"#{ title }\"" if title
726
+ atts << " alt=\"#{ title }\""
727
+ # size = @getimagesize($url);
728
+ # if($size) $atts.= " $size[3]";
729
+
730
+ href, alt_title = check_refs( href ) if href
731
+ url, url_title = check_refs( url )
732
+
733
+ out = ''
734
+ out << "<a#{ shelve( " href=\"#{ href }\"" ) }>" if href
735
+ out << "<img#{ shelve( atts ) } />"
736
+ out << "</a>#{ href_a1 }#{ href_a2 }" if href
737
+
738
+ if algn
739
+ algn = h_align( algn )
740
+ if stln == "<p>"
741
+ out = "<p style=\"float:#{ algn }\">#{ out }"
742
+ else
743
+ out = "#{ stln }<div style=\"float:#{ algn }\">#{ out }</div>"
744
+ end
745
+ else
746
+ out = stln + out
747
+ end
748
+
749
+ out
750
+ end
751
+ end
752
+
753
+ def shelve( val )
754
+ @shelf << val
755
+ " :redsh##{ @shelf.length }:"
756
+ end
757
+
758
+ def retrieve( text )
759
+ @shelf.each_with_index do |r, i|
760
+ text.gsub!( " :redsh##{ i + 1 }:", r )
761
+ end
762
+ end
763
+
764
+ def incoming_entities( text )
765
+ ## turn any incoming ampersands into a dummy character for now.
766
+ ## This uses a negative lookahead for alphanumerics followed by a semicolon,
767
+ ## implying an incoming html entity, to be skipped
768
+
769
+ text.gsub!( /&(?![#a-z0-9]+;)/i, "x%x%" )
770
+ end
771
+
772
+ def no_textile( text )
773
+ text.gsub!( /(^|\s)==([^=]+.*?)==(\s|$)?/,
774
+ '\1<notextile>\2</notextile>\3' )
775
+ text.gsub!( /^ *==([^=]+.*?)==/m,
776
+ '\1<notextile>\2</notextile>\3' )
777
+ end
778
+
779
+ def clean_white_space( text )
780
+ # normalize line breaks
781
+ text.gsub!( /\r\n/, "\n" )
782
+ text.gsub!( /\r/, "\n" )
783
+ text.gsub!( /\t/, ' ' )
784
+ text.gsub!( /^ +$/, '' )
785
+ text.gsub!( /\n{3,}/, "\n\n" )
786
+ text.gsub!( /"$/, "\" " )
787
+
788
+ # if entire document is indented, flush
789
+ # to the left side
790
+ flush_left text
791
+ end
792
+
793
+ def flush_left( text )
794
+ indt = 0
795
+ if text =~ /^ /
796
+ while text !~ /^ {#{indt}}\S/
797
+ indt += 1
798
+ end unless text.empty?
799
+ if indt.nonzero?
800
+ text.gsub!( /^ {#{indt}}/, '' )
801
+ end
802
+ end
803
+ end
804
+
805
+ def footnote_ref( text )
806
+ text.gsub!( /\b\[([0-9]+?)\](\s)?/,
807
+ '<sup><a href="#fn\1">\1</a></sup>\2' )
808
+ end
809
+
810
+ OFFTAGS = /(code|pre|kbd|notextile)/
811
+ OFFTAG_MATCH = /(?:(<\/#{ OFFTAGS }>)|(<#{ OFFTAGS }[^>]*>))(.*?)(?=<\/?#{ OFFTAGS }|\Z)/mi
812
+ OFFTAG_OPEN = /<#{ OFFTAGS }/
813
+ OFFTAG_CLOSE = /<\/?#{ OFFTAGS }/
814
+ HASTAG_MATCH = /(<\/?\w[^\n]*?>)/m
815
+ ALLTAG_MATCH = /(<\/?\w[^\n]*?>)|.*?(?=<\/?\w[^\n]*?>|$)/m
816
+
817
+ def glyphs_textile( text, level = 0 )
818
+ if text !~ HASTAG_MATCH
819
+ pgl text
820
+ footnote_ref text
821
+ else
822
+ codepre = 0
823
+ text.gsub!( ALLTAG_MATCH ) do |line|
824
+ ## matches are off if we're between <code>, <pre> etc.
825
+ if $1
826
+ if line =~ OFFTAG_OPEN
827
+ codepre += 1
828
+ elsif line =~ OFFTAG_CLOSE
829
+ codepre -= 1
830
+ codepre = 0 if codepre < 0
831
+ end
832
+ elsif codepre.zero?
833
+ glyphs_textile( line, level + 1 )
834
+ else
835
+ htmlesc( line, :NoQuotes )
836
+ end
837
+ # p [level, codepre, line]
838
+
839
+ line
840
+ end
841
+ end
842
+ end
843
+
844
+ def rip_offtags( text )
845
+ if text =~ /<.*>/
846
+ ## strip and encode <pre> content
847
+ codepre, used_offtags = 0, {}
848
+ text.gsub!( OFFTAG_MATCH ) do |line|
849
+ if $3
850
+ offtag, aftertag = $4, $5
851
+ codepre += 1
852
+ used_offtags[offtag] = true
853
+ if codepre - used_offtags.length > 0
854
+ htmlesc( line, :NoQuotes ) unless used_offtags['notextile']
855
+ @pre_list.last << line
856
+ line = ""
857
+ else
858
+ htmlesc( aftertag, :NoQuotes ) if aftertag and not used_offtags['notextile']
859
+ line = "<redpre##{ @pre_list.length }>"
860
+ @pre_list << "#{ $3 }#{ aftertag }"
861
+ end
862
+ elsif $1 and codepre > 0
863
+ if codepre - used_offtags.length > 0
864
+ htmlesc( line, :NoQuotes ) unless used_offtags['notextile']
865
+ @pre_list.last << line
866
+ line = ""
867
+ end
868
+ codepre -= 1 unless codepre.zero?
869
+ used_offtags = {} if codepre.zero?
870
+ end
871
+ line
872
+ end
873
+ end
874
+ text
875
+ end
876
+
877
+ def smooth_offtags( text )
878
+ unless @pre_list.empty?
879
+ ## replace <pre> content
880
+ text.gsub!( /<redpre#(\d+)>/ ) { @pre_list[$1.to_i] }
881
+ end
882
+ end
883
+
884
+ def inline( text )
885
+ [/^inline_/, /^glyphs_/].each do |meth_re|
886
+ @rules.each do |rule_name|
887
+ method( rule_name ).call( text ) if rule_name.to_s.match( meth_re )
888
+ end
889
+ end
890
+ end
891
+
892
+ def h_align( text )
893
+ H_ALGN_VALS[text]
894
+ end
895
+
896
+ def v_align( text )
897
+ V_ALGN_VALS[text]
898
+ end
899
+
900
+ def textile_popup_help( name, windowW, windowH )
901
+ ' <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 />'
902
+ end
903
+
904
+ # HTML cleansing stuff
905
+ BASIC_TAGS = {
906
+ 'a' => ['href', 'title'],
907
+ 'img' => ['src', 'alt', 'title'],
908
+ 'br' => [],
909
+ 'i' => nil,
910
+ 'u' => nil,
911
+ 'b' => nil,
912
+ 'pre' => nil,
913
+ 'kbd' => nil,
914
+ 'code' => ['lang'],
915
+ 'cite' => nil,
916
+ 'strong' => nil,
917
+ 'em' => nil,
918
+ 'ins' => nil,
919
+ 'sup' => nil,
920
+ 'sub' => nil,
921
+ 'del' => nil,
922
+ 'table' => nil,
923
+ 'tr' => nil,
924
+ 'td' => ['colspan', 'rowspan'],
925
+ 'th' => nil,
926
+ 'ol' => nil,
927
+ 'ul' => nil,
928
+ 'li' => nil,
929
+ 'p' => nil,
930
+ 'h1' => nil,
931
+ 'h2' => nil,
932
+ 'h3' => nil,
933
+ 'h4' => nil,
934
+ 'h5' => nil,
935
+ 'h6' => nil,
936
+ 'blockquote' => ['cite']
937
+ }
938
+
939
+ def clean_html( text, tags = BASIC_TAGS )
940
+ text.gsub!( /<!\[CDATA\[/, '' )
941
+ text.gsub!( /<(\/*)(\w+)([^>]*)>/ ) do
942
+ raw = $~
943
+ tag = raw[2].downcase
944
+ if tags.has_key? tag
945
+ pcs = [tag]
946
+ tags[tag].each do |prop|
947
+ ['"', "'", ''].each do |q|
948
+ q2 = ( q != '' ? q : '\s' )
949
+ if raw[3] =~ /#{prop}\s*=\s*#{q}([^#{q2}]+)#{q}/i
950
+ attrv = $1
951
+ next if prop == 'src' and attrv =~ %r{^(?!http)\w+:}
952
+ pcs << "#{prop}=\"#{$1.gsub('"', '\\"')}\""
953
+ break
954
+ end
955
+ end
956
+ end if tags[tag]
957
+ "<#{raw[1]}#{pcs.join " "}>"
958
+ else
959
+ " "
960
+ end
961
+ end
962
+ end
963
+ end