summaryrefslogtreecommitdiff
path: root/lib/coderay/encoders/html/output.rb
blob: 203cacaaba37e2fb88a0c632a2ef55a0f930fa87 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
module CodeRay
module Encoders

	class HTML
		
		# This module is included in the output String from thew HTML Encoder.
		#
		# It provides methods like wrap, div, page etc.
		#
		# Remember to use #clone instead of #dup to keep the modules the object was
		# extended with.
		#
		# TODO: more doc.
		module Output

			require 'coderay/encoders/html/numerization.rb'

			attr_accessor :css
			
			class << self
				
				# This makes Output look like a class.
				#
				# Example:
				# 
				#  a = Output.new '<span class="co">Code</span>'
				#  a.wrap! :page
				def new string, css = CSS.new, element = nil
					output = string.clone.extend self
					output.wrapped_in = element
					output.css = css
					output
				end
				
				# Raises an exception if an object that doesn't respond to to_str is extended by Output,
				# to prevent users from misuse. Use Module#remove_method to disable.
				def extended o
					warn "The Output module is intended to extend instances of String, not #{o.class}." unless o.respond_to? :to_str
				end

				def make_stylesheet css, in_tag = false
					sheet = css.stylesheet
					sheet = <<-CSS if in_tag
<style type="text/css">
#{sheet}
</style>
					CSS
					sheet
				end

				def page_template_for_css css
					sheet = make_stylesheet css
					PAGE.apply 'CSS', sheet
				end

				# Define a new wrapper. This is meta programming.
				def wrapper *wrappers
					wrappers.each do |wrapper|
						define_method wrapper do |*args|
							wrap wrapper, *args
						end
						define_method "#{wrapper}!".to_sym do |*args|  
							wrap! wrapper, *args
						end
					end
				end
				
			end

			wrapper :div, :span, :page

			def wrapped_in? element
				wrapped_in == element
			end
			
			def wrapped_in
				@wrapped_in ||= nil
			end
			attr_writer :wrapped_in
			
			def wrap_in template
				clone.wrap_in! template
			end

			def wrap_in! template
				Template.wrap! self, template, 'CONTENT'
				self
			end
			
			def wrap! element, *args
				return self if not element or element == wrapped_in
				case element
				when :div
					raise "Can't wrap %p in %p" % [wrapped_in, element] unless wrapped_in? nil
					wrap_in! DIV
				when :span
					raise "Can't wrap %p in %p" % [wrapped_in, element] unless wrapped_in? nil
					wrap_in! SPAN
				when :page
					wrap! :div if wrapped_in? nil
					raise "Can't wrap %p in %p" % [wrapped_in, element] unless wrapped_in? :div
					wrap_in! Output.page_template_for_css(@css)
				when nil
					return self
				else
					raise "Unknown value %p for :wrap" % element
				end
				@wrapped_in = element
				self
			end

			def wrap *args
				clone.wrap!(*args)
			end

			def stylesheet in_tag = false
				Output.make_stylesheet @css, in_tag
			end

			class Template < String

				def self.wrap! str, template, target
					target = Regexp.new(Regexp.escape("<%#{target}%>"))
					if template =~ target
						str[0,0] = $`
						str << $'
					else
						raise "Template target <%%%p%%> not found" % target
					end
				end
				
				def apply target, replacement
					target = Regexp.new(Regexp.escape("<%#{target}%>"))
					if self =~ target
						Template.new($` + replacement + $')
					else
						raise "Template target <%%%p%%> not found" % target
					end
				end

				module Simple
					def ` str  #`
						Template.new str
					end
				end
			end
			
			extend Template::Simple

#-- don't include the templates in docu
			
			SPAN = `<span class="CodeRay"><%CONTENT%></span>`

			DIV = <<-`DIV`
<div class="CodeRay">
	<div class="code"><pre><%CONTENT%></pre></div>	
</div>
			DIV

			TABLE = <<-`TABLE`
<table class="CodeRay"><tr>
	<td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"><pre><%LINE_NUMBERS%></pre></td>
	<td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"><%CONTENT%></pre></td>
</tr></table>
			TABLE
			# title="double click to expand" 

			LIST = <<-`LIST`
<ol class="CodeRay"><%CONTENT%></ol>
			LIST

			PAGE = <<-`PAGE`
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
	"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="de">
<head>
	<meta http-equiv="content-type" content="text/html; charset=utf-8" />
	<title>CodeRay HTML Encoder Example</title>
	<style type="text/css">
<%CSS%>
	</style>
</head>
<body style="background-color: white;">

<%CONTENT%>
</body>
</html>
			PAGE

		end

	end

end
end