coderay 0.4.3.48

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 (45) hide show
  1. data/LICENSE +340 -0
  2. data/README +103 -0
  3. data/demo/demo_count.rb +10 -0
  4. data/demo/demo_css.rb +4 -0
  5. data/demo/demo_div.rb +19 -0
  6. data/demo/demo_dump.rb +15 -0
  7. data/demo/demo_encoder.rb +39 -0
  8. data/demo/demo_global_vars.rb +13 -0
  9. data/demo/demo_global_vars2.rb +28 -0
  10. data/demo/demo_html.rb +394 -0
  11. data/demo/demo_html2.rb +11 -0
  12. data/demo/demo_load_encoder.rb +17 -0
  13. data/demo/demo_more.rb +204 -0
  14. data/demo/demo_scanner.rb +36 -0
  15. data/demo/demo_server.rb +92 -0
  16. data/demo/demo_simple.rb +10 -0
  17. data/demo/demo_stream.rb +25 -0
  18. data/demo/demo_stream2.rb +8 -0
  19. data/demo/demo_tokens.rb +3 -0
  20. data/lib/coderay.rb +284 -0
  21. data/lib/coderay/encoder.rb +151 -0
  22. data/lib/coderay/encoders/count.rb +21 -0
  23. data/lib/coderay/encoders/div.rb +16 -0
  24. data/lib/coderay/encoders/helpers/html_css.rb +155 -0
  25. data/lib/coderay/encoders/helpers/html_helper.rb +68 -0
  26. data/lib/coderay/encoders/helpers/html_output.rb +237 -0
  27. data/lib/coderay/encoders/html.rb +169 -0
  28. data/lib/coderay/encoders/null.rb +20 -0
  29. data/lib/coderay/encoders/span.rb +16 -0
  30. data/lib/coderay/encoders/statistic.rb +74 -0
  31. data/lib/coderay/encoders/text.rb +33 -0
  32. data/lib/coderay/encoders/tokens.rb +44 -0
  33. data/lib/coderay/encoders/yaml.rb +19 -0
  34. data/lib/coderay/helpers/filetype.rb +145 -0
  35. data/lib/coderay/helpers/gzip_simple.rb +123 -0
  36. data/lib/coderay/helpers/plugin.rb +286 -0
  37. data/lib/coderay/helpers/scanner_helper.rb +63 -0
  38. data/lib/coderay/scanner.rb +197 -0
  39. data/lib/coderay/scanners/c.rb +147 -0
  40. data/lib/coderay/scanners/delphi.rb +123 -0
  41. data/lib/coderay/scanners/helpers/ruby_helper.rb +212 -0
  42. data/lib/coderay/scanners/plaintext.rb +13 -0
  43. data/lib/coderay/scanners/ruby.rb +337 -0
  44. data/lib/coderay/tokens.rb +324 -0
  45. metadata +89 -0
@@ -0,0 +1,151 @@
1
+ module CodeRay
2
+
3
+ # This module holds the Encoder class and its subclasses.
4
+ # For example, the HTML encoder is named CodeRay::Encoders::HTML
5
+ # can be found in coderay/encoders/html.
6
+ #
7
+ # Encoders also provides methods and constants for the register
8
+ # mechanism and the [] method that returns the Encoder class
9
+ # belonging to the given format.
10
+ module Encoders
11
+ extend PluginHost
12
+ plugin_path 'coderay/encoders'
13
+
14
+ # = Encoder
15
+ #
16
+ # The Encoder base class. Together with Scanner and
17
+ # Tokens, it forms the highlighting triad.
18
+ #
19
+ # Encoder instances take a Tokens object and do something with it.
20
+ #
21
+ # The most common Encoder is surely the HTML encoder
22
+ # (CodeRay::Encoders::HTML). It highlights the code in a colorful
23
+ # html page.
24
+ # If you want the highlighted code in a div or a span instead,
25
+ # use its subclasses Div and Span.
26
+ class Encoder
27
+ extend Plugin
28
+ plugin_host Encoders
29
+
30
+ attr_reader :token_stream
31
+
32
+ class << self
33
+
34
+ # Returns if the Encoder can be used in streaming mode.
35
+ def streamable?
36
+ is_a? Streamable
37
+ end
38
+
39
+ # If FILE_EXTENSION isn't defined, this method returns the
40
+ # downcase class name instead.
41
+ def const_missing sym
42
+ if sym == :FILE_EXTENSION
43
+ sym.to_s.downcase
44
+ else
45
+ super
46
+ end
47
+ end
48
+
49
+ end
50
+
51
+ # Subclasses are to store their default options in this constant.
52
+ DEFAULT_OPTIONS = { :stream => false }
53
+
54
+ # The options you gave the Encoder at creating.
55
+ attr_accessor :options
56
+
57
+ # Creates a new Encoder.
58
+ # +options+ is saved and used for all encode operations, as long
59
+ # as you don't overwrite it there by passing additional options.
60
+ #
61
+ # Encoder objects provide three encode methods:
62
+ # - encode simply takes a +code+ string and a +lang+
63
+ # - encode_tokens expects a +tokens+ object instead
64
+ # - encode_stream is like encode, but uses streaming mode.
65
+ #
66
+ # Each method has an optional +options+ parameter. These are
67
+ # added to the options you passed at creation.
68
+ def initialize options = {}
69
+ @options = self.class::DEFAULT_OPTIONS.merge options
70
+ raise "I am only the basic Encoder class. I can't encode "\
71
+ "anything. :( Use my subclasses." if self.class == Encoder
72
+ end
73
+
74
+ # Encode a Tokens object.
75
+ def encode_tokens tokens, options = {}
76
+ options = @options.merge options
77
+ setup options
78
+ compile tokens, options
79
+ finish options
80
+ end
81
+
82
+ # Encode the given +code+ after tokenizing it using the Scanner
83
+ # for +lang+.
84
+ def encode code, lang, options = {}
85
+ options = @options.merge options
86
+ scanner_options = CodeRay.get_scanner_options(options)
87
+ tokens = CodeRay.scan code, lang, scanner_options
88
+ encode_tokens tokens, options
89
+ end
90
+
91
+ # You can use highlight instead of encode, if that seems
92
+ # more clear to you.
93
+ alias highlight encode
94
+
95
+ # Encode the given +code+ using the Scanner for +lang+ in
96
+ # streaming mode.
97
+ def encode_stream code, lang, options = {}
98
+ raise NotStreamableError, self unless kind_of? Streamable
99
+ options = @options.merge options
100
+ setup options
101
+ scanner_options = CodeRay.get_scanner_options options
102
+ @token_stream =
103
+ CodeRay.scan_stream code, lang, scanner_options, &self
104
+ finish options
105
+ end
106
+
107
+ # Behave like a proc. The token method is converted to a proc.
108
+ def to_proc
109
+ method(:token).to_proc
110
+ end
111
+
112
+ protected
113
+
114
+ # Called with merged options before encoding starts.
115
+ # Sets @out to an empty string.
116
+ #
117
+ # See the HTML Encoder for an example of option caching.
118
+ def setup options
119
+ @out = ''
120
+ end
121
+
122
+ # Called with +text+ and +kind+ of the currently scanned token.
123
+ # For simple scanners, it's enougth to implement this method.
124
+ #
125
+ # Raises a NotImplementedError exception if it is not overwritten
126
+ # in subclass.
127
+ def token text, kind
128
+ raise NotImplementedError,
129
+ "#{self.class}#token not implemented."
130
+ end
131
+
132
+ # Called with merged options after encoding starts.
133
+ # The return value is the result of encoding, typically @out.
134
+ def finish options
135
+ @out
136
+ end
137
+
138
+ # Do the encoding.
139
+ #
140
+ # The already created +tokens+ object must be used; it can be a
141
+ # TokenStream or a Tokens object.
142
+ def compile tokens, options
143
+ tokens.each(&self)
144
+ end
145
+
146
+ end
147
+
148
+ end
149
+ end
150
+
151
+ # vim:sw=2:ts=2:noet:tw=78
@@ -0,0 +1,21 @@
1
+ module CodeRay
2
+ module Encoders
3
+
4
+ class Count < Encoder
5
+
6
+ include Streamable
7
+ register_for :count
8
+
9
+ protected
10
+
11
+ def setup options
12
+ @out = 0
13
+ end
14
+
15
+ def token text, kind
16
+ @out += 1
17
+ end
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,16 @@
1
+ module CodeRay module Encoders
2
+
3
+ require 'coderay/encoders/html'
4
+ class Div < HTML
5
+
6
+ FILE_EXTENSION = 'div.html'
7
+
8
+ register_for :div
9
+
10
+ DEFAULT_OPTIONS = HTML::DEFAULT_OPTIONS.merge({
11
+ :css => :style,
12
+ :wrap => :div,
13
+ })
14
+ end
15
+
16
+ end end
@@ -0,0 +1,155 @@
1
+ module CodeRay module Encoders
2
+
3
+ class HTML
4
+ class CSS
5
+
6
+ def initialize stylesheet = TOKENS
7
+ @classes = Hash.new
8
+ parse stylesheet
9
+ end
10
+
11
+ def [] *styles
12
+ cl = @classes[styles.first]
13
+ return '' unless cl
14
+ style = false
15
+ 1.upto(cl.size + 1) do |offset|
16
+ break if style = cl[styles[offset .. -1]]
17
+ end
18
+ return style
19
+ end
20
+
21
+ private
22
+
23
+ CSS_CLASS_PATTERN = /
24
+ ( (?: # $1 = classes
25
+ \s* \. [-\w]+
26
+ )+ )
27
+ \s* \{
28
+ ( [^\}]* ) # $2 = style
29
+ \} \s*
30
+ |
31
+ ( . ) # $3 = error
32
+ /mx
33
+ def parse stylesheet
34
+ stylesheet.scan CSS_CLASS_PATTERN do |classes, style, error|
35
+ raise "CSS parse error: '#{error.inspect}' not recognized" if error
36
+ styles = classes.scan(/[-\w]+/)
37
+ cl = styles.pop
38
+ @classes[cl] ||= Hash.new
39
+ @classes[cl][styles] = style.strip
40
+ end
41
+ end
42
+
43
+ MAIN = <<-'MAIN'
44
+ .CodeRay {
45
+ background-color: #f8f8f8;
46
+ border: 1px solid silver;
47
+ font-family: 'Courier New', 'Terminal', monospace;
48
+ color: black;
49
+ width: 100%;
50
+ padding: 2px;
51
+ }
52
+ .CodeRay pre { margin: 0px; }
53
+
54
+ div.CodeRay { }
55
+
56
+ span.CodeRay { white-space: pre; border: 0; }
57
+
58
+ table.CodeRay { border-collapse: collapse; }
59
+ table.CodeRay td { padding: 2px 4px; vertical-align: top; }
60
+
61
+ .CodeRay .line_numbers, .CodeRay .no {
62
+ background-color: #def;
63
+ color: gray;
64
+ text-align: right;
65
+ }
66
+ .CodeRay .line_numbers tt { font-weight: bold; }
67
+ .CodeRay .no { padding: 0px 4px; }
68
+ .CodeRay .code { width: 100%; }
69
+
70
+ .CodeRay .code {
71
+ }
72
+ .CodeRay .code pre { overflow: auto; }
73
+ MAIN
74
+
75
+ TOKENS = <<-'TOKENS'
76
+ .af { color:#00C; }
77
+ .an { color:#007; }
78
+ .av { color:#700; }
79
+ .aw { color:#C00; }
80
+ .bi { color:#509; font-weight:bold; }
81
+ .c { color:#888; }
82
+
83
+ .ch { color:#04D; }
84
+ .ch .k { color:#04D; }
85
+ .ch .dl { color:#039; }
86
+
87
+ .cl { color:#B06; font-weight:bold; }
88
+ .co { color:#036; font-weight:bold; }
89
+ .cr { color:#0A0; }
90
+ .cv { color:#369; }
91
+ .df { color:#099; font-weight:bold; }
92
+ .di { color:#088; font-weight:bold; }
93
+ .dl { color:black; }
94
+ .do { color:#970; }
95
+ .ds { color:#D42; font-weight:bold; }
96
+ .e { color:#666; font-weight:bold; }
97
+ .er { color:#F00; background-color:#FAA; }
98
+ .ex { color:#F00; font-weight:bold; }
99
+ .fl { color:#60E; font-weight:bold; }
100
+ .fu { color:#06B; font-weight:bold; }
101
+ .gv { color:#d70; font-weight:bold; }
102
+ .hx { color:#058; font-weight:bold; }
103
+ .i { color:#00D; font-weight:bold; }
104
+ .ic { color:#B44; font-weight:bold; }
105
+ .in { color:#B2B; font-weight:bold; }
106
+ .iv { color:#33B; }
107
+ .la { color:#970; font-weight:bold; }
108
+ .lv { color:#963; }
109
+ .oc { color:#40E; font-weight:bold; }
110
+ .on { color:#000; font-weight:bold; }
111
+ .pc { color:#038; font-weight:bold; }
112
+ .pd { color:#369; font-weight:bold; }
113
+ .pp { color:#579; }
114
+ .pt { color:#339; font-weight:bold; }
115
+ .r { color:#080; font-weight:bold; }
116
+
117
+ .rx { background-color:#fff0ff; }
118
+ .rx .k { color:#808; }
119
+ .rx .dl { color:#404; }
120
+ .rx .mod { color:#C2C; }
121
+ .rx .fu { color:#404; font-weight: bold; }
122
+
123
+ .s { background-color:#fff0f0; }
124
+ .s .s { background-color:#ffe0e0; }
125
+ .s .s .s { background-color:#ffd0d0; }
126
+ .s .k { color:#D20; }
127
+ .s .dl { color:#710; }
128
+
129
+ .sh { background-color:#f0fff0; }
130
+ .sh .k { color:#2B2; }
131
+ .sh .dl { color:#161; }
132
+
133
+ .sy { color:#A60; }
134
+ .sy .k { color:#A60; }
135
+ .sy .dl { color:#630; }
136
+
137
+ .ta { color:#070; }
138
+ .tf { color:#070; font-weight:bold; }
139
+ .ts { color:#D70; font-weight:bold; }
140
+ .ty { color:#339; font-weight:bold; }
141
+ .v { color:#036; }
142
+ .xt { color:#444; }
143
+ TOKENS
144
+
145
+ DEFAULT_STYLESHEET = MAIN + TOKENS.gsub(/^(?!$)/, '.CodeRay ')
146
+
147
+ end
148
+ end
149
+
150
+ end end
151
+
152
+ if $0 == __FILE__
153
+ require 'pp'
154
+ pp CodeRay::Encoders::HTML::CSS.new
155
+ end
@@ -0,0 +1,68 @@
1
+ module CodeRay module Encoders
2
+
3
+ class HTML
4
+
5
+ ClassOfKind = {
6
+ :attribute_name => 'an',
7
+ :attribute_name_fat => 'af',
8
+ :attribute_value => 'av',
9
+ :attribute_value_fat => 'aw',
10
+ :bin => 'bi',
11
+ :char => 'ch',
12
+ :class => 'cl',
13
+ :class_variable => 'cv',
14
+ :color => 'cr',
15
+ :comment => 'c',
16
+ :constant => 'co',
17
+ :content => 'k',
18
+ :definition => 'df',
19
+ :delimiter => 'dl',
20
+ :directive => 'di',
21
+ :doc => 'do',
22
+ :doc_string => 'ds',
23
+ :error => 'er',
24
+ :escape => 'e',
25
+ :exception => 'ex',
26
+ :float => 'fl',
27
+ :function => 'fu',
28
+ :global_variable => 'gv',
29
+ :hex => 'hx',
30
+ :include => 'ic',
31
+ :instance_variable => 'iv',
32
+ :integer => 'i',
33
+ :interpreted => 'in',
34
+ :label => 'la',
35
+ :local_variable => 'lv',
36
+ :modifier => 'mod',
37
+ :oct => 'oc',
38
+ :operator_name => 'on',
39
+ :pre_constant => 'pc',
40
+ :pre_type => 'pt',
41
+ :predefined => 'pd',
42
+ :preprocessor => 'pp',
43
+ :regexp => 'rx',
44
+ :reserved => 'r',
45
+ :shell => 'sh',
46
+ :string => 's',
47
+ :symbol => 'sy',
48
+ :tag => 'ta',
49
+ :tag_fat => 'tf',
50
+ :tag_special => 'ts',
51
+ :type => 'ty',
52
+ :variable => 'v',
53
+ :xml_text => 'xt',
54
+
55
+ :ident => :NO_HIGHLIGHT, # 'id'
56
+ :operator => :NO_HIGHLIGHT, # 'op'
57
+ :space => :NO_HIGHLIGHT, # 'sp'
58
+ :plain => :NO_HIGHLIGHT,
59
+ }
60
+ ClassOfKind[:procedure] = ClassOfKind[:method] = ClassOfKind[:function]
61
+ ClassOfKind[:open] = ClassOfKind[:close] = ClassOfKind[:delimiter]
62
+ ClassOfKind[:nesting_delimiter] = ClassOfKind[:delimiter]
63
+ ClassOfKind[:escape] = ClassOfKind[:delimiter]
64
+ ClassOfKind.default = ClassOfKind[:error] or raise 'no class found for :error!'
65
+
66
+ end
67
+
68
+ end end
@@ -0,0 +1,237 @@
1
+ module CodeRay
2
+ module Encoders
3
+
4
+ class HTML
5
+
6
+ # This module is included in the output String from thew HTML Encoder.
7
+ #
8
+ # It provides methods like wrap, div, page etc.
9
+ #
10
+ # Remember to use #clone instead of #dup to keep the modules the object was
11
+ # extended with.
12
+ #
13
+ # TODO: more doc.
14
+ module Output
15
+
16
+ class << self
17
+
18
+ # This makes Output look like a class.
19
+ #
20
+ # Example:
21
+ #
22
+ # a = Output.new '<span class="co">Code</span>'
23
+ # a.wrap! :page
24
+ def new string, element = nil
25
+ output = string.clone.extend self
26
+ output.wrapped_in = element
27
+ output
28
+ end
29
+
30
+ # Raises an exception if an object that doesn't respond to to_str is extended by Output,
31
+ # to prevent users from misuse. Use Module#remove_method to disable.
32
+ def extended o
33
+ warn "The Output module is intended to extend instances of String, not #{o.class}." unless o.respond_to? :to_str
34
+ end
35
+
36
+ def page_template_for_css css = :default
37
+ css = CSS::DEFAULT_STYLESHEET if css == :default
38
+ PAGE.apply 'CSS', css
39
+ end
40
+
41
+ # Define a new wrapper. This is meta programming.
42
+ def wrapper *wrappers
43
+ wrappers.each do |wrapper|
44
+ define_method wrapper do |*args|
45
+ wrap wrapper, *args
46
+ end
47
+ define_method "#{wrapper}!".to_sym do |*args|
48
+ wrap! wrapper, *args
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ wrapper :div, :span, :page
55
+
56
+ def wrapped_in
57
+ @wrapped_in ||= nil
58
+ end
59
+ attr_writer :wrapped_in
60
+
61
+ def wrapped_in? element
62
+ wrapped_in == element
63
+ end
64
+
65
+ def wrap_in template
66
+ clone.wrap_in! template
67
+ end
68
+
69
+ def wrap_in! template
70
+ Template.wrap! self, template, 'CONTENT'
71
+ self
72
+ end
73
+
74
+ def wrap! element, *args
75
+ return self if not element or element == wrapped_in
76
+ case element
77
+ when :div
78
+ raise "Can't wrap %p in %p" % [wrapped_in, element] unless wrapped_in? nil
79
+ wrap_in! DIV
80
+ when :span
81
+ raise "Can't wrap %p in %p" % [wrapped_in, element] unless wrapped_in? nil
82
+ wrap_in! SPAN
83
+ when :page
84
+ wrap! :div if wrapped_in? nil
85
+ raise "Can't wrap %p in %p" % [wrapped_in, element] unless wrapped_in? :div
86
+ wrap_in! Output.page_template_for_css
87
+ when nil
88
+ return self
89
+ else
90
+ raise "Unknown value %p for :wrap" % element
91
+ end
92
+ @wrapped_in = element
93
+ self
94
+ end
95
+
96
+ def wrap *args
97
+ clone.wrap!(*args)
98
+ end
99
+
100
+ def numerize! mode = :table, options = {}
101
+ return self unless mode
102
+
103
+ start = options.fetch :line_number_start, DEFAULT_OPTIONS[:line_number_start]
104
+ unless start.is_a? Integer
105
+ raise ArgumentError, "Invalid value %p for :line_number_start; Integer expected." % start
106
+ end
107
+
108
+ unless NUMERIZABLE_WRAPPINGS.include? options[:wrap]
109
+ raise ArgumentError, "Can't numerize, :wrap must be in %p, but is %p" % [NUMERIZABLE_WRAPPINGS, options[:wrap]]
110
+ end
111
+
112
+ bold_every = options.fetch :bold_every, DEFAULT_OPTIONS[:bold_every]
113
+ bolding =
114
+ if bold_every == :no_bolding or bold_every == 0
115
+ proc { |line| line.to_s }
116
+ elsif bold_every.is_a? Integer
117
+ proc do |line|
118
+ if line % bold_every == 0
119
+ "<strong>#{line}</strong>" # every bold_every-th number in bold
120
+ else
121
+ line.to_s
122
+ end
123
+ end
124
+ else
125
+ raise ArgumentError, "Invalid value %p for :bolding; :no_bolding or Integer expected." % bolding
126
+ end
127
+
128
+ line_count = count("\n")
129
+ line_count += 1 if self[-1] != ?\n
130
+
131
+ case mode
132
+ when :inline
133
+ max_width = (start + line_count).to_s.size
134
+ line = start
135
+ gsub!(/^/) do
136
+ line_number = bolding.call line
137
+ line += 1
138
+ "<span class=\"no\">#{ line_number.rjust(max_width) }</span> "
139
+ end
140
+ wrap! :div
141
+
142
+ when :table
143
+ # This is really ugly.
144
+ # Because even monospace fonts seem to have different heights when bold,
145
+ # I make the newline bold, both in the code and the line numbers.
146
+ # FIXME Still not working perfect for Mr. Internet Exploder
147
+ line_numbers = (start ... start + line_count).to_a.map(&bolding).join("\n")
148
+ line_numbers << "\n" # also for Mr. MS Internet Exploder :-/
149
+ line_numbers.gsub!(/\n/) { "<tt>\n</tt>" }
150
+
151
+ line_numbers_tpl = DIV_TABLE.apply('LINE_NUMBERS', line_numbers)
152
+ gsub!(/\n/) { "<tt>\n</tt>" }
153
+ wrap_in! line_numbers_tpl
154
+ @wrapped_in = :div
155
+
156
+ else
157
+ raise ArgumentError, "Unknown value %p for mode: :inline or :table expected" % mode
158
+ end
159
+
160
+ self
161
+ end
162
+
163
+ def numerize *args
164
+ clone.numerize!(*args)
165
+ end
166
+
167
+ class Template < String
168
+
169
+ def self.wrap! str, template, target
170
+ target = Regexp.new(Regexp.escape("<%#{target}%>"))
171
+ if template =~ target
172
+ str[0,0] = $`
173
+ str << $'
174
+ else
175
+ raise "Template target <%%%p%%> not found" % target
176
+ end
177
+ end
178
+
179
+ def apply target, replacement
180
+ target = Regexp.new(Regexp.escape("<%#{target}%>"))
181
+ if self =~ target
182
+ Template.new($` + replacement + $')
183
+ else
184
+ raise "Template target <%%%p%%> not found" % target
185
+ end
186
+ end
187
+
188
+ module Simple
189
+ def ` str #`
190
+ Template.new str
191
+ end
192
+ end
193
+ end
194
+
195
+ extend Template::Simple
196
+
197
+ #-- don't include the templates in docu
198
+
199
+ SPAN = `<span class="CodeRay"><%CONTENT%></span>`
200
+
201
+ DIV = <<-`DIV`
202
+ <div class="CodeRay">
203
+ <div class="code"><pre><%CONTENT%></pre></div>
204
+ </div>
205
+ DIV
206
+
207
+ DIV_TABLE = <<-`DIV_TABLE`
208
+ <table class="CodeRay"> <tr>
209
+ <td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"><pre><%LINE_NUMBERS%></pre></td>
210
+ <td class="code"><pre title="double click to expand" ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"><%CONTENT%></pre></td>
211
+ </tr> </table>
212
+ DIV_TABLE
213
+
214
+ PAGE = <<-`PAGE`
215
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
216
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
217
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="de">
218
+ <head>
219
+ <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
220
+ <title>CodeRay HTML Encoder Example</title>
221
+ <style type="text/css">
222
+ <%CSS%>
223
+ </style>
224
+ </head>
225
+ <body style="background-color: white;">
226
+
227
+ <%CONTENT%>
228
+ </body>
229
+ </html>
230
+ PAGE
231
+
232
+ end
233
+
234
+ end
235
+
236
+ end
237
+ end