instiki 0.10.0 → 0.10.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. data/CHANGELOG +174 -165
  2. data/README +68 -68
  3. data/app/controllers/admin_controller.rb +94 -94
  4. data/app/controllers/application.rb +135 -131
  5. data/app/controllers/file_controller.rb +129 -129
  6. data/app/controllers/wiki_controller.rb +354 -354
  7. data/app/helpers/application_helper.rb +68 -68
  8. data/app/models/author.rb +3 -3
  9. data/app/models/chunks/category.rb +33 -33
  10. data/app/models/chunks/chunk.rb +86 -86
  11. data/app/models/chunks/engines.rb +61 -54
  12. data/app/models/chunks/include.rb +41 -41
  13. data/app/models/chunks/literal.rb +31 -31
  14. data/app/models/chunks/nowiki.rb +28 -28
  15. data/app/models/chunks/test.rb +18 -18
  16. data/app/models/chunks/uri.rb +182 -182
  17. data/app/models/chunks/wiki.rb +141 -141
  18. data/app/models/file_yard.rb +58 -58
  19. data/app/models/page.rb +112 -112
  20. data/app/models/page_lock.rb +22 -22
  21. data/app/models/page_set.rb +89 -89
  22. data/app/models/revision.rb +123 -123
  23. data/app/models/web.rb +182 -176
  24. data/app/models/wiki_content.rb +207 -207
  25. data/app/models/wiki_service.rb +233 -233
  26. data/app/models/wiki_words.rb +23 -23
  27. data/app/views/admin/create_system.rhtml +83 -83
  28. data/app/views/admin/create_web.rhtml +69 -69
  29. data/app/views/admin/edit_web.rhtml +137 -136
  30. data/app/views/file/file.rhtml +18 -18
  31. data/app/views/file/import.rhtml +22 -22
  32. data/app/views/layouts/default.rhtml +86 -85
  33. data/app/views/markdown_help.rhtml +12 -12
  34. data/app/views/mixed_help.rhtml +6 -6
  35. data/app/views/navigation.rhtml +30 -30
  36. data/app/views/rdoc_help.rhtml +12 -12
  37. data/app/views/textile_help.rhtml +24 -24
  38. data/app/views/wiki/authors.rhtml +11 -11
  39. data/app/views/wiki/edit.rhtml +39 -39
  40. data/app/views/wiki/export.rhtml +12 -12
  41. data/app/views/wiki/feeds.rhtml +14 -14
  42. data/app/views/wiki/list.rhtml +64 -64
  43. data/app/views/wiki/locked.rhtml +23 -23
  44. data/app/views/wiki/login.rhtml +14 -14
  45. data/app/views/wiki/new.rhtml +31 -31
  46. data/app/views/wiki/page.rhtml +115 -115
  47. data/app/views/wiki/print.rhtml +14 -14
  48. data/app/views/wiki/published.rhtml +9 -9
  49. data/app/views/wiki/recently_revised.rhtml +26 -26
  50. data/app/views/wiki/revision.rhtml +103 -103
  51. data/app/views/wiki/rollback.rhtml +36 -36
  52. data/app/views/wiki/rss_feed.rhtml +22 -22
  53. data/app/views/wiki/search.rhtml +38 -38
  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 -18
  57. data/app/views/wiki_words_help.rhtml +9 -9
  58. data/config/environment.rb +82 -82
  59. data/config/environments/development.rb +5 -5
  60. data/config/environments/production.rb +4 -4
  61. data/config/environments/test.rb +17 -17
  62. data/config/routes.rb +18 -18
  63. data/lib/active_record_stub.rb +31 -31
  64. data/lib/bluecloth_tweaked.rb +1127 -0
  65. data/lib/diff.rb +444 -444
  66. data/lib/instiki_errors.rb +14 -14
  67. data/lib/rdocsupport.rb +151 -151
  68. data/lib/redcloth_for_tex.rb +736 -736
  69. data/natives/osx/desktop_launcher/AppDelegate.h +18 -18
  70. data/natives/osx/desktop_launcher/AppDelegate.mm +109 -109
  71. data/natives/osx/desktop_launcher/Credits.html +15 -15
  72. data/natives/osx/desktop_launcher/English.lproj/MainMenu.nib/classes.nib +12 -12
  73. data/natives/osx/desktop_launcher/English.lproj/MainMenu.nib/info.nib +24 -24
  74. data/natives/osx/desktop_launcher/Info.plist +12 -12
  75. data/natives/osx/desktop_launcher/Instiki.xcode/project.pbxproj +592 -592
  76. data/natives/osx/desktop_launcher/Instiki_Prefix.pch +7 -7
  77. data/natives/osx/desktop_launcher/MakeDMG.sh +9 -9
  78. data/natives/osx/desktop_launcher/main.mm +14 -14
  79. data/natives/osx/desktop_launcher/version.plist +16 -16
  80. data/public/404.html +5 -5
  81. data/public/500.html +5 -5
  82. data/public/dispatch.rb +9 -9
  83. data/public/javascripts/edit_web.js +52 -52
  84. data/public/javascripts/prototype.js +336 -336
  85. data/public/stylesheets/instiki.css +222 -222
  86. data/script/breakpointer +4 -4
  87. data/script/server +93 -93
  88. metadata +4 -3
@@ -1,41 +1,41 @@
1
- require 'chunks/wiki'
2
-
3
- # Includes the contents of another page for rendering.
4
- # The include command looks like this: "[[!include PageName]]".
5
- # It is a WikiReference since it refers to another page (PageName)
6
- # and the wiki content using this command must be notified
7
- # of changes to that page.
8
- # If the included page could not be found, a warning is displayed.
9
-
10
- class Include < WikiChunk::WikiReference
11
-
12
- INCLUDE_PATTERN = /\[\[!include(.*)\]\]\s*/i
13
- def self.pattern() INCLUDE_PATTERN end
14
-
15
-
16
- def initialize(match_data, content)
17
- super
18
- @page_name = match_data[1].strip
19
- @unmask_text = get_unmask_text_avoiding_recursion_loops
20
- end
21
-
22
- private
23
-
24
- def get_unmask_text_avoiding_recursion_loops
25
- if refpage then
26
- if refpage.wiki_includes.include?(@content.page_name)
27
- # this will break the recursion
28
- @content.delete_chunk(self)
29
- refpage.clear_display_cache
30
- return "<em>Recursive include detected; #{@page_name} --> #{@content.page_name} " +
31
- "--> #{@page_name}</em>\n"
32
- else
33
- @content.merge_chunks(refpage.display_content)
34
- return refpage.display_content.pre_rendered
35
- end
36
- else
37
- return "<em>Could not include #{@page_name}</em>\n"
38
- end
39
- end
40
-
41
- end
1
+ require 'chunks/wiki'
2
+
3
+ # Includes the contents of another page for rendering.
4
+ # The include command looks like this: "[[!include PageName]]".
5
+ # It is a WikiReference since it refers to another page (PageName)
6
+ # and the wiki content using this command must be notified
7
+ # of changes to that page.
8
+ # If the included page could not be found, a warning is displayed.
9
+
10
+ class Include < WikiChunk::WikiReference
11
+
12
+ INCLUDE_PATTERN = /\[\[!include(.*)\]\]\s*/i
13
+ def self.pattern() INCLUDE_PATTERN end
14
+
15
+
16
+ def initialize(match_data, content)
17
+ super
18
+ @page_name = match_data[1].strip
19
+ @unmask_text = get_unmask_text_avoiding_recursion_loops
20
+ end
21
+
22
+ private
23
+
24
+ def get_unmask_text_avoiding_recursion_loops
25
+ if refpage then
26
+ if refpage.wiki_includes.include?(@content.page_name)
27
+ # this will break the recursion
28
+ @content.delete_chunk(self)
29
+ refpage.clear_display_cache
30
+ return "<em>Recursive include detected; #{@page_name} --> #{@content.page_name} " +
31
+ "--> #{@page_name}</em>\n"
32
+ else
33
+ @content.merge_chunks(refpage.display_content)
34
+ return refpage.display_content.pre_rendered
35
+ end
36
+ else
37
+ return "<em>Could not include #{@page_name}</em>\n"
38
+ end
39
+ end
40
+
41
+ end
@@ -1,31 +1,31 @@
1
- require 'chunks/chunk'
2
-
3
- # These are basic chunks that have a pattern and can be protected.
4
- # They are used by rendering process to prevent wiki rendering
5
- # occuring within literal areas such as <code> and <pre> blocks
6
- # and within HTML tags.
7
- module Literal
8
-
9
- class AbstractLiteral < Chunk::Abstract
10
-
11
- def initialize(match_data, content)
12
- super
13
- @unmask_text = @text
14
- end
15
-
16
- end
17
-
18
- # A literal chunk that protects 'code' and 'pre' tags from wiki rendering.
19
- class Pre < AbstractLiteral
20
- PRE_BLOCKS = "a|pre|code"
21
- PRE_PATTERN = Regexp.new('<('+PRE_BLOCKS+')\b[^>]*?>.*?</\1>', Regexp::MULTILINE)
22
- def self.pattern() PRE_PATTERN end
23
- end
24
-
25
- # A literal chunk that protects HTML tags from wiki rendering.
26
- class Tags < AbstractLiteral
27
- TAGS = "a|img|em|strong|div|span|table|td|th|ul|ol|li|dl|dt|dd"
28
- TAGS_PATTERN = Regexp.new('<(?:'+TAGS+')[^>]*?>', Regexp::MULTILINE)
29
- def self.pattern() TAGS_PATTERN end
30
- end
31
- end
1
+ require 'chunks/chunk'
2
+
3
+ # These are basic chunks that have a pattern and can be protected.
4
+ # They are used by rendering process to prevent wiki rendering
5
+ # occuring within literal areas such as <code> and <pre> blocks
6
+ # and within HTML tags.
7
+ module Literal
8
+
9
+ class AbstractLiteral < Chunk::Abstract
10
+
11
+ def initialize(match_data, content)
12
+ super
13
+ @unmask_text = @text
14
+ end
15
+
16
+ end
17
+
18
+ # A literal chunk that protects 'code' and 'pre' tags from wiki rendering.
19
+ class Pre < AbstractLiteral
20
+ PRE_BLOCKS = "a|pre|code"
21
+ PRE_PATTERN = Regexp.new('<('+PRE_BLOCKS+')\b[^>]*?>.*?</\1>', Regexp::MULTILINE)
22
+ def self.pattern() PRE_PATTERN end
23
+ end
24
+
25
+ # A literal chunk that protects HTML tags from wiki rendering.
26
+ class Tags < AbstractLiteral
27
+ TAGS = "a|img|em|strong|div|span|table|td|th|ul|ol|li|dl|dt|dd"
28
+ TAGS_PATTERN = Regexp.new('<(?:'+TAGS+')[^>]*?>', Regexp::MULTILINE)
29
+ def self.pattern() TAGS_PATTERN end
30
+ end
31
+ end
@@ -1,28 +1,28 @@
1
- require 'chunks/chunk'
2
-
3
- # This chunks allows certain parts of a wiki page to be hidden from the
4
- # rest of the rendering pipeline. It should be run at the beginning
5
- # of the pipeline in `wiki_content.rb`.
6
- #
7
- # An example use of this chunk is to markup double brackets or
8
- # auto URI links:
9
- # <nowiki>Here are [[double brackets]] and a URI: www.uri.org</nowiki>
10
- #
11
- # The contents of the chunks will not be processed by any other chunk
12
- # so the `www.uri.org` and the double brackets will appear verbatim.
13
- #
14
- # Author: Mark Reid <mark at threewordslong dot com>
15
- # Created: 8th June 2004
16
- class NoWiki < Chunk::Abstract
17
-
18
- NOWIKI_PATTERN = Regexp.new('<nowiki>(.*?)</nowiki>')
19
- def self.pattern() NOWIKI_PATTERN end
20
-
21
- attr_reader :plain_text
22
-
23
- def initialize(match_data, content)
24
- super
25
- @plain_text = @unmask_text = match_data[1]
26
- end
27
-
28
- end
1
+ require 'chunks/chunk'
2
+
3
+ # This chunks allows certain parts of a wiki page to be hidden from the
4
+ # rest of the rendering pipeline. It should be run at the beginning
5
+ # of the pipeline in `wiki_content.rb`.
6
+ #
7
+ # An example use of this chunk is to markup double brackets or
8
+ # auto URI links:
9
+ # <nowiki>Here are [[double brackets]] and a URI: www.uri.org</nowiki>
10
+ #
11
+ # The contents of the chunks will not be processed by any other chunk
12
+ # so the `www.uri.org` and the double brackets will appear verbatim.
13
+ #
14
+ # Author: Mark Reid <mark at threewordslong dot com>
15
+ # Created: 8th June 2004
16
+ class NoWiki < Chunk::Abstract
17
+
18
+ NOWIKI_PATTERN = Regexp.new('<nowiki>(.*?)</nowiki>')
19
+ def self.pattern() NOWIKI_PATTERN end
20
+
21
+ attr_reader :plain_text
22
+
23
+ def initialize(match_data, content)
24
+ super
25
+ @plain_text = @unmask_text = match_data[1]
26
+ end
27
+
28
+ end
@@ -1,18 +1,18 @@
1
- require 'test/unit'
2
-
3
- class ChunkTest < Test::Unit::TestCase
4
-
5
- # Asserts a number of tests for the given type and text.
6
- def match(type, test_text, expected)
7
- pattern = type.pattern
8
- assert_match(pattern, test_text)
9
- pattern =~ test_text # Previous assertion guarantees match
10
- chunk = type.new($~)
11
-
12
- # Test if requested parts are correct.
13
- for method_sym, value in expected do
14
- assert_respond_to(chunk, method_sym)
15
- assert_equal(value, chunk.method(method_sym).call, "Checking value of '#{method_sym}'")
16
- end
17
- end
18
- end
1
+ require 'test/unit'
2
+
3
+ class ChunkTest < Test::Unit::TestCase
4
+
5
+ # Asserts a number of tests for the given type and text.
6
+ def match(type, test_text, expected)
7
+ pattern = type.pattern
8
+ assert_match(pattern, test_text)
9
+ pattern =~ test_text # Previous assertion guarantees match
10
+ chunk = type.new($~)
11
+
12
+ # Test if requested parts are correct.
13
+ for method_sym, value in expected do
14
+ assert_respond_to(chunk, method_sym)
15
+ assert_equal(value, chunk.method(method_sym).call, "Checking value of '#{method_sym}'")
16
+ end
17
+ end
18
+ end
@@ -1,182 +1,182 @@
1
- require 'chunks/chunk'
2
-
3
- # This wiki chunk matches arbitrary URIs, using patterns from the Ruby URI modules.
4
- # It parses out a variety of fields that could be used by renderers to format
5
- # the links in various ways (shortening domain names, hiding email addresses)
6
- # It matches email addresses and host.com.au domains without schemes (http://)
7
- # but adds these on as required.
8
- #
9
- # The heuristic used to match a URI is designed to err on the side of caution.
10
- # That is, it is more likely to not autolink a URI than it is to accidently
11
- # autolink something that is not a URI. The reason behind this is it is easier
12
- # to force a URI link by prefixing 'http://' to it than it is to escape and
13
- # incorrectly marked up non-URI.
14
- #
15
- # I'm using a part of the [ISO 3166-1 Standard][iso3166] for country name suffixes.
16
- # The generic names are from www.bnoack.com/data/countrycode2.html)
17
- # [iso3166]: http://geotags.com/iso3166/
18
-
19
- class URIChunk < Chunk::Abstract
20
- include URI::REGEXP::PATTERN
21
-
22
- # this condition is to get rid of pesky warnings in tests
23
- unless defined? URIChunk::INTERNET_URI_REGEXP
24
-
25
- GENERIC = 'aero|biz|com|coop|edu|gov|info|int|mil|museum|name|net|org'
26
-
27
- COUNTRY = 'ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|az|ba|bb|bd|be|' +
28
- 'bf|bg|bh|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cf|cd|cg|ch|ci|ck|cl|' +
29
- 'cm|cn|co|cr|cs|cu|cv|cx|cy|cz|de|dj|dk|dm|do|dz|ec|ee|eg|eh|er|es|et|fi|' +
30
- 'fj|fk|fm|fo|fr|fx|ga|gb|gd|ge|gf|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|' +
31
- 'hk|hm|hn|hr|ht|hu|id|ie|il|in|io|iq|ir|is|it|jm|jo|jp|ke|kg|kh|ki|km|kn|' +
32
- 'kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|mg|mh|mk|ml|mm|' +
33
- 'mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|mz|na|nc|ne|nf|ng|ni|nl|no|np|nr|nt|' +
34
- 'nu|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|pt|pw|py|qa|re|ro|ru|rw|sa|sb|sc|' +
35
- 'sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|st|su|sv|sy|sz|tc|td|tf|tg|th|tj|tk|' +
36
- 'tm|tn|to|tp|tr|tt|tv|tw|tz|ua|ug|uk|um|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|' +
37
- 'ws|ye|yt|yu|za|zm|zr|zw'
38
- # These are needed otherwise HOST will match almost anything
39
- TLDS = "(?:#{GENERIC}|#{COUNTRY})"
40
-
41
- # Redefine USERINFO so that it must have non-zero length
42
- USERINFO = "(?:[#{UNRESERVED};:&=+$,]|#{ESCAPED})+"
43
-
44
- # unreserved_no_ending = alphanum | mark, but URI_ENDING [)!] excluded
45
- UNRESERVED_NO_ENDING = "-_.~*'(#{ALNUM}"
46
-
47
- # this ensures that query or fragment do not end with URI_ENDING
48
- # and enable us to use a much simpler self.pattern Regexp
49
-
50
- # uric_no_ending = reserved | unreserved_no_ending | escaped
51
- URIC_NO_ENDING = "(?:[#{UNRESERVED_NO_ENDING}#{RESERVED}]|#{ESCAPED})"
52
- # query = *uric
53
- QUERY = "#{URIC_NO_ENDING}*"
54
- # fragment = *uric
55
- FRAGMENT = "#{URIC_NO_ENDING}*"
56
-
57
- # DOMLABEL is defined in the ruby uri library, TLDS is defined above
58
- INTERNET_HOSTNAME = "(?:#{DOMLABEL}\\.)+#{TLDS}"
59
-
60
- # Correct a typo bug in ruby 1.8.x lib/uri/common.rb
61
- PORT = '\\d*'
62
-
63
- INTERNET_URI =
64
- "(?:(#{SCHEME}):/{0,2})?" + # Optional scheme: (\1)
65
- "(?:(#{USERINFO})@)?" + # Optional userinfo@ (\2)
66
- "(#{INTERNET_HOSTNAME})" + # Mandatory hostname (\3)
67
- "(?::(#{PORT}))?" + # Optional :port (\4)
68
- "(#{ABS_PATH})?" + # Optional absolute path (\5)
69
- "(?:\\?(#{QUERY}))?" + # Optional ?query (\6)
70
- "(?:\\#(#{FRAGMENT}))?" + # Optional #fragment (\7)
71
- '(?=\.?(?:\s|\)|\z))' # ends only with optional dot + space or ")"
72
- # or end of the string
73
-
74
- SUSPICIOUS_PRECEDING_CHARACTER = '(!|\"\:|\"|\\\')?' # any of !, ":, ", '
75
-
76
- INTERNET_URI_REGEXP =
77
- Regexp.new(SUSPICIOUS_PRECEDING_CHARACTER + INTERNET_URI, Regexp::EXTENDED, 'N')
78
-
79
- end
80
-
81
- def URIChunk.pattern
82
- INTERNET_URI_REGEXP
83
- end
84
-
85
- attr_reader :user, :host, :port, :path, :query, :fragment, :link_text
86
-
87
- def self.apply_to(content)
88
- content.gsub!( self.pattern ) do |matched_text|
89
- chunk = self.new($~, content)
90
- if chunk.avoid_autolinking?
91
- # do not substitute nor register the chunk
92
- matched_text
93
- else
94
- content.add_chunk(chunk)
95
- chunk.mask
96
- end
97
- end
98
- end
99
-
100
- def initialize(match_data, content)
101
- super
102
- @link_text = match_data[0]
103
- @suspicious_preceding_character = match_data[1]
104
- @original_scheme, @user, @host, @port, @path, @query, @fragment = match_data[2..-1]
105
- treat_trailing_character
106
- @unmask_text = "<a href=\"#{uri}\">#{link_text}</a>"
107
- end
108
-
109
- def avoid_autolinking?
110
- not @suspicious_preceding_character.nil?
111
- end
112
-
113
- def treat_trailing_character
114
- # If the last character matched by URI pattern is in ! or ), this may be part of the markup,
115
- # not a URL. We should handle it as such. It is possible to do it by a regexp, but
116
- # much easier to do programmatically
117
- last_char = @link_text[-1..-1]
118
- if last_char == ')' or last_char == '!'
119
- @trailing_punctuation = last_char
120
- @link_text.chop!
121
- [@original_scheme, @user, @host, @port, @path, @query, @fragment].compact.last.chop!
122
- else
123
- @trailing_punctuation = nil
124
- end
125
- end
126
-
127
- def scheme
128
- @original_scheme or (@user ? 'mailto' : 'http')
129
- end
130
-
131
- def scheme_delimiter
132
- scheme == 'mailto' ? ':' : '://'
133
- end
134
-
135
- def user_delimiter
136
- '@' unless @user.nil?
137
- end
138
-
139
- def port_delimiter
140
- ':' unless @port.nil?
141
- end
142
-
143
- def query_delimiter
144
- '?' unless @query.nil?
145
- end
146
-
147
- def uri
148
- [scheme, scheme_delimiter, user, user_delimiter, host, port_delimiter, port, path,
149
- query_delimiter, query].compact.join
150
- end
151
-
152
- end
153
-
154
- # uri with mandatory scheme but less restrictive hostname, like
155
- # http://localhost:2500/blah.html
156
- class LocalURIChunk < URIChunk
157
-
158
- unless defined? LocalURIChunk::LOCAL_URI_REGEXP
159
- # hostname can be just a simple word like 'localhost'
160
- ANY_HOSTNAME = "(?:#{DOMLABEL}\\.)*#{TOPLABEL}\\.?"
161
-
162
- # The basic URI expression as a string
163
- # Scheme and hostname are mandatory
164
- LOCAL_URI =
165
- "(?:(#{SCHEME})://)+" + # Mandatory scheme:// (\1)
166
- "(?:(#{USERINFO})@)?" + # Optional userinfo@ (\2)
167
- "(#{ANY_HOSTNAME})" + # Mandatory hostname (\3)
168
- "(?::(#{PORT}))?" + # Optional :port (\4)
169
- "(#{ABS_PATH})?" + # Optional absolute path (\5)
170
- "(?:\\?(#{QUERY}))?" + # Optional ?query (\6)
171
- "(?:\\#(#{FRAGMENT}))?" + # Optional #fragment (\7)
172
- '(?=\.?(?:\s|\)|\z))' # ends only with optional dot + space or ")"
173
- # or end of the string
174
-
175
- LOCAL_URI_REGEXP = Regexp.new(SUSPICIOUS_PRECEDING_CHARACTER + LOCAL_URI, Regexp::EXTENDED, 'N')
176
- end
177
-
178
- def LocalURIChunk.pattern
179
- LOCAL_URI_REGEXP
180
- end
181
-
182
- end
1
+ require 'chunks/chunk'
2
+
3
+ # This wiki chunk matches arbitrary URIs, using patterns from the Ruby URI modules.
4
+ # It parses out a variety of fields that could be used by renderers to format
5
+ # the links in various ways (shortening domain names, hiding email addresses)
6
+ # It matches email addresses and host.com.au domains without schemes (http://)
7
+ # but adds these on as required.
8
+ #
9
+ # The heuristic used to match a URI is designed to err on the side of caution.
10
+ # That is, it is more likely to not autolink a URI than it is to accidently
11
+ # autolink something that is not a URI. The reason behind this is it is easier
12
+ # to force a URI link by prefixing 'http://' to it than it is to escape and
13
+ # incorrectly marked up non-URI.
14
+ #
15
+ # I'm using a part of the [ISO 3166-1 Standard][iso3166] for country name suffixes.
16
+ # The generic names are from www.bnoack.com/data/countrycode2.html)
17
+ # [iso3166]: http://geotags.com/iso3166/
18
+
19
+ class URIChunk < Chunk::Abstract
20
+ include URI::REGEXP::PATTERN
21
+
22
+ # this condition is to get rid of pesky warnings in tests
23
+ unless defined? URIChunk::INTERNET_URI_REGEXP
24
+
25
+ GENERIC = 'aero|biz|com|coop|edu|gov|info|int|mil|museum|name|net|org'
26
+
27
+ COUNTRY = 'ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|az|ba|bb|bd|be|' +
28
+ 'bf|bg|bh|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cf|cd|cg|ch|ci|ck|cl|' +
29
+ 'cm|cn|co|cr|cs|cu|cv|cx|cy|cz|de|dj|dk|dm|do|dz|ec|ee|eg|eh|er|es|et|fi|' +
30
+ 'fj|fk|fm|fo|fr|fx|ga|gb|gd|ge|gf|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|' +
31
+ 'hk|hm|hn|hr|ht|hu|id|ie|il|in|io|iq|ir|is|it|jm|jo|jp|ke|kg|kh|ki|km|kn|' +
32
+ 'kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|mg|mh|mk|ml|mm|' +
33
+ 'mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|mz|na|nc|ne|nf|ng|ni|nl|no|np|nr|nt|' +
34
+ 'nu|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|pt|pw|py|qa|re|ro|ru|rw|sa|sb|sc|' +
35
+ 'sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|st|su|sv|sy|sz|tc|td|tf|tg|th|tj|tk|' +
36
+ 'tm|tn|to|tp|tr|tt|tv|tw|tz|ua|ug|uk|um|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|' +
37
+ 'ws|ye|yt|yu|za|zm|zr|zw'
38
+ # These are needed otherwise HOST will match almost anything
39
+ TLDS = "(?:#{GENERIC}|#{COUNTRY})"
40
+
41
+ # Redefine USERINFO so that it must have non-zero length
42
+ USERINFO = "(?:[#{UNRESERVED};:&=+$,]|#{ESCAPED})+"
43
+
44
+ # unreserved_no_ending = alphanum | mark, but URI_ENDING [)!] excluded
45
+ UNRESERVED_NO_ENDING = "-_.~*'(#{ALNUM}"
46
+
47
+ # this ensures that query or fragment do not end with URI_ENDING
48
+ # and enable us to use a much simpler self.pattern Regexp
49
+
50
+ # uric_no_ending = reserved | unreserved_no_ending | escaped
51
+ URIC_NO_ENDING = "(?:[#{UNRESERVED_NO_ENDING}#{RESERVED}]|#{ESCAPED})"
52
+ # query = *uric
53
+ QUERY = "#{URIC_NO_ENDING}*"
54
+ # fragment = *uric
55
+ FRAGMENT = "#{URIC_NO_ENDING}*"
56
+
57
+ # DOMLABEL is defined in the ruby uri library, TLDS is defined above
58
+ INTERNET_HOSTNAME = "(?:#{DOMLABEL}\\.)+#{TLDS}"
59
+
60
+ # Correct a typo bug in ruby 1.8.x lib/uri/common.rb
61
+ PORT = '\\d*'
62
+
63
+ INTERNET_URI =
64
+ "(?:(#{SCHEME}):/{0,2})?" + # Optional scheme: (\1)
65
+ "(?:(#{USERINFO})@)?" + # Optional userinfo@ (\2)
66
+ "(#{INTERNET_HOSTNAME})" + # Mandatory hostname (\3)
67
+ "(?::(#{PORT}))?" + # Optional :port (\4)
68
+ "(#{ABS_PATH})?" + # Optional absolute path (\5)
69
+ "(?:\\?(#{QUERY}))?" + # Optional ?query (\6)
70
+ "(?:\\#(#{FRAGMENT}))?" + # Optional #fragment (\7)
71
+ '(?=\.?(?:\s|\)|\z))' # ends only with optional dot + space or ")"
72
+ # or end of the string
73
+
74
+ SUSPICIOUS_PRECEDING_CHARACTER = '(!|\"\:|\"|\\\')?' # any of !, ":, ", '
75
+
76
+ INTERNET_URI_REGEXP =
77
+ Regexp.new(SUSPICIOUS_PRECEDING_CHARACTER + INTERNET_URI, Regexp::EXTENDED, 'N')
78
+
79
+ end
80
+
81
+ def URIChunk.pattern
82
+ INTERNET_URI_REGEXP
83
+ end
84
+
85
+ attr_reader :user, :host, :port, :path, :query, :fragment, :link_text
86
+
87
+ def self.apply_to(content)
88
+ content.gsub!( self.pattern ) do |matched_text|
89
+ chunk = self.new($~, content)
90
+ if chunk.avoid_autolinking?
91
+ # do not substitute nor register the chunk
92
+ matched_text
93
+ else
94
+ content.add_chunk(chunk)
95
+ chunk.mask
96
+ end
97
+ end
98
+ end
99
+
100
+ def initialize(match_data, content)
101
+ super
102
+ @link_text = match_data[0]
103
+ @suspicious_preceding_character = match_data[1]
104
+ @original_scheme, @user, @host, @port, @path, @query, @fragment = match_data[2..-1]
105
+ treat_trailing_character
106
+ @unmask_text = "<a href=\"#{uri}\">#{link_text}</a>"
107
+ end
108
+
109
+ def avoid_autolinking?
110
+ not @suspicious_preceding_character.nil?
111
+ end
112
+
113
+ def treat_trailing_character
114
+ # If the last character matched by URI pattern is in ! or ), this may be part of the markup,
115
+ # not a URL. We should handle it as such. It is possible to do it by a regexp, but
116
+ # much easier to do programmatically
117
+ last_char = @link_text[-1..-1]
118
+ if last_char == ')' or last_char == '!'
119
+ @trailing_punctuation = last_char
120
+ @link_text.chop!
121
+ [@original_scheme, @user, @host, @port, @path, @query, @fragment].compact.last.chop!
122
+ else
123
+ @trailing_punctuation = nil
124
+ end
125
+ end
126
+
127
+ def scheme
128
+ @original_scheme or (@user ? 'mailto' : 'http')
129
+ end
130
+
131
+ def scheme_delimiter
132
+ scheme == 'mailto' ? ':' : '://'
133
+ end
134
+
135
+ def user_delimiter
136
+ '@' unless @user.nil?
137
+ end
138
+
139
+ def port_delimiter
140
+ ':' unless @port.nil?
141
+ end
142
+
143
+ def query_delimiter
144
+ '?' unless @query.nil?
145
+ end
146
+
147
+ def uri
148
+ [scheme, scheme_delimiter, user, user_delimiter, host, port_delimiter, port, path,
149
+ query_delimiter, query].compact.join
150
+ end
151
+
152
+ end
153
+
154
+ # uri with mandatory scheme but less restrictive hostname, like
155
+ # http://localhost:2500/blah.html
156
+ class LocalURIChunk < URIChunk
157
+
158
+ unless defined? LocalURIChunk::LOCAL_URI_REGEXP
159
+ # hostname can be just a simple word like 'localhost'
160
+ ANY_HOSTNAME = "(?:#{DOMLABEL}\\.)*#{TOPLABEL}\\.?"
161
+
162
+ # The basic URI expression as a string
163
+ # Scheme and hostname are mandatory
164
+ LOCAL_URI =
165
+ "(?:(#{SCHEME})://)+" + # Mandatory scheme:// (\1)
166
+ "(?:(#{USERINFO})@)?" + # Optional userinfo@ (\2)
167
+ "(#{ANY_HOSTNAME})" + # Mandatory hostname (\3)
168
+ "(?::(#{PORT}))?" + # Optional :port (\4)
169
+ "(#{ABS_PATH})?" + # Optional absolute path (\5)
170
+ "(?:\\?(#{QUERY}))?" + # Optional ?query (\6)
171
+ "(?:\\#(#{FRAGMENT}))?" + # Optional #fragment (\7)
172
+ '(?=\.?(?:\s|\)|\z))' # ends only with optional dot + space or ")"
173
+ # or end of the string
174
+
175
+ LOCAL_URI_REGEXP = Regexp.new(SUSPICIOUS_PRECEDING_CHARACTER + LOCAL_URI, Regexp::EXTENDED, 'N')
176
+ end
177
+
178
+ def LocalURIChunk.pattern
179
+ LOCAL_URI_REGEXP
180
+ end
181
+
182
+ end