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.
Files changed (115) hide show
  1. data/Gemfile +21 -0
  2. data/Rakefile +171 -0
  3. data/VERSION.yml +3 -2
  4. data/lib/erector.rb +3 -5
  5. data/lib/erector/abstract_widget.rb +76 -31
  6. data/lib/erector/attributes.rb +34 -0
  7. data/lib/erector/caching.rb +1 -0
  8. data/lib/erector/convenience.rb +12 -4
  9. data/lib/erector/dependency.rb +2 -1
  10. data/lib/erector/element.rb +113 -0
  11. data/lib/erector/erect/erect.rb +10 -7
  12. data/lib/erector/externals.rb +2 -1
  13. data/lib/erector/html.rb +6 -340
  14. data/lib/erector/html_widget.rb +300 -0
  15. data/lib/erector/inline.rb +1 -1
  16. data/lib/erector/output.rb +39 -12
  17. data/lib/erector/promise.rb +137 -0
  18. data/lib/erector/rails2/extensions/rails_widget.rb +1 -1
  19. data/lib/erector/rails3.rb +1 -1
  20. data/lib/erector/sass.rb +14 -19
  21. data/lib/erector/tag.rb +65 -0
  22. data/lib/erector/text.rb +123 -0
  23. data/lib/erector/version.rb +1 -1
  24. data/lib/erector/widget.rb +52 -12
  25. data/lib/erector/widgets/page.rb +12 -10
  26. data/lib/erector/xml_widget.rb +131 -0
  27. data/spec/erector/caching_spec.rb +1 -0
  28. data/spec/erector/externals_spec.rb +0 -1
  29. data/spec/erector/html_spec.rb +9 -25
  30. data/spec/erector/output_spec.rb +102 -9
  31. data/spec/erector/promise_spec.rb +173 -0
  32. data/spec/erector/sass_spec.rb +1 -1
  33. data/spec/erector/tag_spec.rb +67 -0
  34. data/spec/erector/widget_spec.rb +53 -2
  35. data/spec/erector/xml_widget_spec.rb +74 -0
  36. data/spec/rails2/rails_app/Gemfile +1 -0
  37. data/spec/rails2/rails_app/spec/rails_spec_helper.rb +2 -0
  38. data/spec/rails_root/Gemfile +11 -0
  39. data/spec/rails_root/README +256 -0
  40. data/spec/rails_root/Rakefile +7 -0
  41. data/spec/rails_root/app/controllers/application.rb +6 -0
  42. data/spec/rails_root/app/controllers/application_controller.rb +3 -0
  43. data/spec/rails_root/app/helpers/application_helper.rb +2 -0
  44. data/spec/rails_root/app/views/layouts/application.html.erb +14 -0
  45. data/spec/rails_root/app/views/test/_erb.erb +1 -0
  46. data/spec/rails_root/app/views/test/_erector.rb +5 -0
  47. data/spec/rails_root/app/views/test/_partial_with_locals.rb +7 -0
  48. data/spec/rails_root/app/views/test/bare.rb +5 -0
  49. data/spec/rails_root/app/views/test/erb_from_erector.html.rb +5 -0
  50. data/spec/rails_root/app/views/test/erector_from_erb.html.erb +1 -0
  51. data/spec/rails_root/app/views/test/erector_with_locals_from_erb.html.erb +6 -0
  52. data/spec/rails_root/app/views/test/implicit_assigns.html.rb +5 -0
  53. data/spec/rails_root/app/views/test/needs.html.rb +7 -0
  54. data/spec/rails_root/app/views/test/needs_subclass.html.rb +5 -0
  55. data/spec/rails_root/app/views/test/protected_instance_variable.html.rb +5 -0
  56. data/spec/rails_root/app/views/test/render_default.html.rb +5 -0
  57. data/spec/rails_root/app/views/test/render_partial.html.rb +5 -0
  58. data/spec/rails_root/config.ru +4 -0
  59. data/spec/rails_root/config/application.rb +42 -0
  60. data/spec/rails_root/config/boot.rb +13 -0
  61. data/spec/rails_root/config/database.yml +22 -0
  62. data/spec/rails_root/config/environment.rb +5 -0
  63. data/spec/rails_root/config/environments/development.rb +22 -0
  64. data/spec/rails_root/config/environments/production.rb +49 -0
  65. data/spec/rails_root/config/environments/test.rb +35 -0
  66. data/spec/rails_root/config/initializers/backtrace_silencers.rb +7 -0
  67. data/spec/rails_root/config/initializers/inflections.rb +10 -0
  68. data/spec/rails_root/config/initializers/mime_types.rb +5 -0
  69. data/spec/rails_root/config/initializers/secret_token.rb +7 -0
  70. data/spec/rails_root/config/initializers/session_store.rb +8 -0
  71. data/spec/rails_root/config/locales/en.yml +5 -0
  72. data/spec/rails_root/config/routes.rb +58 -0
  73. data/spec/rails_root/db/seeds.rb +7 -0
  74. data/spec/rails_root/doc/README_FOR_APP +2 -0
  75. data/spec/rails_root/log/development.log +17 -0
  76. data/spec/rails_root/log/test.log +3750 -0
  77. data/spec/rails_root/public/404.html +26 -0
  78. data/spec/rails_root/public/422.html +26 -0
  79. data/spec/rails_root/public/500.html +26 -0
  80. data/spec/rails_root/public/dispatch.cgi +10 -0
  81. data/spec/rails_root/public/dispatch.fcgi +24 -0
  82. data/spec/rails_root/public/dispatch.rb +10 -0
  83. data/spec/rails_root/public/favicon.ico +0 -0
  84. data/spec/rails_root/public/images/rails.png +0 -0
  85. data/spec/rails_root/public/index.html +262 -0
  86. data/spec/rails_root/public/javascripts/application.js +2 -0
  87. data/spec/rails_root/public/javascripts/controls.js +965 -0
  88. data/spec/rails_root/public/javascripts/dragdrop.js +974 -0
  89. data/spec/rails_root/public/javascripts/effects.js +1123 -0
  90. data/spec/rails_root/public/javascripts/prototype.js +6001 -0
  91. data/spec/rails_root/public/javascripts/rails.js +175 -0
  92. data/spec/rails_root/public/robots.txt +5 -0
  93. data/spec/rails_root/script/about +3 -0
  94. data/spec/rails_root/script/console +3 -0
  95. data/spec/rails_root/script/destroy +3 -0
  96. data/spec/rails_root/script/generate +3 -0
  97. data/spec/rails_root/script/performance/benchmarker +3 -0
  98. data/spec/rails_root/script/performance/profiler +3 -0
  99. data/spec/rails_root/script/performance/request +3 -0
  100. data/spec/rails_root/script/plugin +3 -0
  101. data/spec/rails_root/script/process/inspector +3 -0
  102. data/spec/rails_root/script/process/reaper +3 -0
  103. data/spec/rails_root/script/process/spawner +3 -0
  104. data/spec/rails_root/script/rails +6 -0
  105. data/spec/rails_root/script/runner +3 -0
  106. data/spec/rails_root/script/server +3 -0
  107. data/spec/rails_root/spec/form_builder_spec.rb +21 -0
  108. data/spec/rails_root/spec/rails_helpers_spec.rb +220 -0
  109. data/spec/rails_root/spec/rails_spec_helper.rb +10 -0
  110. data/spec/rails_root/spec/rails_widget_spec.rb +83 -0
  111. data/spec/rails_root/spec/render_spec.rb +298 -0
  112. data/spec/rails_root/test/performance/browsing_test.rb +9 -0
  113. data/spec/rails_root/test/test_helper.rb +13 -0
  114. data/spec/spec_helper.rb +3 -1
  115. metadata +202 -66
@@ -73,6 +73,7 @@ module Erector
73
73
  cache && block.nil? && self.class.cachable?
74
74
  end
75
75
 
76
+ protected
76
77
  def _render(options = {})
77
78
  if should_cache?
78
79
  cache[self.class, assigns, options[:content_method_name]] ||= super
@@ -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
- to_html(options.merge(:prettyprint => true))
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 |widget_or_text|
28
+ array.each do |item|
29
29
  if !first
30
- text separator
30
+ if separator.is_a? Widget
31
+ widget separator
32
+ else
33
+ text separator
34
+ end
31
35
  end
32
36
  first = false
33
- text widget_or_text
37
+ if item.is_a? Widget
38
+ widget item
39
+ else
40
+ text item
41
+ end
34
42
  end
35
43
  end
36
44
 
@@ -23,7 +23,8 @@ module Erector
23
23
  end
24
24
 
25
25
  def hash
26
- # this is a fairly inefficient hash function but it does the trick for now
26
+ # this is a fairly inefficient hash function but it does the trick for
27
+ # now
27
28
  "#{type}#{text}#{options}".hash
28
29
  end
29
30
  end
@@ -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
@@ -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 = FileList.new
72
+ exploded_files = []
74
73
  files.each do |file|
75
74
  if File.directory?(file)
76
- exploded_files.add(explode(file))
75
+ exploded_files << explode(file)
77
76
  else
78
- exploded_files.add(file)
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
- FileList["#{dir}/**/*.rhtml", "#{dir}/**/*.html", "#{dir}/**/*.html.erb"]
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
- FileList["#{dir}/**/*.rb"]
92
+ Dir.glob "#{dir}/**/*.rb"
90
93
  end
91
94
  end
92
95
 
@@ -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
- renderer = ExternalRenderer.new({:classes => nested_widgets}.merge(options_to_external_renderer))
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
@@ -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&lt;4", not "2&amp;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 '&#160;' since that works
172
- # in both HTML and XML (as opposed to '&nbsp;' which only works in HTML).
173
- def nbsp(value = " ")
174
- raw(h(value).gsub(/ /,'&#160;'))
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 (&lt;!-- ... --&gt;) 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