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
@@ -1,33 +1,68 @@
1
- module ViewHelper
2
- # Accepts a container (hash, array, enumerable, your type) and returns a string of option tags. Given a container
3
- # where the elements respond to first and last (such as a two-element array), the "lasts" serve as option values and
4
- # the "firsts" as option text. Hashes are turned into this form automatically, so the keys become "firsts" and values
5
- # become lasts. If +selected+ is specified, the matching "last" or element will get the selected option-tag.
6
- #
7
- # Examples (call, result):
8
- # html_options([["Dollar", "$"], ["Kroner", "DKK"]])
9
- # <option value="$">Dollar</option>\n<option value="DKK">Kroner</option>
10
- #
11
- # html_options([ "VISA", "Mastercard" ], "Mastercard")
12
- # <option>VISA</option>\n<option selected>Mastercard</option>
13
- #
14
- # html_options({ "Basic" => "$20", "Plus" => "$40" }, "$40")
15
- # <option value="$20">Basic</option>\n<option value="$40" selected>Plus</option>
16
- def html_options(container, selected = nil)
17
- container = container.to_a if Hash === container
18
-
19
- html_options = container.inject([]) do |options, element|
20
- if element.respond_to?(:first) && element.respond_to?(:last)
21
- if element.last != selected
22
- options << "<option value=\"#{element.last}\">#{element.first}</option>"
23
- else
24
- options << "<option value=\"#{element.last}\" selected>#{element.first}</option>"
25
- end
26
- else
27
- options << ((element != selected) ? "<option>#{element}</option>" : "<option selected>#{element}</option>")
28
- end
29
- end
30
-
31
- html_options.join("\n")
32
- end
33
- end
1
+ # The methods added to this helper will be available to all templates in the application.
2
+ module ApplicationHelper
3
+
4
+ # Accepts a container (hash, array, enumerable, your type) and returns a string of option tags. Given a container
5
+ # where the elements respond to first and last (such as a two-element array), the "lasts" serve as option values and
6
+ # the "firsts" as option text. Hashes are turned into this form automatically, so the keys become "firsts" and values
7
+ # become lasts. If +selected+ is specified, the matching "last" or element will get the selected option-tag.
8
+ #
9
+ # Examples (call, result):
10
+ # html_options([["Dollar", "$"], ["Kroner", "DKK"]])
11
+ # <option value="$">Dollar</option>\n<option value="DKK">Kroner</option>
12
+ #
13
+ # html_options([ "VISA", "Mastercard" ], "Mastercard")
14
+ # <option>VISA</option>\n<option selected>Mastercard</option>
15
+ #
16
+ # html_options({ "Basic" => "$20", "Plus" => "$40" }, "$40")
17
+ # <option value="$20">Basic</option>\n<option value="$40" selected>Plus</option>
18
+ def html_options(container, selected = nil)
19
+ container = container.to_a if Hash === container
20
+
21
+ html_options = container.inject([]) do |options, element|
22
+ if element.respond_to?(:first) && element.respond_to?(:last)
23
+ if element.last != selected
24
+ options << "<option value=\"#{element.last}\">#{element.first}</option>"
25
+ else
26
+ options << "<option value=\"#{element.last}\" selected>#{element.first}</option>"
27
+ end
28
+ else
29
+ options << ((element != selected) ? "<option>#{element}</option>" : "<option selected>#{element}</option>")
30
+ end
31
+ end
32
+
33
+ html_options.join("\n")
34
+ end
35
+
36
+ # Creates a hyperlink to a Wiki page, without checking if the page exists or not
37
+ def link_to_existing_page(page, text = nil, html_options = {})
38
+ link_to(
39
+ text || page.name,
40
+ {:web => @web.address, :action => 'show', :id => page.name, :only_path => true},
41
+ html_options)
42
+ end
43
+
44
+
45
+ # Creates a hyperlink to a Wiki page, or to a "new page" form if the page doesn't exist yet
46
+ def link_to_page(page_name, web = @web, text = nil, options = {})
47
+ raise 'Web not defined' if web.nil?
48
+ home_page_url = url_for :web => web.address, :action => 'show', :id => 'HomePage', :only_path => true
49
+ base_url = home_page_url.sub(%r-/show/HomePage/?$-, '')
50
+ web.make_link(page_name, text, options.merge(:base_url => base_url))
51
+ end
52
+
53
+ # Creates a menu of categories
54
+ def categories_menu
55
+ if @categories.empty?
56
+ ''
57
+ else
58
+ "<div id=\"categories\">\n" +
59
+ '<strong>Categories</strong>:' +
60
+ '[' + link_to_unless_current('Any', :web => @web.address, :action => @action_name) + "]\n" +
61
+ @categories.map { |c|
62
+ link_to_unless_current(c, :web => @web.address, :action => @action_name, :category => c)
63
+ }.join(', ') + "\n" +
64
+ '</div>'
65
+ end
66
+ end
67
+
68
+ end
@@ -1,4 +1,4 @@
1
- class Author < String
2
- attr_accessor :ip
3
- def initialize(name, ip) @ip = ip; super(name) end
1
+ class Author < String
2
+ attr_accessor :ip
3
+ def initialize(name, ip) @ip = ip; super(name) end
4
4
  end
@@ -1,31 +1,33 @@
1
- require 'chunks/chunk'
2
-
3
- # The category chunk looks for "category: news" on a line by
4
- # itself and parses the terms after the ':' as categories.
5
- # Other classes can search for Category chunks within
6
- # rendered content to find out what categories this page
7
- # should be in.
8
- #
9
- # Category lines can be hidden using ':category: news', for example
10
- class Category < Chunk::Abstract
11
- def self.pattern() return /^(:)?category\s*:(.*)$/i end
12
-
13
- attr_reader :hidden, :list
14
-
15
- def initialize(match_data)
16
- super(match_data)
17
- @hidden = match_data[1]
18
- @list = match_data[2].split(',').map { |c| c.strip }
19
- end
20
-
21
- # Mark this chunk's start and end points but allow the terms
22
- # after the ':' to be marked up.
23
- def mask(content) pre_mask + list.join(', ') + post_mask end
24
-
25
- # If the chunk is hidden, erase the mask and return this chunk
26
- # otherwise, surround it with a 'div' block.
27
- def unmask(content)
28
- replacement = ( hidden ? '' : '<div class="property">category:\1</div>' )
29
- self if content.sub!( Regexp.new( pre_mask+'(.*)?'+post_mask ), replacement )
30
- end
31
- end
1
+ require 'chunks/chunk'
2
+
3
+ # The category chunk looks for "category: news" on a line by
4
+ # itself and parses the terms after the ':' as categories.
5
+ # Other classes can search for Category chunks within
6
+ # rendered content to find out what categories this page
7
+ # should be in.
8
+ #
9
+ # Category lines can be hidden using ':category: news', for example
10
+ class Category < Chunk::Abstract
11
+ CATEGORY_PATTERN = /^(:)?category\s*:(.*)$/i
12
+ def self.pattern() CATEGORY_PATTERN end
13
+
14
+ attr_reader :hidden, :list
15
+
16
+ def initialize(match_data, content)
17
+ super(match_data, content)
18
+ @hidden = match_data[1]
19
+ @list = match_data[2].split(',').map { |c| c.strip }
20
+ @unmask_text = ''
21
+ if @hidden
22
+ @unmask_text = ''
23
+ else
24
+ category_urls = @list.map { |category| url(category) }.join(', ')
25
+ @unmask_text = '<div class="property"> category: ' + category_urls + '</div>'
26
+ end
27
+ end
28
+
29
+ # TODO move presentation of page metadata to controller/view
30
+ def url(category)
31
+ %{<a class="category_link" href="../list/?category=#{category}">#{category}</a>}
32
+ end
33
+ end
@@ -1,20 +1,86 @@
1
- require 'digest/md5'
2
- require 'uri/common'
3
-
4
- # A chunk is a pattern of text that can be protected
5
- # and interrogated by a renderer. Each Chunk class has a
6
- # +pattern+ that states what sort of text it matches.
7
- # Chunks are initalized by passing in the result of a
8
- # match by its pattern.
9
- module Chunk
10
- class Abstract
11
- attr_reader :text
12
-
13
- def initialize(match_data) @text = match_data[0] end
14
- def pre_mask() "chunk#{self.object_id}start " end
15
- def post_mask() " chunk#{self.object_id}end" end
16
- def mask(content) "chunk#{self.object_id}chunk" end
17
- def revert(content) content.sub!( Regexp.new(mask(content)), text ) end
18
- def unmask(content) self if revert(content) end
19
- end
20
- end
1
+ require 'uri/common'
2
+
3
+ # A chunk is a pattern of text that can be protected
4
+ # and interrogated by a renderer. Each Chunk class has a
5
+ # +pattern+ that states what sort of text it matches.
6
+ # Chunks are initalized by passing in the result of a
7
+ # match by its pattern.
8
+
9
+ module Chunk
10
+ class Abstract
11
+
12
+ # automatically construct the array of derivatives of Chunk::Abstract
13
+ @derivatives = []
14
+
15
+ class << self
16
+ attr_reader :derivatives
17
+ end
18
+
19
+ def self::inherited( klass )
20
+ Abstract::derivatives << klass
21
+ end
22
+
23
+ # the class name part of the mask strings
24
+ def self.mask_string
25
+ self.to_s.delete(':').downcase
26
+ end
27
+
28
+ # a regexp that matches all chunk_types masks
29
+ def Abstract::mask_re(chunk_types)
30
+ tmp = chunk_types.map{|klass| klass.mask_string}.join("|")
31
+ Regexp.new("chunk([0-9a-f]+n\\d+)(#{tmp})chunk")
32
+ end
33
+
34
+ attr_reader :text, :unmask_text, :unmask_mode
35
+
36
+ def initialize(match_data, content)
37
+ @text = match_data[0]
38
+ @content = content
39
+ @unmask_mode = :normal
40
+ end
41
+
42
+ # Find all the chunks of the given type in content
43
+ # Each time the pattern is matched, create a new
44
+ # chunk for it, and replace the occurance of the chunk
45
+ # in this content with its mask.
46
+ def self.apply_to(content)
47
+ content.gsub!( self.pattern ) do |match|
48
+ new_chunk = self.new($~, content)
49
+ content.add_chunk(new_chunk)
50
+ new_chunk.mask
51
+ end
52
+ end
53
+
54
+ # should contain only [a-z0-9]
55
+ def mask
56
+ @mask ||="chunk#{@id}#{self.class.mask_string}chunk"
57
+ end
58
+
59
+ # We should not use object_id because object_id is not guarantied
60
+ # to be unique when we restart the wiki (new object ids can equal old ones
61
+ # that were restored form madeleine storage)
62
+ def id
63
+ @id ||= "#{@content.page_id}n#{@content.chunk_id}"
64
+ end
65
+
66
+ def unmask
67
+ @content.sub!(mask, @unmask_text)
68
+ end
69
+
70
+ def rendered?
71
+ @unmask_mode == :normal
72
+ end
73
+
74
+ def escaped?
75
+ @unmask_mode == :escape
76
+ end
77
+
78
+ def revert
79
+ @content.sub!(mask, @text)
80
+ # unregister
81
+ @content.delete_chunk(self)
82
+ end
83
+
84
+ end
85
+
86
+ end
@@ -1,38 +1,54 @@
1
- $: << File.dirname(__FILE__) + "../../libraries"
2
-
3
- require 'redcloth'
4
- require 'bluecloth'
5
- require 'rdocsupport'
6
- require 'chunks/chunk'
7
-
8
- # The markup engines are Chunks that call the one of RedCloth, BlueCloth
9
- # or RDoc to convert text. This markup occurs when the chunk is required
10
- # to mask itself.
11
- module Engines
12
- class Textile < Chunk::Abstract
13
- def self.pattern() /^(.*)$/m end
14
- def mask(content)
15
- RedCloth.new(text,content.options[:engine_opts]).to_html
16
- end
17
- def unmask(content) self end
18
- end
19
-
20
- class Markdown < Chunk::Abstract
21
- def self.pattern() /^(.*)$/m end
22
- def mask(content)
23
- BlueCloth.new(text,content.options[:engine_opts]).to_html
24
- end
25
- def unmask(content) self end
26
- end
27
-
28
- class RDoc < Chunk::Abstract
29
- def self.pattern() /^(.*)$/m end
30
- def mask(content)
31
- RDocSupport::RDocFormatter.new(text).to_html
32
- end
33
- def unmask(content) self end
34
- end
35
-
36
- MAP = { :textile => Textile, :markdown => Markdown, :rdoc => RDoc }
37
- end
38
-
1
+ $: << File.dirname(__FILE__) + "../../lib"
2
+
3
+ require 'redcloth'
4
+ require 'rdocsupport'
5
+ require 'chunks/chunk'
6
+
7
+ # The markup engines are Chunks that call the one of RedCloth
8
+ # or RDoc to convert text. This markup occurs when the chunk is required
9
+ # to mask itself.
10
+ module Engines
11
+ class AbstractEngine < Chunk::Abstract
12
+
13
+ # Create a new chunk for the whole content and replace it with its mask.
14
+ def self.apply_to(content)
15
+ new_chunk = self.new(content)
16
+ content.replace(new_chunk.mask)
17
+ end
18
+
19
+ private
20
+
21
+ # Never create engines by constructor - use apply_to instead
22
+ def initialize(content)
23
+ @content = content
24
+ end
25
+
26
+ end
27
+
28
+ class Textile < AbstractEngine
29
+ def mask
30
+ RedCloth.new(@content, @content.options[:engine_opts]).to_html(:textile)
31
+ end
32
+ end
33
+
34
+ class Markdown < AbstractEngine
35
+ def mask
36
+ RedCloth.new(@content, @content.options[:engine_opts]).to_html(:markdown)
37
+ end
38
+ end
39
+
40
+ class Mixed < AbstractEngine
41
+ def mask
42
+ RedCloth.new(@content, @content.options[:engine_opts]).to_html
43
+ end
44
+ end
45
+
46
+ class RDoc < AbstractEngine
47
+ def mask
48
+ RDocSupport::RDocFormatter.new(@content).to_html
49
+ end
50
+ end
51
+
52
+ MAP = { :textile => Textile, :markdown => Markdown, :mixed => Mixed, :rdoc => RDoc, }
53
+ MAP.default = Textile
54
+ end
@@ -1,29 +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 WikiLink 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
- class Include < WikiChunk::WikiLink
10
- def self.pattern() /^\[\[!include(.*)\]\]\s*$/i end
11
-
12
- attr_reader :page_name
13
-
14
- def initialize(match_data)
15
- super(match_data)
16
- @page_name = match_data[1].strip
17
- end
18
-
19
- # This replaces the [[!include PageName]] text with
20
- # the contents of PageName if it exists. Otherwise
21
- # a warning is displayed.
22
- def mask(content)
23
- page = content.web.pages[page_name]
24
- (page ? page.content : "<em>Could not include #{page_name}</em>")
25
- end
26
-
27
- # Keep this chunk regardless of what happens.
28
- def unmask(content) self end
29
- 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,19 +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
- # A literal chunk that protects 'code' and 'pre' tags from wiki rendering.
9
- class Pre < Chunk::Abstract
10
- PRE_BLOCKS = "a|pre|code"
11
- def self.pattern() Regexp.new('<('+PRE_BLOCKS+')\b[^>]*?>.*?</\1>', Regexp::MULTILINE) end
12
- end
13
-
14
- # A literal chunk that protects HTML tags from wiki rendering.
15
- class Tags < Chunk::Abstract
16
- TAGS = "a|img|em|strong|div|span|table|td|th|ul|ol|li|dl|dt|dd"
17
- def self.pattern() Regexp.new('<(?:'+TAGS+')[^>]*?>', Regexp::MULTILINE) end
18
- end
19
- 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