erector 0.8.3 → 0.9.0.pre1
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.
- 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
|