instiki 0.9.2 → 0.10.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 (102) hide show
  1. data/CHANGELOG +165 -0
  2. data/README +68 -172
  3. data/app/controllers/admin_controller.rb +94 -0
  4. data/app/controllers/application.rb +131 -0
  5. data/app/controllers/file_controller.rb +129 -0
  6. data/app/controllers/wiki_controller.rb +354 -0
  7. data/{libraries/view_helper.rb → app/helpers/application_helper.rb} +68 -33
  8. data/app/models/author.rb +3 -3
  9. data/app/models/chunks/category.rb +33 -31
  10. data/app/models/chunks/chunk.rb +86 -20
  11. data/app/models/chunks/engines.rb +54 -38
  12. data/app/models/chunks/include.rb +41 -29
  13. data/app/models/chunks/literal.rb +31 -19
  14. data/app/models/chunks/nowiki.rb +28 -31
  15. data/app/models/chunks/test.rb +18 -18
  16. data/app/models/chunks/uri.rb +182 -97
  17. data/app/models/chunks/wiki.rb +141 -82
  18. data/app/models/file_yard.rb +58 -0
  19. data/app/models/page.rb +112 -86
  20. data/app/models/page_lock.rb +22 -23
  21. data/app/models/page_set.rb +89 -64
  22. data/app/models/revision.rb +123 -90
  23. data/app/models/web.rb +176 -89
  24. data/app/models/wiki_content.rb +207 -105
  25. data/app/models/wiki_service.rb +233 -83
  26. data/app/models/wiki_words.rb +23 -25
  27. data/app/views/{wiki/new_system.rhtml → admin/create_system.rhtml} +83 -78
  28. data/app/views/{wiki/new_web.rhtml → admin/create_web.rhtml} +69 -64
  29. data/app/views/admin/edit_web.rhtml +136 -0
  30. data/app/views/file/file.rhtml +19 -0
  31. data/app/views/file/import.rhtml +23 -0
  32. data/app/views/layouts/default.rhtml +85 -0
  33. data/app/views/markdown_help.rhtml +12 -16
  34. data/app/views/mixed_help.rhtml +7 -0
  35. data/app/views/navigation.rhtml +30 -19
  36. data/app/views/rdoc_help.rhtml +12 -16
  37. data/app/views/textile_help.rhtml +24 -28
  38. data/app/views/wiki/authors.rhtml +11 -13
  39. data/app/views/wiki/edit.rhtml +39 -31
  40. data/app/views/wiki/export.rhtml +12 -14
  41. data/app/views/wiki/feeds.rhtml +14 -10
  42. data/app/views/wiki/list.rhtml +64 -57
  43. data/app/views/wiki/locked.rhtml +23 -14
  44. data/app/views/wiki/login.rhtml +14 -11
  45. data/app/views/wiki/new.rhtml +31 -27
  46. data/app/views/wiki/page.rhtml +115 -81
  47. data/app/views/wiki/print.rhtml +14 -16
  48. data/app/views/wiki/published.rhtml +9 -10
  49. data/app/views/wiki/recently_revised.rhtml +27 -30
  50. data/app/views/wiki/revision.rhtml +103 -81
  51. data/app/views/wiki/rollback.rhtml +14 -9
  52. data/app/views/wiki/rss_feed.rhtml +22 -22
  53. data/app/views/wiki/search.rhtml +38 -15
  54. data/app/views/wiki/tex.rhtml +22 -22
  55. data/app/views/wiki/tex_web.rhtml +34 -34
  56. data/app/views/wiki/web_list.rhtml +18 -13
  57. data/app/views/wiki_words_help.rhtml +9 -8
  58. data/config/environment.rb +82 -0
  59. data/config/environments/development.rb +5 -0
  60. data/config/environments/production.rb +4 -0
  61. data/config/environments/test.rb +17 -0
  62. data/config/routes.rb +18 -0
  63. data/instiki +6 -67
  64. data/instiki.rb +3 -0
  65. data/lib/active_record_stub.rb +31 -0
  66. data/{libraries/diff → lib}/diff.rb +444 -475
  67. data/lib/instiki_errors.rb +15 -0
  68. data/{libraries → lib}/rdocsupport.rb +151 -155
  69. data/lib/redcloth_for_tex.rb +736 -0
  70. data/natives/osx/desktop_launcher/AppDelegate.h +18 -0
  71. data/natives/osx/desktop_launcher/AppDelegate.mm +109 -0
  72. data/natives/osx/desktop_launcher/Credits.html +16 -0
  73. data/natives/osx/desktop_launcher/English.lproj/InfoPlist.strings +0 -0
  74. data/natives/osx/desktop_launcher/English.lproj/MainMenu.nib/classes.nib +13 -0
  75. data/natives/osx/desktop_launcher/English.lproj/MainMenu.nib/info.nib +24 -0
  76. data/natives/osx/desktop_launcher/English.lproj/MainMenu.nib/objects.nib +0 -0
  77. data/natives/osx/desktop_launcher/Info.plist +13 -0
  78. data/natives/osx/desktop_launcher/Instiki.xcode/project.pbxproj +592 -0
  79. data/natives/osx/desktop_launcher/Instiki_Prefix.pch +7 -0
  80. data/natives/osx/desktop_launcher/MakeDMG.sh +9 -0
  81. data/natives/osx/desktop_launcher/main.mm +14 -0
  82. data/natives/osx/desktop_launcher/version.plist +16 -0
  83. data/public/404.html +6 -0
  84. data/public/500.html +6 -0
  85. data/public/dispatch.rb +10 -0
  86. data/public/favicon.ico +0 -0
  87. data/public/javascripts/edit_web.js +52 -0
  88. data/public/javascripts/prototype.js +336 -0
  89. data/{app/views/static_style_sheet.rhtml → public/stylesheets/instiki.css} +221 -198
  90. data/script/breakpointer +4 -0
  91. data/script/server +93 -0
  92. metadata +59 -32
  93. data/app/controllers/wiki.rb +0 -389
  94. data/app/models/chunks/match.rb +0 -19
  95. data/app/views/bottom.rhtml +0 -4
  96. data/app/views/top.rhtml +0 -49
  97. data/app/views/wiki/edit_web.rhtml +0 -138
  98. data/libraries/action_controller_servlet.rb +0 -177
  99. data/libraries/erb.rb +0 -490
  100. data/libraries/madeleine_service.rb +0 -68
  101. data/libraries/redcloth_for_tex.rb +0 -869
  102. data/libraries/web_controller_server.rb +0 -81
@@ -0,0 +1,15 @@
1
+ # Model methods that want to rollback transactions gracefully
2
+ # (i.e, returning the user back to the form from which the request was posted)
3
+ # should raise Instiki::ValidationError.
4
+ #
5
+ # E.g. if a model object does
6
+ # raise "Foo: '#{foo}' is not equal to Bar: '#{bar}'" if (foo != bar)
7
+ #
8
+ # then the operation is not committed; Rails returns the user to the page
9
+ # where s/he was entering foo and bar, and the error message will be displayed
10
+ # on the page
11
+
12
+ module Instiki
13
+ class ValidationError < StandardError
14
+ end
15
+ end
@@ -1,156 +1,152 @@
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
-
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
+ super.sub(/^<p>\n/, '').sub(/<\/p>$/, '')
37
+ end
38
+ end
39
+
40
+ # Handle special hyperlinking requirments for RDoc formatted
41
+ # entries. Requires RDoc
42
+
43
+ class HyperLinkHtml < SM::ToHtml
44
+
45
+ # Initialize the HyperLinkHtml object.
46
+ # [path] location of the node
47
+ # [site] object representing the whole site (typically of class
48
+ # +Site+)
49
+ def initialize
50
+ super()
51
+ add_tag(:CENTER, "<center>", "</center>")
52
+ end
53
+
54
+ # handle <br/>
55
+ def handle_special_BR(special)
56
+ return "&lt;br/&gt" if special.text[0,1] == '\\'
57
+ special.text
58
+ end
59
+
60
+ # We're invoked with a potential external hyperlink.
61
+ # [mailto:] just gets inserted.
62
+ # [http:] links are checked to see if they
63
+ # reference an image. If so, that image gets inserted
64
+ # using an <img> tag. Otherwise a conventional <a href>
65
+ # is used.
66
+ # [img:] insert a <tt><img></tt> tag
67
+ # [link:] used to insert arbitrary <tt><a></tt> references
68
+ # [anchor:] used to create an anchor
69
+ def handle_special_HYPERLINK(special)
70
+ text = special.text.strip
71
+ return text[1..-1] if text[0,1] == '\\'
72
+ url = special.text.strip
73
+ if url =~ /([A-Za-z]+):(.*)/
74
+ type = $1
75
+ path = $2
76
+ else
77
+ type = "http"
78
+ path = url
79
+ url = "http://#{url}"
80
+ end
81
+
82
+ case type
83
+ when "http"
84
+ if url =~ /\.(gif|png|jpg|jpeg|bmp)$/
85
+ "<img src=\"#{url}\"/>"
86
+ else
87
+ "<a href=\"#{url}\">#{url.sub(%r{^\w+:/*}, '')}</a>"
88
+ end
89
+ when "img"
90
+ "<img src=\"#{path}\"/>"
91
+ when "link"
92
+ "<a href=\"#{path}\">#{path}</a>"
93
+ when "anchor"
94
+ "<a name=\"#{path}\"></a>"
95
+ else
96
+ "<a href=\"#{url}\">#{url.sub(%r{^\w+:/*}, '')}</a>"
97
+ end
98
+ end
99
+
100
+ # Here's a hyperlink where the label is different to the URL
101
+ # [[url label that may contain spaces]]
102
+ #
103
+
104
+ def handle_special_TIDYLINK(special)
105
+ text = special.text.strip
106
+ return text[1..-1] if text[0,1] == '\\'
107
+ unless text =~ /\[\[(\S+?)\s+(.+?)\]\]/
108
+ return text
109
+ end
110
+ url = $1
111
+ label = $2
112
+ label = RDocFormatter.new(label).to_html
113
+ label = label.split.select{|x| x =~ /\S/}.
114
+ map{|x| x.chomp}.join(' ')
115
+
116
+ case url
117
+ when /link:(\S+)/
118
+ return %{<a href="#{$1}">#{label}</a>}
119
+ when /img:(\S+)/
120
+ return %{<img src="http://#{$1}" alt="#{label}" />}
121
+ when /rubytalk:(\S+)/
122
+ return %{<a href="http://ruby-talk.org/blade/#{$1}">#{label}</a>}
123
+ when /rubygarden:(\S+)/
124
+ return %{<a href="http://www.rubygarden.org/ruby?#{$1}">#{label}</a>}
125
+ when /c2:(\S+)/
126
+ return %{<a href="http://c2.com/cgi/wiki?#{$1}">#{label}</a>}
127
+ when /isbn:(\S+)/
128
+ return %{<a href="http://search.barnesandnoble.com/bookSearch/} +
129
+ %{isbnInquiry.asp?isbn=#{$1}">#{label}</a>}
130
+ end
131
+
132
+ unless url =~ /\w+?:/
133
+ url = "http://#{url}"
134
+ end
135
+
136
+ "<a href=\"#{url}\">#{label}</a>"
137
+ end
138
+ end
139
+
140
+ class RDocFormatter
141
+ def initialize(text)
142
+ @text = text
143
+ end
144
+
145
+ def to_html
146
+ markup = RDocMarkup.new
147
+ h = HyperLinkHtml.new
148
+ markup.convert(@text, h)
149
+ end
150
+ end
151
+
156
152
  end
@@ -0,0 +1,736 @@
1
+ # This is RedCloth (http://www.whytheluckystiff.net/ruby/redcloth/)
2
+ # converted by David Heinemeier Hansson to emit Tex
3
+
4
+ class String
5
+ # Flexible HTML escaping
6
+ def texesc!( mode )
7
+ gsub!( '&', '\\\\&' )
8
+ gsub!( '%', '\%' )
9
+ gsub!( '$', '\$' )
10
+ gsub!( '~', '$\sim$' )
11
+ end
12
+ end
13
+
14
+
15
+ def table_of_contents(text, pages)
16
+ text.gsub( /^([#*]+? .*?)$(?![^#*])/m ) do |match|
17
+ lines = match.split( /\n/ )
18
+ last_line = -1
19
+ depth = []
20
+ lines.each_with_index do |line, line_id|
21
+ if line =~ /^([#*]+) (.*)$/m
22
+ tl,content = $~[1..2]
23
+ content.gsub! /[\[\]]/, ""
24
+ content.strip!
25
+
26
+ if depth.last
27
+ if depth.last.length > tl.length
28
+ (depth.length - 1).downto(0) do |i|
29
+ break if depth[i].length == tl.length
30
+ lines[line_id - 1] << "" # "\n\t\\end{#{ lT( depth[i] ) }}\n\t"
31
+ depth.pop
32
+ end
33
+ end
34
+ if !depth.last.nil? && !tl.length.nil? && depth.last.length == tl.length
35
+ lines[line_id - 1] << ''
36
+ end
37
+ end
38
+
39
+ depth << tl unless depth.last == tl
40
+
41
+ subsection_depth = [depth.length - 1, 2].min
42
+
43
+ lines[line_id] = "\n\\#{ "sub" * subsection_depth }section{#{ content }}"
44
+ lines[line_id] += "\n#{pages[content]}" if pages.keys.include?(content)
45
+
46
+ lines[line_id] = "\\pagebreak\n#{lines[line_id]}" if subsection_depth == 0
47
+
48
+ last_line = line_id
49
+
50
+ elsif line =~ /^\s+\S/
51
+ last_line = line_id
52
+ elsif line_id - last_line < 2 and line =~ /^\S/
53
+ last_line = line_id
54
+ end
55
+ if line_id - last_line > 1 or line_id == lines.length - 1
56
+ depth.delete_if do |v|
57
+ lines[last_line] << "" # "\n\t\\end{#{ lT( v ) }}"
58
+ end
59
+ end
60
+ end
61
+ lines.join( "\n" )
62
+ end
63
+ end
64
+
65
+ class RedClothForTex < String
66
+
67
+ VERSION = '2.0.7'
68
+
69
+ #
70
+ # Mapping of 8-bit ASCII codes to HTML numerical entity equivalents.
71
+ # (from PyTextile)
72
+ #
73
+ TEXTILE_TAGS =
74
+
75
+ [[128, 8364], [129, 0], [130, 8218], [131, 402], [132, 8222], [133, 8230],
76
+ [134, 8224], [135, 8225], [136, 710], [137, 8240], [138, 352], [139, 8249],
77
+ [140, 338], [141, 0], [142, 0], [143, 0], [144, 0], [145, 8216], [146, 8217],
78
+ [147, 8220], [148, 8221], [149, 8226], [150, 8211], [151, 8212], [152, 732],
79
+ [153, 8482], [154, 353], [155, 8250], [156, 339], [157, 0], [158, 0], [159, 376]].
80
+
81
+ collect! do |a, b|
82
+ [a.chr, ( b.zero? and "" or "&#{ b };" )]
83
+ end
84
+
85
+ #
86
+ # Regular expressions to convert to HTML.
87
+ #
88
+ A_HLGN = /(?:(?:<>|<|>|\=|[()]+)+)/
89
+ A_VLGN = /[\-^~]/
90
+ C_CLAS = '(?:\([^)]+\))'
91
+ C_LNGE = '(?:\[[^\]]+\])'
92
+ C_STYL = '(?:\{[^}]+\})'
93
+ S_CSPN = '(?:\\\\\d+)'
94
+ S_RSPN = '(?:/\d+)'
95
+ A = "(?:#{A_HLGN}?#{A_VLGN}?|#{A_VLGN}?#{A_HLGN}?)"
96
+ S = "(?:#{S_CSPN}?#{S_RSPN}|#{S_RSPN}?#{S_CSPN}?)"
97
+ C = "(?:#{C_CLAS}?#{C_STYL}?#{C_LNGE}?|#{C_STYL}?#{C_LNGE}?#{C_CLAS}?|#{C_LNGE}?#{C_STYL}?#{C_CLAS}?)"
98
+ # PUNCT = Regexp::quote( '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~' )
99
+ PUNCT = Regexp::quote( '!"#$%&\'*+,-./:;=?@\\^_`|~' )
100
+ HYPERLINK = '(\S+?)([^\w\s/;=\?]*?)(\s|$)'
101
+
102
+ GLYPHS = [
103
+ # [ /([^\s\[{(>])?\'([dmst]\b|ll\b|ve\b|\s|:|$)/, '\1&#8217;\2' ], # single closing
104
+ [ /([^\s\[{(>])\'/, '\1&#8217;' ], # single closing
105
+ [ /\'(?=\s|s\b|[#{PUNCT}])/, '&#8217;' ], # single closing
106
+ [ /\'/, '&#8216;' ], # single opening
107
+ # [ /([^\s\[{(])?"(\s|:|$)/, '\1&#8221;\2' ], # double closing
108
+ [ /([^\s\[{(>])"/, '\1&#8221;' ], # double closing
109
+ [ /"(?=\s|[#{PUNCT}])/, '&#8221;' ], # double closing
110
+ [ /"/, '&#8220;' ], # double opening
111
+ [ /\b( )?\.{3}/, '\1&#8230;' ], # ellipsis
112
+ [ /\b([A-Z][A-Z0-9]{2,})\b(?:[(]([^)]*)[)])/, '<acronym title="\2">\1</acronym>' ], # 3+ uppercase acronym
113
+ [ /(^|[^"][>\s])([A-Z][A-Z0-9 ]{2,})([^<a-z0-9]|$)/, '\1<span class="caps">\2</span>\3' ], # 3+ uppercase caps
114
+ [ /(\.\s)?\s?--\s?/, '\1&#8212;' ], # em dash
115
+ [ /\s->\s/, ' &rarr; ' ], # en dash
116
+ [ /\s-\s/, ' &#8211; ' ], # en dash
117
+ [ /(\d+) ?x ?(\d+)/, '\1&#215;\2' ], # dimension sign
118
+ [ /\b ?[(\[]TM[\])]/i, '&#8482;' ], # trademark
119
+ [ /\b ?[(\[]R[\])]/i, '&#174;' ], # registered
120
+ [ /\b ?[(\[]C[\])]/i, '&#169;' ] # copyright
121
+ ]
122
+
123
+ I_ALGN_VALS = {
124
+ '<' => 'left',
125
+ '=' => 'center',
126
+ '>' => 'right'
127
+ }
128
+
129
+ H_ALGN_VALS = {
130
+ '<' => 'left',
131
+ '=' => 'center',
132
+ '>' => 'right',
133
+ '<>' => 'justify'
134
+ }
135
+
136
+ V_ALGN_VALS = {
137
+ '^' => 'top',
138
+ '-' => 'middle',
139
+ '~' => 'bottom'
140
+ }
141
+
142
+ QTAGS = [
143
+ ['**', 'bf'],
144
+ ['*', 'bf'],
145
+ ['??', 'cite'],
146
+ ['-', 'del'],
147
+ ['__', 'underline'],
148
+ ['_', 'em'],
149
+ ['%', 'span'],
150
+ ['+', 'ins'],
151
+ ['^', 'sup'],
152
+ ['~', 'sub']
153
+ ]
154
+
155
+ def self.available?
156
+ if not defined? @@available
157
+ begin
158
+ @@available = system "pdflatex -version"
159
+ rescue Errno::ENOENT
160
+ @@available = false
161
+ end
162
+ end
163
+ @@available
164
+ end
165
+
166
+ #
167
+ # Two accessor for setting security restrictions.
168
+ #
169
+ # This is a nice thing if you're using RedCloth for
170
+ # formatting in public places (e.g. Wikis) where you
171
+ # don't want users to abuse HTML for bad things.
172
+ #
173
+ # If +:filter_html+ is set, HTML which wasn't
174
+ # created by the Textile processor will be escaped.
175
+ #
176
+ # If +:filter_styles+ is set, it will also disable
177
+ # the style markup specifier. ('{color: red}')
178
+ #
179
+ attr_accessor :filter_html, :filter_styles
180
+
181
+ #
182
+ # Accessor for toggling line folding.
183
+ #
184
+ # If +:fold_lines+ is set, single newlines will
185
+ # not be converted to break tags.
186
+ #
187
+ attr_accessor :fold_lines
188
+
189
+ def initialize( string, restrictions = [] )
190
+ restrictions.each { |r| method( "#{ r }=" ).call( true ) }
191
+ super( string )
192
+ end
193
+
194
+ #
195
+ # Generate tex.
196
+ #
197
+ def to_tex( lite = false )
198
+
199
+ # make our working copy
200
+ text = self.dup
201
+
202
+ @urlrefs = {}
203
+ @shelf = []
204
+
205
+ # incoming_entities text
206
+ fix_entities text
207
+ clean_white_space text
208
+
209
+ get_refs text
210
+
211
+ no_textile text
212
+
213
+ unless lite
214
+ lists text
215
+ table text
216
+ end
217
+
218
+ glyphs text
219
+
220
+ unless lite
221
+ fold text
222
+ block text
223
+ end
224
+
225
+ retrieve text
226
+ encode_entities text
227
+
228
+ text.gsub!(/\[\[(.*?)\]\]/, "\\1")
229
+ text.gsub!(/_/, "\\_")
230
+ text.gsub!( /<\/?notextile>/, '' )
231
+ # text.gsub!( /x%x%/, '&#38;' )
232
+ # text.gsub!( /<br \/>/, "<br />\n" )
233
+ text.strip!
234
+ text
235
+
236
+ end
237
+
238
+ def pgl( text )
239
+ GLYPHS.each do |re, resub|
240
+ text.gsub! re, resub
241
+ end
242
+ end
243
+
244
+ def pba( text_in, element = "" )
245
+
246
+ return '' unless text_in
247
+
248
+ style = []
249
+ text = text_in.dup
250
+ if element == 'td'
251
+ colspan = $1 if text =~ /\\(\d+)/
252
+ rowspan = $1 if text =~ /\/(\d+)/
253
+ style << "vertical-align:#{ v_align( $& ) };" if text =~ A_VLGN
254
+ end
255
+
256
+ style << "#{ $1 };" if not @filter_styles and
257
+ text.sub!( /\{([^}]*)\}/, '' )
258
+
259
+ lang = $1 if
260
+ text.sub!( /\[([^)]+?)\]/, '' )
261
+
262
+ cls = $1 if
263
+ text.sub!( /\(([^()]+?)\)/, '' )
264
+
265
+ style << "padding-left:#{ $1.length }em;" if
266
+ text.sub!( /([(]+)/, '' )
267
+
268
+ style << "padding-right:#{ $1.length }em;" if text.sub!( /([)]+)/, '' )
269
+
270
+ style << "text-align:#{ h_align( $& ) };" if text =~ A_HLGN
271
+
272
+ cls, id = $1, $2 if cls =~ /^(.*?)#(.*)$/
273
+
274
+ atts = ''
275
+ atts << " style=\"#{ style.join }\"" unless style.empty?
276
+ atts << " class=\"#{ cls }\"" unless cls.to_s.empty?
277
+ atts << " lang=\"#{ lang }\"" if lang
278
+ atts << " id=\"#{ id }\"" if id
279
+ atts << " colspan=\"#{ colspan }\"" if colspan
280
+ atts << " rowspan=\"#{ rowspan }\"" if rowspan
281
+
282
+ atts
283
+ end
284
+
285
+ def table( text )
286
+ text << "\n\n"
287
+ text.gsub!( /^(?:table(_?#{S}#{A}#{C})\. ?\n)?^(#{A}#{C}\.? ?\|.*?\|)\n\n/m ) do |matches|
288
+
289
+ tatts, fullrow = $~[1..2]
290
+ tatts = pba( tatts, 'table' )
291
+ rows = []
292
+
293
+ fullrow.
294
+ split( /\|$/m ).
295
+ delete_if { |x| x.empty? }.
296
+ each do |row|
297
+
298
+ ratts, row = pba( $1, 'tr' ), $2 if row =~ /^(#{A}#{C}\. )(.*)/m
299
+
300
+ cells = []
301
+ row.split( '|' ).each do |cell|
302
+ ctyp = 'd'
303
+ ctyp = 'h' if cell =~ /^_/
304
+
305
+ catts = ''
306
+ catts, cell = pba( $1, 'td' ), $2 if cell =~ /^(_?#{S}#{A}#{C}\. )(.*)/
307
+
308
+ unless cell.strip.empty?
309
+ cells << "\t\t\t<t#{ ctyp }#{ catts }>#{ cell }</t#{ ctyp }>"
310
+ end
311
+ end
312
+ rows << "\t\t<tr#{ ratts }>\n#{ cells.join( "\n" ) }\n\t\t</tr>"
313
+ end
314
+ "\t<table#{ tatts }>\n#{ rows.join( "\n" ) }\n\t</table>\n\n"
315
+ end
316
+ end
317
+
318
+ def lists( text )
319
+ text.gsub!( /^([#*]+?#{C} .*?)$(?![^#*])/m ) do |match|
320
+ lines = match.split( /\n/ )
321
+ last_line = -1
322
+ depth = []
323
+ lines.each_with_index do |line, line_id|
324
+ if line =~ /^([#*]+)(#{A}#{C}) (.*)$/m
325
+ tl,atts,content = $~[1..3]
326
+ if depth.last
327
+ if depth.last.length > tl.length
328
+ (depth.length - 1).downto(0) do |i|
329
+ break if depth[i].length == tl.length
330
+ lines[line_id - 1] << "\n\t\\end{#{ lT( depth[i] ) }}\n\t"
331
+ depth.pop
332
+ end
333
+ end
334
+ if !depth.last.nil? && !tl.length.nil? && depth.last.length == tl.length
335
+ lines[line_id - 1] << ''
336
+ end
337
+ end
338
+ unless depth.last == tl
339
+ depth << tl
340
+ atts = pba( atts )
341
+ lines[line_id] = "\t\\begin{#{ lT(tl) }}\n\t\\item #{ content }"
342
+ else
343
+ lines[line_id] = "\t\t\\item #{ content }"
344
+ end
345
+ last_line = line_id
346
+
347
+ elsif line =~ /^\s+\S/
348
+ last_line = line_id
349
+ elsif line_id - last_line < 2 and line =~ /^\S/
350
+ last_line = line_id
351
+ end
352
+ if line_id - last_line > 1 or line_id == lines.length - 1
353
+ depth.delete_if do |v|
354
+ lines[last_line] << "\n\t\\end{#{ lT( v ) }}"
355
+ end
356
+ end
357
+ end
358
+ lines.join( "\n" )
359
+ end
360
+ end
361
+
362
+ def lT( text )
363
+ text =~ /\#$/ ? 'enumerate' : 'itemize'
364
+ end
365
+
366
+ def fold( text )
367
+ text.gsub!( /(.+)\n(?![#*\s|])/, "\\1\\\\\\\\" )
368
+ # text.gsub!( /(.+)\n(?![#*\s|])/, "\\1#{ @fold_lines ? ' ' : '<br />' }" )
369
+ end
370
+
371
+ def block( text )
372
+ pre = false
373
+ find = ['bq','h[1-6]','fn\d+']
374
+
375
+ regexp_cue = []
376
+
377
+ lines = text.split( /\n/ ) + [' ']
378
+ new_text =
379
+ lines.collect do |line|
380
+ pre = true if line =~ /<(pre|notextile)>/i
381
+ find.each do |tag|
382
+ line.gsub!( /^(#{ tag })(#{A}#{C})\.(?::(\S+))? (.*)$/ ) do |m|
383
+ tag,atts,cite,content = $~[1..4]
384
+
385
+ atts = pba( atts )
386
+
387
+ if tag =~ /fn(\d+)/
388
+ # tag = 'p';
389
+ # atts << " id=\"fn#{ $1 }\""
390
+ regexp_cue << [ /footnote\{#{$1}}/, "footnote{#{content}}" ]
391
+ content = ""
392
+ end
393
+
394
+ if tag =~ /h([1-6])/
395
+ section_type = "sub" * [$1.to_i - 1, 2].min
396
+ start = "\t\\#{section_type}section*{"
397
+ tend = "}"
398
+ end
399
+
400
+ if tag == "bq"
401
+ cite = check_refs( cite )
402
+ cite = " cite=\"#{ cite }\"" if cite
403
+ start = "\t\\begin{quotation}\n\\noindent {\\em ";
404
+ tend = "}\n\t\\end{quotation}";
405
+ end
406
+
407
+ "#{ start }#{ content }#{ tend }"
408
+ end unless pre
409
+ end
410
+
411
+ #line.gsub!( /^(?!\t|<\/?pre|<\/?notextile|<\/?code|$| )(.*)/, "\t<p>\\1</p>" )
412
+
413
+ #line.gsub!( "<br />", "\n" ) if pre
414
+ # pre = false if line =~ /<\/(pre|notextile)>/i
415
+
416
+ line
417
+ end.join( "\n" )
418
+ text.replace( new_text )
419
+ regexp_cue.each { |pair| text.gsub!(pair.first, pair.last) }
420
+ end
421
+
422
+ def span( text )
423
+ QTAGS.each do |tt, ht|
424
+ ttr = Regexp::quote( tt )
425
+ text.gsub!(
426
+
427
+ /(^|\s|\>|[#{PUNCT}{(\[])
428
+ #{ttr}
429
+ (#{C})
430
+ (?::(\S+?))?
431
+ ([^\s#{ttr}]+?(?:[^\n]|\n(?!\n))*?)
432
+ ([#{PUNCT}]*?)
433
+ #{ttr}
434
+ (?=[\])}]|[#{PUNCT}]+?|<|\s|$)/xm
435
+
436
+ ) do |m|
437
+
438
+ start,atts,cite,content,tend = $~[1..5]
439
+ atts = pba( atts )
440
+ atts << " cite=\"#{ cite }\"" if cite
441
+
442
+ "#{ start }{\\#{ ht } #{ content }#{ tend }}"
443
+
444
+ end
445
+ end
446
+ end
447
+
448
+ def links( text )
449
+ text.gsub!( /
450
+ ([\s\[{(]|[#{PUNCT}])? # $pre
451
+ " # start
452
+ (#{C}) # $atts
453
+ ([^"]+?) # $text
454
+ \s?
455
+ (?:\(([^)]+?)\)(?="))? # $title
456
+ ":
457
+ (\S+?) # $url
458
+ (\/)? # $slash
459
+ ([^\w\/;]*?) # $post
460
+ (?=\s|$)
461
+ /x ) do |m|
462
+ pre,atts,text,title,url,slash,post = $~[1..7]
463
+
464
+ url.gsub!(/(\\)(.)/, '\2')
465
+ url = check_refs( url )
466
+
467
+ atts = pba( atts )
468
+ atts << " title=\"#{ title }\"" if title
469
+ atts = shelve( atts ) if atts
470
+
471
+ "#{ pre }\\textit{#{ text }} \\footnote{\\texttt{\\textless #{ url }#{ slash }" +
472
+ "\\textgreater}#{ post }}"
473
+ end
474
+ end
475
+
476
+ def get_refs( text )
477
+ text.gsub!( /(^|\s)\[(.+?)\]((?:http:\/\/|javascript:|ftp:\/\/|\/)\S+?)(?=\s|$)/ ) do |m|
478
+ flag, url = $~[1..2]
479
+ @urlrefs[flag] = url
480
+ end
481
+ end
482
+
483
+ def check_refs( text )
484
+ @urlrefs[text] || text
485
+ end
486
+
487
+ def image( text )
488
+ text.gsub!( /
489
+ \! # opening
490
+ (\<|\=|\>)? # optional alignment atts
491
+ (#{C}) # optional style,class atts
492
+ (?:\. )? # optional dot-space
493
+ ([^\s(!]+?) # presume this is the src
494
+ \s? # optional space
495
+ (?:\(((?:[^\(\)]|\([^\)]+\))+?)\))? # optional title
496
+ \! # closing
497
+ (?::#{ HYPERLINK })? # optional href
498
+ /x ) do |m|
499
+ algn,atts,url,title,href,href_a1,href_a2 = $~[1..7]
500
+ atts = pba( atts )
501
+ atts << " align=\"#{ i_align( algn ) }\"" if algn
502
+ atts << " title=\"#{ title }\"" if title
503
+ atts << " alt=\"#{ title }\""
504
+ # size = @getimagesize($url);
505
+ # if($size) $atts.= " $size[3]";
506
+
507
+ href = check_refs( href ) if href
508
+ url = check_refs( url )
509
+
510
+ out = ''
511
+ out << "<a href=\"#{ href }\">" if href
512
+ out << "<img src=\"#{ url }\"#{ atts } />"
513
+ out << "</a>#{ href_a1 }#{ href_a2 }" if href
514
+
515
+ out
516
+ end
517
+ end
518
+
519
+ def code( text )
520
+ text.gsub!( /
521
+ (?:^|([\s\(\[{])) # 1 open bracket?
522
+ @ # opening
523
+ (?:\|(\w+?)\|)? # 2 language
524
+ (\S(?:[^\n]|\n(?!\n))*?) # 3 code
525
+ @ # closing
526
+ (?:$|([\]})])|
527
+ (?=[#{PUNCT}]{1,2}|
528
+ \s)) # 4 closing bracket?
529
+ /x ) do |m|
530
+ before,lang,code,after = $~[1..4]
531
+ lang = " language=\"#{ lang }\"" if lang
532
+ "#{ before }<code#{ lang }>#{ code }</code>#{ after }"
533
+ end
534
+ end
535
+
536
+ def shelve( val )
537
+ @shelf << val
538
+ " <#{ @shelf.length }>"
539
+ end
540
+
541
+ def retrieve( text )
542
+ @shelf.each_with_index do |r, i|
543
+ text.gsub!( " <#{ i + 1 }>", r )
544
+ end
545
+ end
546
+
547
+ def incoming_entities( text )
548
+ ## turn any incoming ampersands into a dummy character for now.
549
+ ## This uses a negative lookahead for alphanumerics followed by a semicolon,
550
+ ## implying an incoming html entity, to be skipped
551
+
552
+ text.gsub!( /&(?![#a-z0-9]+;)/i, "x%x%" )
553
+ end
554
+
555
+ def encode_entities( text )
556
+ ## Convert high and low ascii to entities.
557
+ # if $-K == "UTF-8"
558
+ # encode_high( text )
559
+ # else
560
+ text.texesc!( :NoQuotes )
561
+ # end
562
+ end
563
+
564
+ def fix_entities( text )
565
+ ## de-entify any remaining angle brackets or ampersands
566
+ text.gsub!( "\&", "&" )
567
+ text.gsub!( "\%", "%" )
568
+ end
569
+
570
+ def clean_white_space( text )
571
+ text.gsub!( /\r\n/, "\n" )
572
+ text.gsub!( /\t/, '' )
573
+ text.gsub!( /\n{3,}/, "\n\n" )
574
+ text.gsub!( /\n *\n/, "\n\n" )
575
+ text.gsub!( /"$/, "\" " )
576
+ end
577
+
578
+ def no_textile( text )
579
+ text.gsub!( /(^|\s)==(.*?)==(\s|$)?/,
580
+ '\1<notextile>\2</notextile>\3' )
581
+ end
582
+
583
+ def footnote_ref( text )
584
+ text.gsub!( /\[([0-9]+?)\](\s)?/,
585
+ '\footnote{\1}\2')
586
+ #'<sup><a href="#fn\1">\1</a></sup>\2' )
587
+ end
588
+
589
+ def inline( text )
590
+ image text
591
+ links text
592
+ code text
593
+ span text
594
+ end
595
+
596
+ def glyphs_deep( text )
597
+ codepre = 0
598
+ offtags = /(?:code|pre|kbd|notextile)/
599
+ if text !~ /<.*>/
600
+ # pgl text
601
+ footnote_ref text
602
+ else
603
+ used_offtags = {}
604
+ text.gsub!( /(?:[^<].*?(?=<[^\n]*?>|$)|<[^\n]*?>+)/m ) do |line|
605
+ tagline = ( line =~ /^<.*>/ )
606
+
607
+ ## matches are off if we're between <code>, <pre> etc.
608
+ if tagline
609
+ if line =~ /<(#{ offtags })>/i
610
+ codepre += 1
611
+ used_offtags[$1] = true
612
+ line.texesc!( :NoQuotes ) if codepre - used_offtags.length > 0
613
+ elsif line =~ /<\/(#{ offtags })>/i
614
+ line.texesc!( :NoQuotes ) if codepre - used_offtags.length > 0
615
+ codepre -= 1 unless codepre.zero?
616
+ used_offtags = {} if codepre.zero?
617
+ elsif @filter_html or codepre > 0
618
+ line.texesc!( :NoQuotes )
619
+ ## line.gsub!( /&lt;(\/?#{ offtags })&gt;/, '<\1>' )
620
+ end
621
+ ## do htmlspecial if between <code>
622
+ elsif codepre > 0
623
+ line.texesc!( :NoQuotes )
624
+ ## line.gsub!( /&lt;(\/?#{ offtags })&gt;/, '<\1>' )
625
+ elsif not tagline
626
+ inline line
627
+ glyphs_deep line
628
+ end
629
+
630
+ line
631
+ end
632
+ end
633
+ end
634
+
635
+ def glyphs( text )
636
+ text.gsub!( /"\z/, "\" " )
637
+ ## if no html, do a simple search and replace...
638
+ if text !~ /<.*>/
639
+ inline text
640
+ end
641
+ glyphs_deep text
642
+ end
643
+
644
+ def i_align( text )
645
+ I_ALGN_VALS[text]
646
+ end
647
+
648
+ def h_align( text )
649
+ H_ALGN_VALS[text]
650
+ end
651
+
652
+ def v_align( text )
653
+ V_ALGN_VALS[text]
654
+ end
655
+
656
+ def encode_high( text )
657
+ ## mb_encode_numericentity($text, $cmap, $charset);
658
+ end
659
+
660
+ def decode_high( text )
661
+ ## mb_decode_numericentity($text, $cmap, $charset);
662
+ end
663
+
664
+ def textile_popup_help( name, helpvar, windowW, windowH )
665
+ ' <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 />'
666
+ end
667
+
668
+ CMAP = [
669
+ 160, 255, 0, 0xffff,
670
+ 402, 402, 0, 0xffff,
671
+ 913, 929, 0, 0xffff,
672
+ 931, 937, 0, 0xffff,
673
+ 945, 969, 0, 0xffff,
674
+ 977, 978, 0, 0xffff,
675
+ 982, 982, 0, 0xffff,
676
+ 8226, 8226, 0, 0xffff,
677
+ 8230, 8230, 0, 0xffff,
678
+ 8242, 8243, 0, 0xffff,
679
+ 8254, 8254, 0, 0xffff,
680
+ 8260, 8260, 0, 0xffff,
681
+ 8465, 8465, 0, 0xffff,
682
+ 8472, 8472, 0, 0xffff,
683
+ 8476, 8476, 0, 0xffff,
684
+ 8482, 8482, 0, 0xffff,
685
+ 8501, 8501, 0, 0xffff,
686
+ 8592, 8596, 0, 0xffff,
687
+ 8629, 8629, 0, 0xffff,
688
+ 8656, 8660, 0, 0xffff,
689
+ 8704, 8704, 0, 0xffff,
690
+ 8706, 8707, 0, 0xffff,
691
+ 8709, 8709, 0, 0xffff,
692
+ 8711, 8713, 0, 0xffff,
693
+ 8715, 8715, 0, 0xffff,
694
+ 8719, 8719, 0, 0xffff,
695
+ 8721, 8722, 0, 0xffff,
696
+ 8727, 8727, 0, 0xffff,
697
+ 8730, 8730, 0, 0xffff,
698
+ 8733, 8734, 0, 0xffff,
699
+ 8736, 8736, 0, 0xffff,
700
+ 8743, 8747, 0, 0xffff,
701
+ 8756, 8756, 0, 0xffff,
702
+ 8764, 8764, 0, 0xffff,
703
+ 8773, 8773, 0, 0xffff,
704
+ 8776, 8776, 0, 0xffff,
705
+ 8800, 8801, 0, 0xffff,
706
+ 8804, 8805, 0, 0xffff,
707
+ 8834, 8836, 0, 0xffff,
708
+ 8838, 8839, 0, 0xffff,
709
+ 8853, 8853, 0, 0xffff,
710
+ 8855, 8855, 0, 0xffff,
711
+ 8869, 8869, 0, 0xffff,
712
+ 8901, 8901, 0, 0xffff,
713
+ 8968, 8971, 0, 0xffff,
714
+ 9001, 9002, 0, 0xffff,
715
+ 9674, 9674, 0, 0xffff,
716
+ 9824, 9824, 0, 0xffff,
717
+ 9827, 9827, 0, 0xffff,
718
+ 9829, 9830, 0, 0xffff,
719
+ 338, 339, 0, 0xffff,
720
+ 352, 353, 0, 0xffff,
721
+ 376, 376, 0, 0xffff,
722
+ 710, 710, 0, 0xffff,
723
+ 732, 732, 0, 0xffff,
724
+ 8194, 8195, 0, 0xffff,
725
+ 8201, 8201, 0, 0xffff,
726
+ 8204, 8207, 0, 0xffff,
727
+ 8211, 8212, 0, 0xffff,
728
+ 8216, 8218, 0, 0xffff,
729
+ 8218, 8218, 0, 0xffff,
730
+ 8220, 8222, 0, 0xffff,
731
+ 8224, 8225, 0, 0xffff,
732
+ 8240, 8240, 0, 0xffff,
733
+ 8249, 8250, 0, 0xffff,
734
+ 8364, 8364, 0, 0xffff
735
+ ]
736
+ end