Pimki 1.0.092

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. data/README +158 -0
  2. data/README-PIMKI +87 -0
  3. data/app/controllers/wiki.rb +563 -0
  4. data/app/models/author.rb +4 -0
  5. data/app/models/chunks/category.rb +31 -0
  6. data/app/models/chunks/category_test.rb +21 -0
  7. data/app/models/chunks/chunk.rb +20 -0
  8. data/app/models/chunks/engines.rb +34 -0
  9. data/app/models/chunks/include.rb +29 -0
  10. data/app/models/chunks/literal.rb +19 -0
  11. data/app/models/chunks/match.rb +19 -0
  12. data/app/models/chunks/nowiki.rb +31 -0
  13. data/app/models/chunks/nowiki_test.rb +14 -0
  14. data/app/models/chunks/test.rb +18 -0
  15. data/app/models/chunks/todo.rb +22 -0
  16. data/app/models/chunks/uri.rb +97 -0
  17. data/app/models/chunks/uri_test.rb +92 -0
  18. data/app/models/chunks/wiki.rb +82 -0
  19. data/app/models/chunks/wiki_test.rb +36 -0
  20. data/app/models/page.rb +91 -0
  21. data/app/models/page_lock.rb +24 -0
  22. data/app/models/page_set.rb +73 -0
  23. data/app/models/page_test.rb +76 -0
  24. data/app/models/revision.rb +91 -0
  25. data/app/models/revision_test.rb +252 -0
  26. data/app/models/web.rb +277 -0
  27. data/app/models/web_test.rb +53 -0
  28. data/app/models/wiki_content.rb +113 -0
  29. data/app/models/wiki_service.rb +137 -0
  30. data/app/models/wiki_service_test.rb +15 -0
  31. data/app/models/wiki_words.rb +26 -0
  32. data/app/models/wiki_words_test.rb +12 -0
  33. data/app/views/bottom.rhtml +4 -0
  34. data/app/views/markdown_help.rhtml +16 -0
  35. data/app/views/menu.rhtml +20 -0
  36. data/app/views/navigation.rhtml +26 -0
  37. data/app/views/rdoc_help.rhtml +16 -0
  38. data/app/views/static_style_sheet.rhtml +231 -0
  39. data/app/views/style.rhtml +179 -0
  40. data/app/views/textile_help.rhtml +28 -0
  41. data/app/views/top.rhtml +52 -0
  42. data/app/views/wiki/authors.rhtml +15 -0
  43. data/app/views/wiki/bliki.rhtml +101 -0
  44. data/app/views/wiki/bliki_edit.rhtml +33 -0
  45. data/app/views/wiki/bliki_new.rhtml +61 -0
  46. data/app/views/wiki/bliki_revision.rhtml +51 -0
  47. data/app/views/wiki/edit.rhtml +34 -0
  48. data/app/views/wiki/edit_menu.rhtml +27 -0
  49. data/app/views/wiki/edit_web.rhtml +139 -0
  50. data/app/views/wiki/export.rhtml +14 -0
  51. data/app/views/wiki/feeds.rhtml +10 -0
  52. data/app/views/wiki/list.rhtml +164 -0
  53. data/app/views/wiki/locked.rhtml +14 -0
  54. data/app/views/wiki/login.rhtml +11 -0
  55. data/app/views/wiki/mind.rhtml +39 -0
  56. data/app/views/wiki/new.rhtml +27 -0
  57. data/app/views/wiki/new_system.rhtml +78 -0
  58. data/app/views/wiki/new_web.rhtml +64 -0
  59. data/app/views/wiki/page.rhtml +84 -0
  60. data/app/views/wiki/print.rhtml +16 -0
  61. data/app/views/wiki/published.rhtml +10 -0
  62. data/app/views/wiki/recently_revised.rhtml +31 -0
  63. data/app/views/wiki/revision.rhtml +87 -0
  64. data/app/views/wiki/rss_feed.rhtml +22 -0
  65. data/app/views/wiki/search.rhtml +26 -0
  66. data/app/views/wiki/tex.rhtml +23 -0
  67. data/app/views/wiki/tex_web.rhtml +35 -0
  68. data/app/views/wiki/todo.rhtml +39 -0
  69. data/app/views/wiki/web_list.rhtml +13 -0
  70. data/app/views/wiki_words_help.rhtml +8 -0
  71. data/libraries/action_controller_servlet.rb +177 -0
  72. data/libraries/bluecloth.rb +1127 -0
  73. data/libraries/diff/diff.rb +475 -0
  74. data/libraries/diff/diff_test.rb +80 -0
  75. data/libraries/erb.rb +490 -0
  76. data/libraries/madeleine/automatic.rb +357 -0
  77. data/libraries/madeleine/clock.rb +94 -0
  78. data/libraries/madeleine_service.rb +69 -0
  79. data/libraries/rdocsupport.rb +156 -0
  80. data/libraries/redcloth_for_tex.rb +869 -0
  81. data/libraries/redcloth_for_tex_test.rb +41 -0
  82. data/libraries/view_helper.rb +33 -0
  83. data/libraries/web_controller_server.rb +95 -0
  84. data/pimki.rb +97 -0
  85. metadata +169 -0
@@ -0,0 +1,69 @@
1
+ require 'madeleine/automatic'
2
+ require 'madeleine'
3
+ require 'singleton'
4
+ require 'YAML'
5
+
6
+ class MadeleineService
7
+ include Madeleine::Automatic::Interceptor
8
+
9
+ @@storage_path = self.name.downcase + "_storage"
10
+
11
+ class << self
12
+ def storage_path
13
+ @@storage_path
14
+ end
15
+
16
+ def storage_path=(storage_path)
17
+ @@storage_path = storage_path
18
+ end
19
+
20
+ def clean_storage
21
+ MadeleineServer.clean_storage(self)
22
+ end
23
+
24
+ def instance
25
+ @system = MadeleineServer.new(self).system if @system.nil?
26
+ @system
27
+ end
28
+
29
+ def restart
30
+ clean_storage
31
+ @system = MadeleineServer.new(self).system
32
+ end
33
+ end
34
+ end
35
+
36
+ class MadeleineServer
37
+ SNAPSHOT_INTERVAL = 60 * 60 # Each hour * 24 # Each day
38
+ AUTOMATIC_SNAPSHOTS = true
39
+
40
+ # Clears all the command_log and snapshot files located in the storage directory, so the
41
+ # database is essentially dropped and recreated as blank
42
+ def self.clean_storage(service)
43
+ begin
44
+ Dir.foreach(service.storage_path) do |file|
45
+ File.delete(service.storage_path + File::SEPARATOR + file) if file =~ /(command_log|snapshot)$/
46
+ end
47
+ rescue
48
+ Dir.mkdir(service.storage_path)
49
+ end
50
+ end
51
+
52
+ def initialize(service)
53
+ @server = Madeleine::Automatic::AutomaticSnapshotMadeleine.new(service.storage_path) { service.new }
54
+ start_snapshot_thread if AUTOMATIC_SNAPSHOTS
55
+ end
56
+
57
+ def system
58
+ @server.system
59
+ end
60
+
61
+ def start_snapshot_thread
62
+ Thread.new(@server) {
63
+ while true
64
+ sleep(SNAPSHOT_INTERVAL)
65
+ @server.take_snapshot
66
+ end
67
+ }
68
+ end
69
+ end
@@ -0,0 +1,156 @@
1
+ begin
2
+ require "rdoc/markup/simple_markup"
3
+ require 'rdoc/markup/simple_markup/to_html'
4
+ rescue LoadError
5
+ # use old version if available
6
+ require 'markup/simple_markup'
7
+ require 'markup/simple_markup/to_html'
8
+ end
9
+
10
+ module RDocSupport
11
+
12
+ # A simple +rdoc+ markup class which recognizes some additional
13
+ # formatting commands suitable for Wiki use.
14
+ class RDocMarkup < SM::SimpleMarkup
15
+ def initialize
16
+ super()
17
+
18
+ pre = '(?:\\s|^|\\\\)'
19
+
20
+ # links of the form
21
+ # [[<url> description with spaces]]
22
+ add_special(/((\\)?\[\[\S+?\s+.+?\]\])/,:TIDYLINK)
23
+
24
+ # and external references
25
+ add_special(/((\\)?(link:|anchor:|http:|mailto:|ftp:|img:|www\.)\S+\w\/?)/,
26
+ :HYPERLINK)
27
+
28
+ # <br/>
29
+ add_special(%r{(#{pre}<br/>)}, :BR)
30
+
31
+ # and <center> ... </center>
32
+ add_html("center", :CENTER)
33
+ end
34
+
35
+ def convert(text, handler)
36
+ #$stderr.puts text.inspect
37
+ res = super
38
+ res.sub!(/^<p>\n/, '')
39
+ res.sub!(/<\/p>$/, '')
40
+ res
41
+ end
42
+ end
43
+
44
+ # Handle special hyperlinking requirments for RDoc formatted
45
+ # entries. Requires RDoc
46
+
47
+ class HyperLinkHtml < SM::ToHtml
48
+
49
+ # Initialize the HyperLinkHtml object.
50
+ # [path] location of the node
51
+ # [site] object representing the whole site (typically of class
52
+ # +Site+)
53
+ def initialize
54
+ super()
55
+ add_tag(:CENTER, "<center>", "</center>")
56
+ end
57
+
58
+ # handle <br/>
59
+ def handle_special_BR(special)
60
+ return "&lt;br/&gt" if special.text[0,1] == '\\'
61
+ special.text
62
+ end
63
+
64
+ # We're invoked with a potential external hyperlink.
65
+ # [mailto:] just gets inserted.
66
+ # [http:] links are checked to see if they
67
+ # reference an image. If so, that image gets inserted
68
+ # using an <img> tag. Otherwise a conventional <a href>
69
+ # is used.
70
+ # [img:] insert a <tt><img></tt> tag
71
+ # [link:] used to insert arbitrary <tt><a></tt> references
72
+ # [anchor:] used to create an anchor
73
+ def handle_special_HYPERLINK(special)
74
+ text = special.text.strip
75
+ return text[1..-1] if text[0,1] == '\\'
76
+ url = special.text.strip
77
+ if url =~ /([A-Za-z]+):(.*)/
78
+ type = $1
79
+ path = $2
80
+ else
81
+ type = "http"
82
+ path = url
83
+ url = "http://#{url}"
84
+ end
85
+
86
+ case type
87
+ when "http"
88
+ if url =~ /\.(gif|png|jpg|jpeg|bmp)$/
89
+ "<img src=\"#{url}\"/>"
90
+ else
91
+ "<a href=\"#{url}\">#{url.sub(%r{^\w+:/*}, '')}</a>"
92
+ end
93
+ when "img"
94
+ "<img src=\"#{path}\"/>"
95
+ when "link"
96
+ "<a href=\"#{path}\">#{path}</a>"
97
+ when "anchor"
98
+ "<a name=\"#{path}\"></a>"
99
+ else
100
+ "<a href=\"#{url}\">#{url.sub(%r{^\w+:/*}, '')}</a>"
101
+ end
102
+ end
103
+
104
+ # Here's a hyperlink where the label is different to the URL
105
+ # [[url label that may contain spaces]]
106
+ #
107
+
108
+ def handle_special_TIDYLINK(special)
109
+ text = special.text.strip
110
+ return text[1..-1] if text[0,1] == '\\'
111
+ unless text =~ /\[\[(\S+?)\s+(.+?)\]\]/
112
+ return text
113
+ end
114
+ url = $1
115
+ label = $2
116
+ label = RDocFormatter.new(label).to_html
117
+ label = label.split.select{|x| x =~ /\S/}.
118
+ map{|x| x.chomp}.join(' ')
119
+
120
+ case url
121
+ when /link:(\S+)/
122
+ return %{<a href="#{$1}">#{label}</a>}
123
+ when /img:(\S+)/
124
+ return %{<img src="http://#{$1}" alt="#{label}" />}
125
+ when /rubytalk:(\S+)/
126
+ return %{<a href="http://ruby-talk.org/blade/#{$1}">#{label}</a>}
127
+ when /rubygarden:(\S+)/
128
+ return %{<a href="http://www.rubygarden.org/ruby?#{$1}">#{label}</a>}
129
+ when /c2:(\S+)/
130
+ return %{<a href="http://c2.com/cgi/wiki?#{$1}">#{label}</a>}
131
+ when /isbn:(\S+)/
132
+ return %{<a href="http://search.barnesandnoble.com/bookSearch/} +
133
+ %{isbnInquiry.asp?isbn=#{$1}">#{label}</a>}
134
+ end
135
+
136
+ unless url =~ /\w+?:/
137
+ url = "http://#{url}"
138
+ end
139
+
140
+ "<a href=\"#{url}\">#{label}</a>"
141
+ end
142
+ end
143
+
144
+ class RDocFormatter
145
+ def initialize(text)
146
+ @text = text
147
+ end
148
+
149
+ def to_html
150
+ markup = RDocMarkup.new
151
+ h = HyperLinkHtml.new
152
+ markup.convert(@text, h)
153
+ end
154
+ end
155
+
156
+ end
@@ -0,0 +1,869 @@
1
+ # vim:ts=4:sw=4:
2
+ # = RedCloth - Textile for Ruby
3
+ #
4
+ # (c) 2003 why the lucky stiff (and his puppet organizations.)
5
+ #
6
+ # (see http://www.textism.com/tools/textile/ for Textile)
7
+ #
8
+ # Based on (and also inspired by) both:
9
+ #
10
+ # PyTextile: http://diveintomark.org/projects/textile/textile.py.txt
11
+ # Textism for PHP: http://www.textism.com/tools/textile/
12
+ #
13
+ #
14
+ # == What is Textile?
15
+ #
16
+ # Textile is a simple formatting style for text
17
+ # documents, loosely based on some HTML conventions.
18
+ #
19
+ # === Sample Textile Text
20
+ #
21
+ # h2. This is a title
22
+ #
23
+ # h3. This is a subhead
24
+ #
25
+ # This is a bit of paragraph.
26
+ #
27
+ # bq. This is a blockquote.
28
+ #
29
+ # === Writing Textile
30
+ #
31
+ # A Textile document consists of paragraphs. Paragraphs
32
+ # can be specially formatted by adding a small instruction
33
+ # to the beginning of the paragraph.
34
+ #
35
+ # h[n]. Header of size [n].
36
+ # bq. Blockquote.
37
+ # # Numeric list.
38
+ # * Bulleted list.
39
+ #
40
+ # === Quick Phrase Modifiers
41
+ #
42
+ # Quick phrase modifiers are also included, to allow formatting
43
+ # of small portions of text within a paragraph.
44
+ #
45
+ # _emphasis_
46
+ # __italicized__
47
+ # *strong*
48
+ # **bold**
49
+ # ??citation??
50
+ # -deleted text-
51
+ # +inserted text+
52
+ # ^superscript^
53
+ # ~subscript~
54
+ # @code@
55
+ # %(classname)span%
56
+ #
57
+ # ==notextile== (leave text alone)
58
+ #
59
+ # === Links
60
+ #
61
+ # To make a hypertext link, put the link text in "quotation
62
+ # marks" followed immediately by a colon and the URL of the link.
63
+ #
64
+ # Optional: text in (parentheses) following the link text,
65
+ # but before the closing quotation mark, will become a Title
66
+ # attribute for the link, visible as a tool tip when a cursor is above it.
67
+ #
68
+ # Example:
69
+ #
70
+ # "This is a link (This is a title) ":http://www.textism.com
71
+ #
72
+ # Will become:
73
+ #
74
+ # <a href="http://www.textism.com" title="This is a title">This is a link</a>
75
+ #
76
+ # === Images
77
+ #
78
+ # To insert an image, put the URL for the image inside exclamation marks.
79
+ #
80
+ # Optional: text that immediately follows the URL in (parentheses) will
81
+ # be used as the Alt text for the image. Images on the web should always
82
+ # have descriptive Alt text for the benefit of readers using non-graphical
83
+ # browsers.
84
+ #
85
+ # Optional: place a colon followed by a URL immediately after the
86
+ # closing ! to make the image into a link.
87
+ #
88
+ # Example:
89
+ #
90
+ # !http://www.textism.com/common/textist.gif(Textist)!
91
+ #
92
+ # Will become:
93
+ #
94
+ # <img src="http://www.textism.com/common/textist.gif" alt="Textist" />
95
+ #
96
+ # With a link:
97
+ #
98
+ # !/common/textist.gif(Textist)!:http://textism.com
99
+ #
100
+ # Will become:
101
+ #
102
+ # <a href="http://textism.com"><img src="/common/textist.gif" alt="Textist" /></a>
103
+ #
104
+ # === Defining Acronyms
105
+ #
106
+ # HTML allows authors to define acronyms via the tag. The definition appears as a
107
+ # tool tip when a cursor hovers over the acronym. A crucial aid to clear writing,
108
+ # this should be used at least once for each acronym in documents where they appear.
109
+ #
110
+ # To quickly define an acronym in Textile, place the full text in (parentheses)
111
+ # immediately following the acronym.
112
+ #
113
+ # Example:
114
+ #
115
+ # ACLU(American Civil Liberties Union)
116
+ #
117
+ # Will become:
118
+ #
119
+ # <acronym title="American Civil Liberties Union">ACLU</acronym>
120
+ #
121
+ # === Adding Tables
122
+ #
123
+ # In Textile, simple tables can be added by seperating each column by
124
+ # a pipe.
125
+ #
126
+ # |a|simple|table|row|
127
+ # |And|Another|table|row|
128
+ #
129
+ # Attributes are defined by style definitions in parentheses.
130
+ #
131
+ # table(border:1px solid black).
132
+ # (background:#ddd;color:red). |{}| | | |
133
+ #
134
+ # === Using RedCloth
135
+ #
136
+ # RedCloth is simply an extension of the String class, which can handle
137
+ # Textile formatting. Use it like a String and output HTML with its
138
+ # RedCloth#to_html method.
139
+ #
140
+ # doc = RedCloth.new "
141
+ #
142
+ # h2. Test document
143
+ #
144
+ # Just a simple test."
145
+ #
146
+ # puts doc.to_html
147
+
148
+ class String
149
+ #
150
+ # Flexible HTML escaping
151
+ #
152
+ def texesc!( mode )
153
+ gsub!( '&', '\\\\&' )
154
+ gsub!( '%', '\%' )
155
+ gsub!( '$', '\$' )
156
+ end
157
+ end
158
+
159
+
160
+ def table_of_contents(text, pages)
161
+ text.gsub!( /^([#*]+? .*?)$(?![^#*])/m ) do |match|
162
+ lines = match.split( /\n/ )
163
+ last_line = -1
164
+ depth = []
165
+ lines.each_with_index do |line, line_id|
166
+ if line =~ /^([#*]+) (.*)$/m
167
+ tl,content = $~[1..2]
168
+ content.gsub! /[\[\]]/, ""
169
+ content.strip!
170
+
171
+ if depth.last
172
+ if depth.last.length > tl.length
173
+ (depth.length - 1).downto(0) do |i|
174
+ break if depth[i].length == tl.length
175
+ lines[line_id - 1] << "" # "\n\t\\end{#{ lT( depth[i] ) }}\n\t"
176
+ depth.pop
177
+ end
178
+ end
179
+ if !depth.last.nil? && !tl.length.nil? && depth.last.length == tl.length
180
+ lines[line_id - 1] << ''
181
+ end
182
+ end
183
+
184
+ depth << tl unless depth.last == tl
185
+
186
+ subsection_depth = [depth.length - 1, 2].min
187
+
188
+ lines[line_id] = "\n\\#{ "sub" * subsection_depth }section{#{ content }}"
189
+ lines[line_id] += "\n#{pages[content]}" if pages.keys.include?(content)
190
+
191
+ lines[line_id] = "\\pagebreak\n#{lines[line_id]}" if subsection_depth == 0
192
+
193
+ last_line = line_id
194
+
195
+ elsif line =~ /^\s+\S/
196
+ last_line = line_id
197
+ elsif line_id - last_line < 2 and line =~ /^\S/
198
+ last_line = line_id
199
+ end
200
+ if line_id - last_line > 1 or line_id == lines.length - 1
201
+ depth.delete_if do |v|
202
+ lines[last_line] << "" # "\n\t\\end{#{ lT( v ) }}"
203
+ end
204
+ end
205
+ end
206
+ lines.join( "\n" )
207
+ end
208
+ end
209
+
210
+ class RedClothForTex < String
211
+
212
+ VERSION = '2.0.7'
213
+
214
+ #
215
+ # Mapping of 8-bit ASCII codes to HTML numerical entity equivalents.
216
+ # (from PyTextile)
217
+ #
218
+ TEXTILE_TAGS =
219
+
220
+ [[128, 8364], [129, 0], [130, 8218], [131, 402], [132, 8222], [133, 8230],
221
+ [134, 8224], [135, 8225], [136, 710], [137, 8240], [138, 352], [139, 8249],
222
+ [140, 338], [141, 0], [142, 0], [143, 0], [144, 0], [145, 8216], [146, 8217],
223
+ [147, 8220], [148, 8221], [149, 8226], [150, 8211], [151, 8212], [152, 732],
224
+ [153, 8482], [154, 353], [155, 8250], [156, 339], [157, 0], [158, 0], [159, 376]].
225
+
226
+ collect! do |a, b|
227
+ [a.chr, ( b.zero? and "" or "&#{ b };" )]
228
+ end
229
+
230
+ #
231
+ # Regular expressions to convert to HTML.
232
+ #
233
+ A_HLGN = /(?:(?:<>|<|>|\=|[()]+)+)/
234
+ A_VLGN = /[\-^~]/
235
+ C_CLAS = '(?:\([^)]+\))'
236
+ C_LNGE = '(?:\[[^\]]+\])'
237
+ C_STYL = '(?:\{[^}]+\})'
238
+ S_CSPN = '(?:\\\\\d+)'
239
+ S_RSPN = '(?:/\d+)'
240
+ A = "(?:#{A_HLGN}?#{A_VLGN}?|#{A_VLGN}?#{A_HLGN}?)"
241
+ S = "(?:#{S_CSPN}?#{S_RSPN}|#{S_RSPN}?#{S_CSPN}?)"
242
+ C = "(?:#{C_CLAS}?#{C_STYL}?#{C_LNGE}?|#{C_STYL}?#{C_LNGE}?#{C_CLAS}?|#{C_LNGE}?#{C_STYL}?#{C_CLAS}?)"
243
+ # PUNCT = Regexp::quote( '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~' )
244
+ PUNCT = Regexp::quote( '!"#$%&\'*+,-./:;=?@\\^_`|~' )
245
+ HYPERLINK = '(\S+?)([^\w\s/;=\?]*?)(\s|$)'
246
+
247
+ GLYPHS = [
248
+ # [ /([^\s\[{(>])?\'([dmst]\b|ll\b|ve\b|\s|:|$)/, '\1&#8217;\2' ], # single closing
249
+ [ /([^\s\[{(>])\'/, '\1&#8217;' ], # single closing
250
+ [ /\'(?=\s|s\b|[#{PUNCT}])/, '&#8217;' ], # single closing
251
+ [ /\'/, '&#8216;' ], # single opening
252
+ # [ /([^\s\[{(])?"(\s|:|$)/, '\1&#8221;\2' ], # double closing
253
+ [ /([^\s\[{(>])"/, '\1&#8221;' ], # double closing
254
+ [ /"(?=\s|[#{PUNCT}])/, '&#8221;' ], # double closing
255
+ [ /"/, '&#8220;' ], # double opening
256
+ [ /\b( )?\.{3}/, '\1&#8230;' ], # ellipsis
257
+ [ /\b([A-Z][A-Z0-9]{2,})\b(?:[(]([^)]*)[)])/, '<acronym title="\2">\1</acronym>' ], # 3+ uppercase acronym
258
+ [ /(^|[^"][>\s])([A-Z][A-Z0-9 ]{2,})([^<a-z0-9]|$)/, '\1<span class="caps">\2</span>\3' ], # 3+ uppercase caps
259
+ [ /(\.\s)?\s?--\s?/, '\1&#8212;' ], # em dash
260
+ [ /\s->\s/, ' &rarr; ' ], # en dash
261
+ [ /\s-\s/, ' &#8211; ' ], # en dash
262
+ [ /(\d+) ?x ?(\d+)/, '\1&#215;\2' ], # dimension sign
263
+ [ /\b ?[(\[]TM[\])]/i, '&#8482;' ], # trademark
264
+ [ /\b ?[(\[]R[\])]/i, '&#174;' ], # registered
265
+ [ /\b ?[(\[]C[\])]/i, '&#169;' ] # copyright
266
+ ]
267
+
268
+ I_ALGN_VALS = {
269
+ '<' => 'left',
270
+ '=' => 'center',
271
+ '>' => 'right'
272
+ }
273
+
274
+ H_ALGN_VALS = {
275
+ '<' => 'left',
276
+ '=' => 'center',
277
+ '>' => 'right',
278
+ '<>' => 'justify'
279
+ }
280
+
281
+ V_ALGN_VALS = {
282
+ '^' => 'top',
283
+ '-' => 'middle',
284
+ '~' => 'bottom'
285
+ }
286
+
287
+ QTAGS = [
288
+ ['**', 'bf'],
289
+ ['*', 'bf'],
290
+ ['??', 'cite'],
291
+ ['-', 'del'],
292
+ ['__', 'underline'],
293
+ ['_', 'em'],
294
+ ['%', 'span'],
295
+ ['+', 'ins'],
296
+ ['^', 'sup'],
297
+ ['~', 'sub']
298
+ ]
299
+
300
+ #
301
+ # Two accessor for setting security restrictions.
302
+ #
303
+ # This is a nice thing if you're using RedCloth for
304
+ # formatting in public places (e.g. Wikis) where you
305
+ # don't want users to abuse HTML for bad things.
306
+ #
307
+ # If +:filter_html+ is set, HTML which wasn't
308
+ # created by the Textile processor will be escaped.
309
+ #
310
+ # If +:filter_styles+ is set, it will also disable
311
+ # the style markup specifier. ('{color: red}')
312
+ #
313
+ attr_accessor :filter_html, :filter_styles
314
+
315
+ #
316
+ # Accessor for toggling line folding.
317
+ #
318
+ # If +:fold_lines+ is set, single newlines will
319
+ # not be converted to break tags.
320
+ #
321
+ attr_accessor :fold_lines
322
+
323
+ def initialize( string, restrictions = [] )
324
+ restrictions.each { |r| method( "#{ r }=" ).call( true ) }
325
+ super( string )
326
+ end
327
+
328
+ #
329
+ # Generate tex.
330
+ #
331
+ def to_tex( lite = false )
332
+
333
+ # make our working copy
334
+ text = self.dup
335
+
336
+ @urlrefs = {}
337
+ @shelf = []
338
+
339
+ # incoming_entities text
340
+ fix_entities text
341
+ clean_white_space text
342
+
343
+ get_refs text
344
+
345
+ no_textile text
346
+
347
+ unless lite
348
+ lists text
349
+ table text
350
+ end
351
+
352
+ glyphs text
353
+
354
+ unless lite
355
+ fold text
356
+ block text
357
+ end
358
+
359
+ retrieve text
360
+ encode_entities text
361
+
362
+ text.gsub!(/\[\[(.*?)\]\]/, "\\1")
363
+ text.gsub!(/_/, "\\_")
364
+ text.gsub!( /<\/?notextile>/, '' )
365
+ # text.gsub!( /x%x%/, '&#38;' )
366
+ # text.gsub!( /<br \/>/, "<br />\n" )
367
+ text.strip!
368
+ text
369
+
370
+ end
371
+
372
+ def pgl( text )
373
+ GLYPHS.each do |re, resub|
374
+ text.gsub! re, resub
375
+ end
376
+ end
377
+
378
+ def pba( text_in, element = "" )
379
+
380
+ return '' unless text_in
381
+
382
+ style = []
383
+ text = text_in.dup
384
+ if element == 'td'
385
+ colspan = $1 if text =~ /\\(\d+)/
386
+ rowspan = $1 if text =~ /\/(\d+)/
387
+ style << "vertical-align:#{ v_align( $& ) };" if text =~ A_VLGN
388
+ end
389
+
390
+ style << "#{ $1 };" if not @filter_styles and
391
+ text.sub!( /\{([^}]*)\}/, '' )
392
+
393
+ lang = $1 if
394
+ text.sub!( /\[([^)]+?)\]/, '' )
395
+
396
+ cls = $1 if
397
+ text.sub!( /\(([^()]+?)\)/, '' )
398
+
399
+ style << "padding-left:#{ $1.length }em;" if
400
+ text.sub!( /([(]+)/, '' )
401
+
402
+ style << "padding-right:#{ $1.length }em;" if text.sub!( /([)]+)/, '' )
403
+
404
+ style << "text-align:#{ h_align( $& ) };" if text =~ A_HLGN
405
+
406
+ cls, id = $1, $2 if cls =~ /^(.*?)#(.*)$/
407
+
408
+ atts = ''
409
+ atts << " style=\"#{ style.join }\"" unless style.empty?
410
+ atts << " class=\"#{ cls }\"" unless cls.to_s.empty?
411
+ atts << " lang=\"#{ lang }\"" if lang
412
+ atts << " id=\"#{ id }\"" if id
413
+ atts << " colspan=\"#{ colspan }\"" if colspan
414
+ atts << " rowspan=\"#{ rowspan }\"" if rowspan
415
+
416
+ atts
417
+ end
418
+
419
+ def table( text )
420
+ text << "\n\n"
421
+ text.gsub!( /^(?:table(_?#{S}#{A}#{C})\. ?\n)?^(#{A}#{C}\.? ?\|.*?\|)\n\n/m ) do |matches|
422
+
423
+ tatts, fullrow = $~[1..2]
424
+ tatts = pba( tatts, 'table' )
425
+ rows = []
426
+
427
+ fullrow.
428
+ split( /\|$/m ).
429
+ delete_if { |x| x.empty? }.
430
+ each do |row|
431
+
432
+ ratts, row = pba( $1, 'tr' ), $2 if row =~ /^(#{A}#{C}\. )(.*)/m
433
+
434
+ cells = []
435
+ row.split( '|' ).each do |cell|
436
+ ctyp = 'd'
437
+ ctyp = 'h' if cell =~ /^_/
438
+
439
+ catts = ''
440
+ catts, cell = pba( $1, 'td' ), $2 if cell =~ /^(_?#{S}#{A}#{C}\. )(.*)/
441
+
442
+ unless cell.strip.empty?
443
+ cells << "\t\t\t<t#{ ctyp }#{ catts }>#{ cell }</t#{ ctyp }>"
444
+ end
445
+ end
446
+ rows << "\t\t<tr#{ ratts }>\n#{ cells.join( "\n" ) }\n\t\t</tr>"
447
+ end
448
+ "\t<table#{ tatts }>\n#{ rows.join( "\n" ) }\n\t</table>\n\n"
449
+ end
450
+ end
451
+
452
+ def lists( text )
453
+ text.gsub!( /^([#*]+?#{C} .*?)$(?![^#*])/m ) do |match|
454
+ lines = match.split( /\n/ )
455
+ last_line = -1
456
+ depth = []
457
+ lines.each_with_index do |line, line_id|
458
+ if line =~ /^([#*]+)(#{A}#{C}) (.*)$/m
459
+ tl,atts,content = $~[1..3]
460
+ if depth.last
461
+ if depth.last.length > tl.length
462
+ (depth.length - 1).downto(0) do |i|
463
+ break if depth[i].length == tl.length
464
+ lines[line_id - 1] << "\n\t\\end{#{ lT( depth[i] ) }}\n\t"
465
+ depth.pop
466
+ end
467
+ end
468
+ if !depth.last.nil? && !tl.length.nil? && depth.last.length == tl.length
469
+ lines[line_id - 1] << ''
470
+ end
471
+ end
472
+ unless depth.last == tl
473
+ depth << tl
474
+ atts = pba( atts )
475
+ lines[line_id] = "\t\\begin{#{ lT(tl) }}\n\t\\item #{ content }"
476
+ else
477
+ lines[line_id] = "\t\t\\item #{ content }"
478
+ end
479
+ last_line = line_id
480
+
481
+ elsif line =~ /^\s+\S/
482
+ last_line = line_id
483
+ elsif line_id - last_line < 2 and line =~ /^\S/
484
+ last_line = line_id
485
+ end
486
+ if line_id - last_line > 1 or line_id == lines.length - 1
487
+ depth.delete_if do |v|
488
+ lines[last_line] << "\n\t\\end{#{ lT( v ) }}"
489
+ end
490
+ end
491
+ end
492
+ lines.join( "\n" )
493
+ end
494
+ end
495
+
496
+ def lT( text )
497
+ text =~ /\#$/ ? 'enumerate' : 'itemize'
498
+ end
499
+
500
+ def fold( text )
501
+ text.gsub!( /(.+)\n(?![#*\s|])/, "\\1\\\\\\\\" )
502
+ # text.gsub!( /(.+)\n(?![#*\s|])/, "\\1#{ @fold_lines ? ' ' : '<br />' }" )
503
+ end
504
+
505
+ def block( text )
506
+ pre = false
507
+ find = ['bq','h[1-6]','fn\d+']
508
+
509
+ regexp_cue = []
510
+
511
+ lines = text.split( /\n/ ) + [' ']
512
+ new_text =
513
+ lines.collect do |line|
514
+ pre = true if line =~ /<(pre|notextile)>/i
515
+ find.each do |tag|
516
+ line.gsub!( /^(#{ tag })(#{A}#{C})\.(?::(\S+))? (.*)$/ ) do |m|
517
+ tag,atts,cite,content = $~[1..4]
518
+
519
+ atts = pba( atts )
520
+
521
+ if tag =~ /fn(\d+)/
522
+ # tag = 'p';
523
+ # atts << " id=\"fn#{ $1 }\""
524
+ regexp_cue << [ /footnote\{#{$1}}/, "footnote{#{content}}" ]
525
+ content = ""
526
+ end
527
+
528
+ if tag =~ /h([1-6])/
529
+ section_type = "sub" * [$1.to_i - 1, 2].min
530
+ start = "\t\\#{section_type}section*{"
531
+ tend = "}"
532
+ end
533
+
534
+ if tag == "bq"
535
+ cite = check_refs( cite )
536
+ cite = " cite=\"#{ cite }\"" if cite
537
+ start = "\t\\begin{quotation}\n\\noindent {\\em ";
538
+ tend = "}\n\t\\end{quotation}";
539
+ end
540
+
541
+ "#{ start }#{ content }#{ tend }"
542
+ end unless pre
543
+ end
544
+
545
+ #line.gsub!( /^(?!\t|<\/?pre|<\/?notextile|<\/?code|$| )(.*)/, "\t<p>\\1</p>" )
546
+
547
+ #line.gsub!( "<br />", "\n" ) if pre
548
+ # pre = false if line =~ /<\/(pre|notextile)>/i
549
+
550
+ line
551
+ end.join( "\n" )
552
+ text.replace( new_text )
553
+ regexp_cue.each { |pair| text.gsub!(pair.first, pair.last) }
554
+ end
555
+
556
+ def span( text )
557
+ QTAGS.each do |tt, ht|
558
+ ttr = Regexp::quote( tt )
559
+ text.gsub!(
560
+
561
+ /(^|\s|\>|[#{PUNCT}{(\[])
562
+ #{ttr}
563
+ (#{C})
564
+ (?::(\S+?))?
565
+ ([^\s#{ttr}]+?(?:[^\n]|\n(?!\n))*?)
566
+ ([#{PUNCT}]*?)
567
+ #{ttr}
568
+ (?=[\])}]|[#{PUNCT}]+?|<|\s|$)/xm
569
+
570
+ ) do |m|
571
+
572
+ start,atts,cite,content,tend = $~[1..5]
573
+ atts = pba( atts )
574
+ atts << " cite=\"#{ cite }\"" if cite
575
+
576
+ "#{ start }{\\#{ ht } #{ content }#{ tend }}"
577
+
578
+ end
579
+ end
580
+ end
581
+
582
+ def links( text )
583
+ text.gsub!( /
584
+ ([\s\[{(]|[#{PUNCT}])? # $pre
585
+ " # start
586
+ (#{C}) # $atts
587
+ ([^"]+?) # $text
588
+ \s?
589
+ (?:\(([^)]+?)\)(?="))? # $title
590
+ ":
591
+ (\S+?) # $url
592
+ (\/)? # $slash
593
+ ([^\w\/;]*?) # $post
594
+ (?=\s|$)
595
+ /x ) do |m|
596
+ pre,atts,text,title,url,slash,post = $~[1..7]
597
+
598
+ url = check_refs( url )
599
+
600
+ atts = pba( atts )
601
+ atts << " title=\"#{ title }\"" if title
602
+ atts = shelve( atts ) if atts
603
+
604
+ "#{ pre }<a href=\"#{ url }#{ slash }\"#{ atts }>#{ text }</a>#{ post }"
605
+ end
606
+ end
607
+
608
+ def get_refs( text )
609
+ text.gsub!( /(^|\s)\[(.+?)\]((?:http:\/\/|javascript:|ftp:\/\/|\/)\S+?)(?=\s|$)/ ) do |m|
610
+ flag, url = $~[1..2]
611
+ @urlrefs[flag] = url
612
+ end
613
+ end
614
+
615
+ def check_refs( text )
616
+ @urlrefs[text] || text
617
+ end
618
+
619
+ def image( text )
620
+ text.gsub!( /
621
+ \! # opening
622
+ (\<|\=|\>)? # optional alignment atts
623
+ (#{C}) # optional style,class atts
624
+ (?:\. )? # optional dot-space
625
+ ([^\s(!]+?) # presume this is the src
626
+ \s? # optional space
627
+ (?:\(((?:[^\(\)]|\([^\)]+\))+?)\))? # optional title
628
+ \! # closing
629
+ (?::#{ HYPERLINK })? # optional href
630
+ /x ) do |m|
631
+ algn,atts,url,title,href,href_a1,href_a2 = $~[1..7]
632
+ atts = pba( atts )
633
+ atts << " align=\"#{ i_align( algn ) }\"" if algn
634
+ atts << " title=\"#{ title }\"" if title
635
+ atts << " alt=\"#{ title }\""
636
+ # size = @getimagesize($url);
637
+ # if($size) $atts.= " $size[3]";
638
+
639
+ href = check_refs( href ) if href
640
+ url = check_refs( url )
641
+
642
+ out = ''
643
+ out << "<a href=\"#{ href }\">" if href
644
+ out << "<img src=\"#{ url }\"#{ atts } />"
645
+ out << "</a>#{ href_a1 }#{ href_a2 }" if href
646
+
647
+ out
648
+ end
649
+ end
650
+
651
+ def code( text )
652
+ text.gsub!( /
653
+ (?:^|([\s\(\[{])) # 1 open bracket?
654
+ @ # opening
655
+ (?:\|(\w+?)\|)? # 2 language
656
+ (\S(?:[^\n]|\n(?!\n))*?) # 3 code
657
+ @ # closing
658
+ (?:$|([\]})])|
659
+ (?=[#{PUNCT}]{1,2}|
660
+ \s)) # 4 closing bracket?
661
+ /x ) do |m|
662
+ before,lang,code,after = $~[1..4]
663
+ lang = " language=\"#{ lang }\"" if lang
664
+ "#{ before }<code#{ lang }>#{ code }</code>#{ after }"
665
+ end
666
+ end
667
+
668
+ def shelve( val )
669
+ @shelf << val
670
+ " <#{ @shelf.length }>"
671
+ end
672
+
673
+ def retrieve( text )
674
+ @shelf.each_with_index do |r, i|
675
+ text.gsub!( " <#{ i + 1 }>", r )
676
+ end
677
+ end
678
+
679
+ def incoming_entities( text )
680
+ ## turn any incoming ampersands into a dummy character for now.
681
+ ## This uses a negative lookahead for alphanumerics followed by a semicolon,
682
+ ## implying an incoming html entity, to be skipped
683
+
684
+ text.gsub!( /&(?![#a-z0-9]+;)/i, "x%x%" )
685
+ end
686
+
687
+ def encode_entities( text )
688
+ ## Convert high and low ascii to entities.
689
+ # if $-K == "UTF-8"
690
+ # encode_high( text )
691
+ # else
692
+ text.texesc!( :NoQuotes )
693
+ # end
694
+ end
695
+
696
+ def fix_entities( text )
697
+ ## de-entify any remaining angle brackets or ampersands
698
+ text.gsub!( "\&", "&" )
699
+ text.gsub!( "\%", "%" )
700
+ end
701
+
702
+ def clean_white_space( text )
703
+ text.gsub!( /\r\n/, "\n" )
704
+ text.gsub!( /\t/, '' )
705
+ text.gsub!( /\n{3,}/, "\n\n" )
706
+ text.gsub!( /\n *\n/, "\n\n" )
707
+ text.gsub!( /"$/, "\" " )
708
+ end
709
+
710
+ def no_textile( text )
711
+ text.gsub!( /(^|\s)==(.*?)==(\s|$)?/,
712
+ '\1<notextile>\2</notextile>\3' )
713
+ end
714
+
715
+ def footnote_ref( text )
716
+ text.gsub!( /\[([0-9]+?)\](\s)?/,
717
+ '\footnote{\1}\2')
718
+ #'<sup><a href="#fn\1">\1</a></sup>\2' )
719
+ end
720
+
721
+ def inline( text )
722
+ image text
723
+ links text
724
+ code text
725
+ span text
726
+ end
727
+
728
+ def glyphs_deep( text )
729
+ codepre = 0
730
+ offtags = /(?:code|pre|kbd|notextile)/
731
+ if text !~ /<.*>/
732
+ # pgl text
733
+ footnote_ref text
734
+ else
735
+ used_offtags = {}
736
+ text.gsub!( /(?:[^<].*?(?=<[^\n]*?>|$)|<[^\n]*?>+)/m ) do |line|
737
+ tagline = ( line =~ /^<.*>/ )
738
+
739
+ ## matches are off if we're between <code>, <pre> etc.
740
+ if tagline
741
+ if line =~ /<(#{ offtags })>/i
742
+ codepre += 1
743
+ used_offtags[$1] = true
744
+ line.texesc!( :NoQuotes ) if codepre - used_offtags.length > 0
745
+ elsif line =~ /<\/(#{ offtags })>/i
746
+ line.texesc!( :NoQuotes ) if codepre - used_offtags.length > 0
747
+ codepre -= 1 unless codepre.zero?
748
+ used_offtags = {} if codepre.zero?
749
+ elsif @filter_html or codepre > 0
750
+ line.texesc!( :NoQuotes )
751
+ ## line.gsub!( /&lt;(\/?#{ offtags })&gt;/, '<\1>' )
752
+ end
753
+ ## do htmlspecial if between <code>
754
+ elsif codepre > 0
755
+ line.texesc!( :NoQuotes )
756
+ ## line.gsub!( /&lt;(\/?#{ offtags })&gt;/, '<\1>' )
757
+ elsif not tagline
758
+ inline line
759
+ glyphs_deep line
760
+ end
761
+
762
+ line
763
+ end
764
+ end
765
+ end
766
+
767
+ def glyphs( text )
768
+ text.gsub!( /"\z/, "\" " )
769
+ ## if no html, do a simple search and replace...
770
+ if text !~ /<.*>/
771
+ inline text
772
+ end
773
+ glyphs_deep text
774
+ end
775
+
776
+ def i_align( text )
777
+ I_ALGN_VALS[text]
778
+ end
779
+
780
+ def h_align( text )
781
+ H_ALGN_VALS[text]
782
+ end
783
+
784
+ def v_align( text )
785
+ V_ALGN_VALS[text]
786
+ end
787
+
788
+ def encode_high( text )
789
+ ## mb_encode_numericentity($text, $cmap, $charset);
790
+ end
791
+
792
+ def decode_high( text )
793
+ ## mb_decode_numericentity($text, $cmap, $charset);
794
+ end
795
+
796
+ def textile_popup_help( name, helpvar, windowW, windowH )
797
+ ' <a target="_blank" href="http://www.textpattern.com/help/?item=' + helpvar + '" onclick="window.open(this.href, \'popupwindow\', \'width=' + windowW + ',height=' + windowH + ',scrollbars,resizable\'); return false;">' + name + '</a><br />'
798
+ end
799
+
800
+ CMAP = [
801
+ 160, 255, 0, 0xffff,
802
+ 402, 402, 0, 0xffff,
803
+ 913, 929, 0, 0xffff,
804
+ 931, 937, 0, 0xffff,
805
+ 945, 969, 0, 0xffff,
806
+ 977, 978, 0, 0xffff,
807
+ 982, 982, 0, 0xffff,
808
+ 8226, 8226, 0, 0xffff,
809
+ 8230, 8230, 0, 0xffff,
810
+ 8242, 8243, 0, 0xffff,
811
+ 8254, 8254, 0, 0xffff,
812
+ 8260, 8260, 0, 0xffff,
813
+ 8465, 8465, 0, 0xffff,
814
+ 8472, 8472, 0, 0xffff,
815
+ 8476, 8476, 0, 0xffff,
816
+ 8482, 8482, 0, 0xffff,
817
+ 8501, 8501, 0, 0xffff,
818
+ 8592, 8596, 0, 0xffff,
819
+ 8629, 8629, 0, 0xffff,
820
+ 8656, 8660, 0, 0xffff,
821
+ 8704, 8704, 0, 0xffff,
822
+ 8706, 8707, 0, 0xffff,
823
+ 8709, 8709, 0, 0xffff,
824
+ 8711, 8713, 0, 0xffff,
825
+ 8715, 8715, 0, 0xffff,
826
+ 8719, 8719, 0, 0xffff,
827
+ 8721, 8722, 0, 0xffff,
828
+ 8727, 8727, 0, 0xffff,
829
+ 8730, 8730, 0, 0xffff,
830
+ 8733, 8734, 0, 0xffff,
831
+ 8736, 8736, 0, 0xffff,
832
+ 8743, 8747, 0, 0xffff,
833
+ 8756, 8756, 0, 0xffff,
834
+ 8764, 8764, 0, 0xffff,
835
+ 8773, 8773, 0, 0xffff,
836
+ 8776, 8776, 0, 0xffff,
837
+ 8800, 8801, 0, 0xffff,
838
+ 8804, 8805, 0, 0xffff,
839
+ 8834, 8836, 0, 0xffff,
840
+ 8838, 8839, 0, 0xffff,
841
+ 8853, 8853, 0, 0xffff,
842
+ 8855, 8855, 0, 0xffff,
843
+ 8869, 8869, 0, 0xffff,
844
+ 8901, 8901, 0, 0xffff,
845
+ 8968, 8971, 0, 0xffff,
846
+ 9001, 9002, 0, 0xffff,
847
+ 9674, 9674, 0, 0xffff,
848
+ 9824, 9824, 0, 0xffff,
849
+ 9827, 9827, 0, 0xffff,
850
+ 9829, 9830, 0, 0xffff,
851
+ 338, 339, 0, 0xffff,
852
+ 352, 353, 0, 0xffff,
853
+ 376, 376, 0, 0xffff,
854
+ 710, 710, 0, 0xffff,
855
+ 732, 732, 0, 0xffff,
856
+ 8194, 8195, 0, 0xffff,
857
+ 8201, 8201, 0, 0xffff,
858
+ 8204, 8207, 0, 0xffff,
859
+ 8211, 8212, 0, 0xffff,
860
+ 8216, 8218, 0, 0xffff,
861
+ 8218, 8218, 0, 0xffff,
862
+ 8220, 8222, 0, 0xffff,
863
+ 8224, 8225, 0, 0xffff,
864
+ 8240, 8240, 0, 0xffff,
865
+ 8249, 8250, 0, 0xffff,
866
+ 8364, 8364, 0, 0xffff
867
+ ]
868
+ end
869
+