instiki 0.10.0 → 0.10.1

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 (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