erector 0.8.3 → 0.9.0.pre1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +21 -0
- data/Rakefile +171 -0
- data/VERSION.yml +3 -2
- data/lib/erector.rb +3 -5
- data/lib/erector/abstract_widget.rb +76 -31
- data/lib/erector/attributes.rb +34 -0
- data/lib/erector/caching.rb +1 -0
- data/lib/erector/convenience.rb +12 -4
- data/lib/erector/dependency.rb +2 -1
- data/lib/erector/element.rb +113 -0
- data/lib/erector/erect/erect.rb +10 -7
- data/lib/erector/externals.rb +2 -1
- data/lib/erector/html.rb +6 -340
- data/lib/erector/html_widget.rb +300 -0
- data/lib/erector/inline.rb +1 -1
- data/lib/erector/output.rb +39 -12
- data/lib/erector/promise.rb +137 -0
- data/lib/erector/rails2/extensions/rails_widget.rb +1 -1
- data/lib/erector/rails3.rb +1 -1
- data/lib/erector/sass.rb +14 -19
- data/lib/erector/tag.rb +65 -0
- data/lib/erector/text.rb +123 -0
- data/lib/erector/version.rb +1 -1
- data/lib/erector/widget.rb +52 -12
- data/lib/erector/widgets/page.rb +12 -10
- data/lib/erector/xml_widget.rb +131 -0
- data/spec/erector/caching_spec.rb +1 -0
- data/spec/erector/externals_spec.rb +0 -1
- data/spec/erector/html_spec.rb +9 -25
- data/spec/erector/output_spec.rb +102 -9
- data/spec/erector/promise_spec.rb +173 -0
- data/spec/erector/sass_spec.rb +1 -1
- data/spec/erector/tag_spec.rb +67 -0
- data/spec/erector/widget_spec.rb +53 -2
- data/spec/erector/xml_widget_spec.rb +74 -0
- data/spec/rails2/rails_app/Gemfile +1 -0
- data/spec/rails2/rails_app/spec/rails_spec_helper.rb +2 -0
- data/spec/rails_root/Gemfile +11 -0
- data/spec/rails_root/README +256 -0
- data/spec/rails_root/Rakefile +7 -0
- data/spec/rails_root/app/controllers/application.rb +6 -0
- data/spec/rails_root/app/controllers/application_controller.rb +3 -0
- data/spec/rails_root/app/helpers/application_helper.rb +2 -0
- data/spec/rails_root/app/views/layouts/application.html.erb +14 -0
- data/spec/rails_root/app/views/test/_erb.erb +1 -0
- data/spec/rails_root/app/views/test/_erector.rb +5 -0
- data/spec/rails_root/app/views/test/_partial_with_locals.rb +7 -0
- data/spec/rails_root/app/views/test/bare.rb +5 -0
- data/spec/rails_root/app/views/test/erb_from_erector.html.rb +5 -0
- data/spec/rails_root/app/views/test/erector_from_erb.html.erb +1 -0
- data/spec/rails_root/app/views/test/erector_with_locals_from_erb.html.erb +6 -0
- data/spec/rails_root/app/views/test/implicit_assigns.html.rb +5 -0
- data/spec/rails_root/app/views/test/needs.html.rb +7 -0
- data/spec/rails_root/app/views/test/needs_subclass.html.rb +5 -0
- data/spec/rails_root/app/views/test/protected_instance_variable.html.rb +5 -0
- data/spec/rails_root/app/views/test/render_default.html.rb +5 -0
- data/spec/rails_root/app/views/test/render_partial.html.rb +5 -0
- data/spec/rails_root/config.ru +4 -0
- data/spec/rails_root/config/application.rb +42 -0
- data/spec/rails_root/config/boot.rb +13 -0
- data/spec/rails_root/config/database.yml +22 -0
- data/spec/rails_root/config/environment.rb +5 -0
- data/spec/rails_root/config/environments/development.rb +22 -0
- data/spec/rails_root/config/environments/production.rb +49 -0
- data/spec/rails_root/config/environments/test.rb +35 -0
- data/spec/rails_root/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/rails_root/config/initializers/inflections.rb +10 -0
- data/spec/rails_root/config/initializers/mime_types.rb +5 -0
- data/spec/rails_root/config/initializers/secret_token.rb +7 -0
- data/spec/rails_root/config/initializers/session_store.rb +8 -0
- data/spec/rails_root/config/locales/en.yml +5 -0
- data/spec/rails_root/config/routes.rb +58 -0
- data/spec/rails_root/db/seeds.rb +7 -0
- data/spec/rails_root/doc/README_FOR_APP +2 -0
- data/spec/rails_root/log/development.log +17 -0
- data/spec/rails_root/log/test.log +3750 -0
- data/spec/rails_root/public/404.html +26 -0
- data/spec/rails_root/public/422.html +26 -0
- data/spec/rails_root/public/500.html +26 -0
- data/spec/rails_root/public/dispatch.cgi +10 -0
- data/spec/rails_root/public/dispatch.fcgi +24 -0
- data/spec/rails_root/public/dispatch.rb +10 -0
- data/spec/rails_root/public/favicon.ico +0 -0
- data/spec/rails_root/public/images/rails.png +0 -0
- data/spec/rails_root/public/index.html +262 -0
- data/spec/rails_root/public/javascripts/application.js +2 -0
- data/spec/rails_root/public/javascripts/controls.js +965 -0
- data/spec/rails_root/public/javascripts/dragdrop.js +974 -0
- data/spec/rails_root/public/javascripts/effects.js +1123 -0
- data/spec/rails_root/public/javascripts/prototype.js +6001 -0
- data/spec/rails_root/public/javascripts/rails.js +175 -0
- data/spec/rails_root/public/robots.txt +5 -0
- data/spec/rails_root/script/about +3 -0
- data/spec/rails_root/script/console +3 -0
- data/spec/rails_root/script/destroy +3 -0
- data/spec/rails_root/script/generate +3 -0
- data/spec/rails_root/script/performance/benchmarker +3 -0
- data/spec/rails_root/script/performance/profiler +3 -0
- data/spec/rails_root/script/performance/request +3 -0
- data/spec/rails_root/script/plugin +3 -0
- data/spec/rails_root/script/process/inspector +3 -0
- data/spec/rails_root/script/process/reaper +3 -0
- data/spec/rails_root/script/process/spawner +3 -0
- data/spec/rails_root/script/rails +6 -0
- data/spec/rails_root/script/runner +3 -0
- data/spec/rails_root/script/server +3 -0
- data/spec/rails_root/spec/form_builder_spec.rb +21 -0
- data/spec/rails_root/spec/rails_helpers_spec.rb +220 -0
- data/spec/rails_root/spec/rails_spec_helper.rb +10 -0
- data/spec/rails_root/spec/rails_widget_spec.rb +83 -0
- data/spec/rails_root/spec/render_spec.rb +298 -0
- data/spec/rails_root/test/performance/browsing_test.rb +9 -0
- data/spec/rails_root/test/test_helper.rb +13 -0
- data/spec/spec_helper.rb +3 -1
- metadata +202 -66
data/lib/erector/caching.rb
CHANGED
data/lib/erector/convenience.rb
CHANGED
@@ -4,7 +4,7 @@ module Erector
|
|
4
4
|
# You may just want to call to_html(:prettyprint => true)
|
5
5
|
# so you can pass in other rendering options as well.
|
6
6
|
def to_pretty(options = {})
|
7
|
-
|
7
|
+
render(options.merge(:prettyprint => true))
|
8
8
|
end
|
9
9
|
|
10
10
|
# Render (like to_html) but stripping all tags and inserting some
|
@@ -25,12 +25,20 @@ module Erector
|
|
25
25
|
# which are rendered, or strings, which are html-escaped and output.
|
26
26
|
def join(array, separator)
|
27
27
|
first = true
|
28
|
-
array.each do |
|
28
|
+
array.each do |item|
|
29
29
|
if !first
|
30
|
-
|
30
|
+
if separator.is_a? Widget
|
31
|
+
widget separator
|
32
|
+
else
|
33
|
+
text separator
|
34
|
+
end
|
31
35
|
end
|
32
36
|
first = false
|
33
|
-
|
37
|
+
if item.is_a? Widget
|
38
|
+
widget item
|
39
|
+
else
|
40
|
+
text item
|
41
|
+
end
|
34
42
|
end
|
35
43
|
end
|
36
44
|
|
data/lib/erector/dependency.rb
CHANGED
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'erector/abstract_widget'
|
2
|
+
require 'erector/promise'
|
3
|
+
|
4
|
+
# todo: unit test
|
5
|
+
module Erector
|
6
|
+
module Element
|
7
|
+
# Internal method used to emit an HTML/XML element, including an open tag,
|
8
|
+
# attributes (optional, via the default hash), contents (also optional),
|
9
|
+
# and close tag.
|
10
|
+
#
|
11
|
+
# Using the arcane powers of Ruby, there are magic methods that call
|
12
|
+
# +element+ for all the standard HTML tags, like +a+, +body+, +p+, and so
|
13
|
+
# forth. Look at the source of erector/html.rb for the full list.
|
14
|
+
# Unfortunately, this big mojo confuses rdoc, so we can't see each method
|
15
|
+
# in this rdoc page, but trust us, they're there.
|
16
|
+
#
|
17
|
+
# When calling one of these magic methods, put attributes in the default
|
18
|
+
# hash. If there is a string parameter, then it is used as the contents.
|
19
|
+
# If there is a block, then it is executed (yielded), and the string
|
20
|
+
# parameter is ignored. The block will usually be in the scope of the
|
21
|
+
# child widget, which means it has access to all the methods of Widget,
|
22
|
+
# which will eventually end up appending text to the +output+ string. See
|
23
|
+
# how elegant it is? Not confusing at all if you don't think about it.
|
24
|
+
#
|
25
|
+
def element(*args, &block)
|
26
|
+
_element(*args, &block)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Internal method used to emit a self-closing HTML/XML element, including
|
30
|
+
# a tag name and optional attributes (passed in via the default hash).
|
31
|
+
#
|
32
|
+
# Using the arcane powers of Ruby, there are magic methods that call
|
33
|
+
# +empty_element+ for all the standard HTML tags, like +img+, +br+, and so
|
34
|
+
# forth. Look at the source of #self_closing_tags for the full list.
|
35
|
+
# Unfortunately, this big mojo confuses rdoc, so we can't see each method
|
36
|
+
# in this rdoc page, but trust us, they're there.
|
37
|
+
#
|
38
|
+
def empty_element(*args, &block)
|
39
|
+
_empty_element(*args, &block)
|
40
|
+
end
|
41
|
+
|
42
|
+
# moved to Promise
|
43
|
+
# # Emits an open tag, comprising '<', tag name, optional attributes, and '>'
|
44
|
+
# def open_tag(promise)
|
45
|
+
# output.newline if newliney?(promise._tag_name) && !output.at_line_start?
|
46
|
+
# output << promise._open_tag
|
47
|
+
# output.indent
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
# # Emits a close tag, consisting of '<', '/', tag name, and '>'
|
51
|
+
# def close_tag(promise)
|
52
|
+
# output.undent
|
53
|
+
# output << promise._close_tag
|
54
|
+
# if newliney?(promise._tag_name)
|
55
|
+
# output.newline
|
56
|
+
# end
|
57
|
+
# end
|
58
|
+
#
|
59
|
+
# def inside_tag value, block
|
60
|
+
# if block
|
61
|
+
# block.call
|
62
|
+
# else
|
63
|
+
# text value
|
64
|
+
# end
|
65
|
+
# end
|
66
|
+
|
67
|
+
def _element(tag_name, *args, &block)
|
68
|
+
if args.length > 2
|
69
|
+
raise ArgumentError, "too many args"
|
70
|
+
end
|
71
|
+
attributes, value = nil, nil
|
72
|
+
arg0 = args[0]
|
73
|
+
if arg0.is_a?(Hash)
|
74
|
+
attributes = arg0
|
75
|
+
else
|
76
|
+
value = arg0
|
77
|
+
arg1 = args[1]
|
78
|
+
if arg1.is_a?(Hash)
|
79
|
+
attributes = arg1
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
if block && value
|
84
|
+
raise ArgumentError, "You can't pass both a block and a value to #{tag_name} -- please choose one."
|
85
|
+
end
|
86
|
+
|
87
|
+
attributes ||= {}
|
88
|
+
promise = if !value.nil?
|
89
|
+
Promise.new(output, tag_name, attributes, false, newliney?(tag_name)) do
|
90
|
+
if value.is_a? AbstractWidget
|
91
|
+
widget value
|
92
|
+
else
|
93
|
+
text value
|
94
|
+
end
|
95
|
+
end
|
96
|
+
elsif block
|
97
|
+
Promise.new(output, tag_name, attributes, false, newliney?(tag_name), &block)
|
98
|
+
else
|
99
|
+
Promise.new(output, tag_name, attributes, false, newliney?(tag_name))
|
100
|
+
end
|
101
|
+
promise._render
|
102
|
+
promise
|
103
|
+
end
|
104
|
+
|
105
|
+
def _empty_element(tag_name, attributes={})
|
106
|
+
promise = Promise.new(output, tag_name, attributes, true, newliney?(tag_name))
|
107
|
+
promise._render
|
108
|
+
promise
|
109
|
+
end
|
110
|
+
|
111
|
+
|
112
|
+
end
|
113
|
+
end
|
data/lib/erector/erect/erect.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require "optparse"
|
2
|
-
require "rake"
|
3
2
|
require "erector/erect/erected" # pull this out so we don't recreate the grammar every time
|
4
3
|
|
5
4
|
module Erector
|
@@ -70,23 +69,27 @@ module Erector
|
|
70
69
|
|
71
70
|
#todo: unit test
|
72
71
|
def explode_dirs
|
73
|
-
exploded_files =
|
72
|
+
exploded_files = []
|
74
73
|
files.each do |file|
|
75
74
|
if File.directory?(file)
|
76
|
-
exploded_files
|
75
|
+
exploded_files << explode(file)
|
77
76
|
else
|
78
|
-
exploded_files
|
77
|
+
exploded_files << file
|
79
78
|
end
|
80
79
|
end
|
81
|
-
@files = exploded_files
|
80
|
+
@files = exploded_files.flatten
|
82
81
|
end
|
83
82
|
|
84
83
|
def explode(dir)
|
85
84
|
case mode
|
86
85
|
when :to_erector
|
87
|
-
|
86
|
+
["#{dir}/**/*.rhtml",
|
87
|
+
"#{dir}/**/*.html",
|
88
|
+
"#{dir}/**/*.html.erb"].map do |pattern|
|
89
|
+
Dir.glob pattern
|
90
|
+
end.flatten
|
88
91
|
when :to_html
|
89
|
-
|
92
|
+
Dir.glob "#{dir}/**/*.rb"
|
90
93
|
end
|
91
94
|
end
|
92
95
|
|
data/lib/erector/externals.rb
CHANGED
@@ -87,7 +87,8 @@ module Erector
|
|
87
87
|
output = Erector::Output.new
|
88
88
|
self.to_a(:output => output) # render all the externals onto this new output buffer
|
89
89
|
nested_widgets = output.widgets.to_a
|
90
|
-
|
90
|
+
options_to_external_renderer = {:classes => nested_widgets}.merge(options_to_external_renderer)
|
91
|
+
renderer = ExternalRenderer.new(options_to_external_renderer)
|
91
92
|
externals = renderer.to_a(:output => output)
|
92
93
|
output.to_a
|
93
94
|
end
|
data/lib/erector/html.rb
CHANGED
@@ -1,346 +1,12 @@
|
|
1
|
+
require "erector/element"
|
2
|
+
require "erector/attributes"
|
3
|
+
require "erector/promise"
|
4
|
+
require "erector/text"
|
5
|
+
require "erector/tag"
|
6
|
+
|
1
7
|
module Erector
|
2
8
|
module HTML
|
3
|
-
module ClassMethods
|
4
|
-
# Tags which are always self-closing. Click "[show source]" to see the full list.
|
5
|
-
def empty_tags
|
6
|
-
['area', 'base', 'br', 'col', 'embed', 'frame',
|
7
|
-
'hr', 'img', 'input', 'link', 'meta', 'param']
|
8
|
-
end
|
9
|
-
|
10
|
-
# Tags which can contain other stuff. Click "[show source]" to see the full list.
|
11
|
-
def full_tags
|
12
|
-
[
|
13
|
-
'a', 'abbr', 'acronym', 'address', 'article', 'aside', 'audio',
|
14
|
-
'b', 'bdo', 'big', 'blockquote', 'body', 'button',
|
15
|
-
'canvas', 'caption', 'center', 'cite', 'code', 'colgroup', 'command',
|
16
|
-
'datalist', 'dd', 'del', 'details', 'dfn', 'dialog', 'div', 'dl', 'dt',
|
17
|
-
'em',
|
18
|
-
'fieldset', 'figure', 'footer', 'form', 'frameset',
|
19
|
-
'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'html', 'i',
|
20
|
-
'iframe', 'ins', 'keygen', 'kbd', 'label', 'legend', 'li',
|
21
|
-
'map', 'mark', 'meter',
|
22
|
-
'nav', 'noframes', 'noscript',
|
23
|
-
'object', 'ol', 'optgroup', 'option',
|
24
|
-
'p', 'pre', 'progress',
|
25
|
-
'q', 'ruby', 'rt', 'rp', 's',
|
26
|
-
'samp', 'script', 'section', 'select', 'small', 'source', 'span', 'strike',
|
27
|
-
'strong', 'style', 'sub', 'sup',
|
28
|
-
'table', 'tbody', 'td', 'textarea', 'tfoot',
|
29
|
-
'th', 'thead', 'time', 'title', 'tr', 'tt',
|
30
|
-
'u', 'ul',
|
31
|
-
'var', 'video'
|
32
|
-
]
|
33
|
-
end
|
34
|
-
|
35
|
-
def all_tags
|
36
|
-
full_tags + empty_tags
|
37
|
-
end
|
38
|
-
|
39
|
-
def def_empty_tag_method(tag_name)
|
40
|
-
self.class_eval(<<-SRC, __FILE__, __LINE__ + 1)
|
41
|
-
def #{tag_name}(*args, &block)
|
42
|
-
__empty_element__('#{tag_name}', *args, &block)
|
43
|
-
end
|
44
|
-
SRC
|
45
|
-
end
|
46
|
-
|
47
|
-
def def_full_tag_method(tag_name)
|
48
|
-
self.class_eval(<<-SRC, __FILE__, __LINE__ + 1)
|
49
|
-
def #{tag_name}(*args, &block)
|
50
|
-
__element__(false, '#{tag_name}', *args, &block)
|
51
|
-
end
|
52
|
-
|
53
|
-
def #{tag_name}!(*args, &block)
|
54
|
-
__element__(true, '#{tag_name}', *args, &block)
|
55
|
-
end
|
56
|
-
SRC
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
def self.included(base)
|
61
|
-
base.extend ClassMethods
|
62
|
-
|
63
|
-
base.full_tags.each do |tag_name|
|
64
|
-
base.def_full_tag_method(tag_name)
|
65
|
-
end
|
66
|
-
|
67
|
-
base.empty_tags.each do |tag_name|
|
68
|
-
base.def_empty_tag_method(tag_name)
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
# Internal method used to emit an HTML/XML element, including an open tag,
|
73
|
-
# attributes (optional, via the default hash), contents (also optional),
|
74
|
-
# and close tag.
|
75
|
-
#
|
76
|
-
# Using the arcane powers of Ruby, there are magic methods that call
|
77
|
-
# +element+ for all the standard HTML tags, like +a+, +body+, +p+, and so
|
78
|
-
# forth. Look at the source of #full_tags for the full list.
|
79
|
-
# Unfortunately, this big mojo confuses rdoc, so we can't see each method
|
80
|
-
# in this rdoc page, but trust us, they're there.
|
81
|
-
#
|
82
|
-
# When calling one of these magic methods, put attributes in the default
|
83
|
-
# hash. If there is a string parameter, then it is used as the contents.
|
84
|
-
# If there is a block, then it is executed (yielded), and the string
|
85
|
-
# parameter is ignored. The block will usually be in the scope of the
|
86
|
-
# child widget, which means it has access to all the methods of Widget,
|
87
|
-
# which will eventually end up appending text to the +output+ string. See
|
88
|
-
# how elegant it is? Not confusing at all if you don't think about it.
|
89
|
-
#
|
90
|
-
def element(*args, &block)
|
91
|
-
__element__(false, *args, &block)
|
92
|
-
end
|
93
|
-
|
94
|
-
# Like +element+, but string parameters are not escaped.
|
95
|
-
def element!(*args, &block)
|
96
|
-
__element__(true, *args, &block)
|
97
|
-
end
|
98
|
-
|
99
|
-
# Internal method used to emit a self-closing HTML/XML element, including
|
100
|
-
# a tag name and optional attributes (passed in via the default hash).
|
101
|
-
#
|
102
|
-
# Using the arcane powers of Ruby, there are magic methods that call
|
103
|
-
# +empty_element+ for all the standard HTML tags, like +img+, +br+, and so
|
104
|
-
# forth. Look at the source of #empty_tags for the full list.
|
105
|
-
# Unfortunately, this big mojo confuses rdoc, so we can't see each method
|
106
|
-
# in this rdoc page, but trust us, they're there.
|
107
|
-
#
|
108
|
-
def empty_element(*args, &block)
|
109
|
-
__empty_element__(*args, &block)
|
110
|
-
end
|
111
|
-
|
112
|
-
# Returns an HTML-escaped version of its parameter. Leaves the output
|
113
|
-
# string untouched. This method is idempotent: h(h(text)) will not
|
114
|
-
# double-escape text. This means that it is safe to do something like
|
115
|
-
# text(h("2<4")) -- it will produce "2<4", not "2&lt;4".
|
116
|
-
def h(content)
|
117
|
-
if content.respond_to?(:html_safe?) && content.html_safe?
|
118
|
-
content
|
119
|
-
else
|
120
|
-
raw(CGI.escapeHTML(content.to_s))
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
# Emits an open tag, comprising '<', tag name, optional attributes, and '>'
|
125
|
-
def open_tag(tag_name, attributes={})
|
126
|
-
output.newline if newliney?(tag_name) && !output.at_line_start?
|
127
|
-
output << raw("<#{tag_name}#{format_attributes(attributes)}>")
|
128
|
-
output.indent
|
129
|
-
end
|
130
|
-
|
131
|
-
# Emits a close tag, consisting of '<', '/', tag name, and '>'
|
132
|
-
def close_tag(tag_name)
|
133
|
-
output.undent
|
134
|
-
output << raw("</#{tag_name}>")
|
135
|
-
if newliney?(tag_name)
|
136
|
-
output.newline
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
|
-
# Returns text which will *not* be HTML-escaped.
|
141
|
-
def raw(value)
|
142
|
-
RawString.new(value.to_s)
|
143
|
-
end
|
144
|
-
|
145
|
-
# Emits text. If a string is passed in, it will be HTML-escaped. If the
|
146
|
-
# result of calling methods such as raw is passed in, the HTML will not be
|
147
|
-
# HTML-escaped again. If another kind of object is passed in, the result
|
148
|
-
# of calling its to_s method will be treated as a string would be.
|
149
|
-
#
|
150
|
-
# You shouldn't pass a widget in to this method, as that will cause
|
151
|
-
# performance problems (as well as being semantically goofy). Use the
|
152
|
-
# #widget method instead.
|
153
|
-
def text(value)
|
154
|
-
if value.is_a? Widget
|
155
|
-
widget value
|
156
|
-
else
|
157
|
-
output << h(value)
|
158
|
-
end
|
159
|
-
nil
|
160
|
-
end
|
161
|
-
|
162
|
-
# Emits text which will *not* be HTML-escaped. Same effect as text(raw(s))
|
163
|
-
def text!(value)
|
164
|
-
text raw(value)
|
165
|
-
end
|
166
|
-
|
167
|
-
alias rawtext text!
|
168
|
-
|
169
|
-
# Returns a copy of value with spaces replaced by non-breaking space characters.
|
170
|
-
# With no arguments, return a single non-breaking space.
|
171
|
-
# The output uses the escaping format ' ' since that works
|
172
|
-
# in both HTML and XML (as opposed to ' ' which only works in HTML).
|
173
|
-
def nbsp(value = " ")
|
174
|
-
raw(h(value).gsub(/ /,' '))
|
175
|
-
end
|
176
|
-
|
177
|
-
# Return a character given its unicode code point or unicode name.
|
178
|
-
def character(code_point_or_name)
|
179
|
-
if code_point_or_name.is_a?(Symbol)
|
180
|
-
require "erector/unicode"
|
181
|
-
found = Erector::CHARACTERS[code_point_or_name]
|
182
|
-
if found.nil?
|
183
|
-
raise "Unrecognized character #{code_point_or_name}"
|
184
|
-
end
|
185
|
-
raw("&#x#{sprintf '%x', found};")
|
186
|
-
elsif code_point_or_name.is_a?(Integer)
|
187
|
-
raw("&#x#{sprintf '%x', code_point_or_name};")
|
188
|
-
else
|
189
|
-
raise "Unrecognized argument to character: #{code_point_or_name}"
|
190
|
-
end
|
191
|
-
end
|
192
|
-
|
193
|
-
# Emits an XML instruction, which looks like this: <?xml version=\"1.0\" encoding=\"UTF-8\"?>
|
194
|
-
def instruct(attributes={:version => "1.0", :encoding => "UTF-8"})
|
195
|
-
output << raw("<?xml#{format_sorted(sort_for_xml_declaration(attributes))}?>")
|
196
|
-
end
|
197
|
-
|
198
|
-
# Emits an HTML comment (<!-- ... -->) surrounding +text+ and/or the output of +block+.
|
199
|
-
# see http://www.w3.org/TR/html4/intro/sgmltut.html#h-3.2.4
|
200
|
-
#
|
201
|
-
# If +text+ is an Internet Explorer conditional comment condition such as "[if IE]",
|
202
|
-
# the output includes the opening condition and closing "[endif]". See
|
203
|
-
# http://www.quirksmode.org/css/condcom.html
|
204
|
-
#
|
205
|
-
# Since "Authors should avoid putting two or more adjacent hyphens inside comments,"
|
206
|
-
# we emit a warning if you do that.
|
207
|
-
def comment(text = '')
|
208
|
-
puts "Warning: Authors should avoid putting two or more adjacent hyphens inside comments." if text =~ /--/
|
209
|
-
|
210
|
-
conditional = text =~ /\[if .*\]/
|
211
|
-
|
212
|
-
rawtext "<!--"
|
213
|
-
rawtext text
|
214
|
-
rawtext ">" if conditional
|
215
|
-
|
216
|
-
if block_given?
|
217
|
-
rawtext "\n"
|
218
|
-
yield
|
219
|
-
rawtext "\n"
|
220
|
-
end
|
221
|
-
|
222
|
-
rawtext "<![endif]" if conditional
|
223
|
-
rawtext "-->\n"
|
224
|
-
end
|
225
|
-
|
226
|
-
# Emits a javascript block inside a +script+ tag, wrapped in CDATA
|
227
|
-
# doohickeys like all the cool JS kids do.
|
228
|
-
def javascript(value = nil, attributes = {})
|
229
|
-
if value.is_a?(Hash)
|
230
|
-
attributes = value
|
231
|
-
value = nil
|
232
|
-
elsif block_given? && value
|
233
|
-
raise ArgumentError, "You can't pass both a block and a value to javascript -- please choose one."
|
234
|
-
end
|
235
|
-
|
236
|
-
script(attributes.merge(:type => "text/javascript")) do
|
237
|
-
# Shouldn't this be a "cdata" HtmlPart?
|
238
|
-
# (maybe, but the syntax is specific to javascript; it isn't
|
239
|
-
# really a generic XML CDATA section. Specifically,
|
240
|
-
# ]]> within value is not treated as ending the
|
241
|
-
# CDATA section by Firefox2 when parsing text/html,
|
242
|
-
# although I guess we could refuse to generate ]]>
|
243
|
-
# there, for the benefit of XML/XHTML parsers).
|
244
|
-
output << raw("\n// <![CDATA[\n")
|
245
|
-
if block_given?
|
246
|
-
yield
|
247
|
-
else
|
248
|
-
output << raw(value)
|
249
|
-
end
|
250
|
-
output << raw("\n// ]]>")
|
251
|
-
output.append_newline # this forces a newline even if we're not in pretty mode
|
252
|
-
end
|
253
|
-
|
254
|
-
output << raw("\n")
|
255
|
-
end
|
256
|
-
|
257
|
-
protected
|
258
|
-
def __element__(raw, tag_name, *args, &block)
|
259
|
-
if args.length > 2
|
260
|
-
raise ArgumentError, "Cannot accept more than four arguments"
|
261
|
-
end
|
262
|
-
attributes, value = nil, nil
|
263
|
-
arg0 = args[0]
|
264
|
-
if arg0.is_a?(Hash)
|
265
|
-
attributes = arg0
|
266
|
-
else
|
267
|
-
value = arg0
|
268
|
-
arg1 = args[1]
|
269
|
-
if arg1.is_a?(Hash)
|
270
|
-
attributes = arg1
|
271
|
-
end
|
272
|
-
end
|
273
|
-
attributes ||= {}
|
274
|
-
open_tag tag_name, attributes
|
275
|
-
begin
|
276
|
-
if block && value
|
277
|
-
raise ArgumentError, "You can't pass both a block and a value to #{tag_name} -- please choose one."
|
278
|
-
end
|
279
|
-
if block
|
280
|
-
block.call
|
281
|
-
elsif raw
|
282
|
-
text! value
|
283
|
-
else
|
284
|
-
text value
|
285
|
-
end
|
286
|
-
ensure
|
287
|
-
close_tag tag_name
|
288
|
-
end
|
289
|
-
end
|
290
|
-
|
291
|
-
def __empty_element__(tag_name, attributes={})
|
292
|
-
output << raw("<#{tag_name}#{format_attributes(attributes)} />")
|
293
|
-
output.newline if newliney?(tag_name)
|
294
|
-
end
|
295
|
-
|
296
|
-
def format_attributes(attributes)
|
297
|
-
if !attributes || attributes.empty?
|
298
|
-
""
|
299
|
-
else
|
300
|
-
format_sorted(sorted(attributes))
|
301
|
-
end
|
302
|
-
end
|
303
|
-
|
304
|
-
def format_sorted(sorted)
|
305
|
-
results = ['']
|
306
|
-
sorted.each do |key, value|
|
307
|
-
if value
|
308
|
-
if value.is_a?(Array)
|
309
|
-
value = value.flatten
|
310
|
-
next if value.empty?
|
311
|
-
value = value.join(' ')
|
312
|
-
end
|
313
|
-
results << "#{key}=\"#{h(value)}\""
|
314
|
-
end
|
315
|
-
end
|
316
|
-
results.join(' ')
|
317
|
-
end
|
318
|
-
|
319
|
-
def sorted(attributes)
|
320
|
-
stringized = []
|
321
|
-
attributes.each do |key, value|
|
322
|
-
stringized << [key.to_s, value]
|
323
|
-
end
|
324
|
-
stringized.sort
|
325
|
-
end
|
326
|
-
|
327
|
-
def sort_for_xml_declaration(attributes)
|
328
|
-
# correct order is "version, encoding, standalone" (XML 1.0 section 2.8).
|
329
|
-
# But we only try to put version before encoding for now.
|
330
|
-
stringized = []
|
331
|
-
attributes.each do |key, value|
|
332
|
-
stringized << [key.to_s, value]
|
333
|
-
end
|
334
|
-
stringized.sort{|a, b| b <=> a}
|
335
|
-
end
|
336
9
|
|
337
|
-
NON_NEWLINEY = {'i' => true, 'b' => true, 'small' => true,
|
338
|
-
'img' => true, 'span' => true, 'a' => true,
|
339
|
-
'input' => true, 'textarea' => true, 'button' => true, 'select' => true
|
340
|
-
}
|
341
10
|
|
342
|
-
def newliney?(tag_name)
|
343
|
-
!NON_NEWLINEY.include?(tag_name)
|
344
|
-
end
|
345
11
|
end
|
346
12
|
end
|