instiki 0.9.2 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
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