coderay 0.4.3.48

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