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.
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
@@ -45,7 +45,7 @@ module Erector
45
45
 
46
46
  output_buffer = view.with_output_buffer do
47
47
  # Set parent to the view and use Rails's output buffer.
48
- new_output = Output.new { view.output_buffer }
48
+ new_output = Output.new :buffer => lambda { view.output_buffer }
49
49
  widget.to_html(options.merge(:parent => view,
50
50
  :output => new_output))
51
51
  end
@@ -39,7 +39,7 @@ module Erector
39
39
  # Set parent and helpers to the view and use Rails's output buffer.
40
40
  widget.to_html(options.merge(:helpers => view,
41
41
  :parent => view,
42
- :output => Output.new { view.output_buffer }))
42
+ :output => Output.new(:buffer => lambda { view.output_buffer })))
43
43
  end
44
44
  end
45
45
 
@@ -1,22 +1,17 @@
1
-
2
- if Object.const_defined?(:Sass)
3
- module Erector
4
- # Adds sass support to Erector widgets.
5
- # Note that sass is provided inside the gem named "haml",
6
- # not the gem named "sass".
7
- # To get sass support into your Erector project, install the
8
- # haml gem -- see http://sass-lang.com/download.html -- and
9
- # then do something like this:
10
- # require 'rubygems'
11
- # require 'sass'
12
- #
13
- # Current support is barebones. Please offer suggestions (or better
14
- # yet, patches) for whether and how to support, e.g., caching,
15
- # loading from files, precompilation, etc.
16
- module Sass
17
- def sass(sass_text)
18
- style ::Sass::Engine.new(sass_text, :cache => false).render
19
- end
1
+ module Erector
2
+ # Adds sass support to Erector widgets.
3
+ #
4
+ # Sass is an *optional dependency* of the Erector gem, so
5
+ # a call to +sass+ inside a widget will fail unless you have already
6
+ # installed the sass gem (e.g. "gem 'sass'" in your code or Gemfile).
7
+ #
8
+ # Current support is barebones. Please offer suggestions (or better
9
+ # yet, patches) for whether and how to support, e.g., caching,
10
+ # loading from files, precompilation, etc.
11
+ module Sass
12
+ def sass(sass_text)
13
+ require "sass"
14
+ style ::Sass::Engine.new(sass_text, :cache => false).render
20
15
  end
21
16
  end
22
17
  end
@@ -0,0 +1,65 @@
1
+ module Erector
2
+
3
+ # Defines a type of tag (not an actual element with attributes and contents)
4
+ class Tag
5
+
6
+ # Pass the self_closing and inline params as symbols, e.g.
7
+ #
8
+ # Tag.new("i", :inline)
9
+ # Tag.new("input", :inline, :self_closing)
10
+ #
11
+ # @param name the name of the tag, e.g. "div"
12
+ # @param self_closing whether it can (false) or cannot (true) contain text or other elements. Default: false
13
+ # @param inline whether it should appear in line with other elements (true) or on a line by itself (false) in pretty mode. Default: false
14
+ # @param snake whether to covert the method name into "snake case" (aka underscorized). Default: false
15
+ #
16
+ def initialize(name, *params)
17
+ @name = name.to_s
18
+ @method_name = if params.first.is_a? String
19
+ params.shift
20
+ else
21
+ @name
22
+ end
23
+ @self_closing = params.include?(:self_closing)
24
+ @inline = params.include?(:inline)
25
+ @method_name = snake_case(@method_name) if params.include?(:snake_case)
26
+ end
27
+
28
+ attr_reader :name, :method_name
29
+
30
+ def self_closing?
31
+ @self_closing
32
+ end
33
+
34
+ def newliney?
35
+ !@inline
36
+ end
37
+
38
+ def inline?
39
+ @inline
40
+ end
41
+
42
+ ##
43
+ # Convert to snake case.
44
+ #
45
+ # "FooBar".snake_case #=> "foo_bar"
46
+ # "HeadlineCNNNews".snake_case #=> "headline_cnn_news"
47
+ # "CNN".snake_case #=> "cnn"
48
+ #
49
+ # @return [String] Receiver converted to snake case.
50
+ #
51
+ # @api public
52
+ # borrowed from https://github.com/datamapper/extlib/blob/master/lib/extlib/string.rb
53
+ def snake_case(s)
54
+ if s.match(/\A[A-Z]+\z/)
55
+ s.downcase
56
+ else
57
+ s.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').
58
+ gsub(/([a-z])([A-Z])/, '\1_\2').
59
+ downcase
60
+ end
61
+ end
62
+
63
+ end
64
+
65
+ end
@@ -0,0 +1,123 @@
1
+ require "erector/raw_string"
2
+
3
+ module Erector
4
+ module Text
5
+ # Emits text to the output buffer, e.g.
6
+ #
7
+ # text "my dog smells awful"
8
+ # => "my dog smells awful"
9
+ #
10
+ # If a string is passed in, it will be HTML-escaped. If the
11
+ # result of calling methods such as raw is passed in, the HTML will not be
12
+ # HTML-escaped again. If another kind of object is passed in, the result
13
+ # of calling its to_s method will be treated as a string would be.
14
+ #
15
+ # You shouldn't pass a widget in to this method, as that will cause
16
+ # performance problems (as well as being semantically goofy). Use the
17
+ # #widget method instead.
18
+ #
19
+ # You may pass a series of values (i.e. varargs). In that case, each value
20
+ # will be emitted to the output stream in turn. You can specify a delimiter
21
+ # by using an options hash with as the final argument, using +:join+ as the key,
22
+ # e.g.
23
+ #
24
+ # text "my", "dog", "smells", :join => " "
25
+ # => "my dog smells"
26
+ #
27
+ # You may also pass a Promise as a parameter; every tag
28
+ # method now returns a Promise after emitting. This allows
29
+ # you to easily embed simple HTML formatting into a sentence, e.g.
30
+ #
31
+ # text "my", "dog", "smells", b("great!"), :join => " "
32
+ # => "my dog smells <b>great!</b>"
33
+ #
34
+ # (Yes, the initial call to +b+ emits "\&lt;b>great\&lt;/b>" to the output buffer;
35
+ # the Promise feature takes care of rewinding and rewriting the output
36
+ # buffer during the later call to +text+.)
37
+ #
38
+ def text(*values)
39
+ options = if values.last.is_a? Hash
40
+ values.pop
41
+ else
42
+ {}
43
+ end
44
+ delimiter = options[:join]
45
+
46
+ values.select{|value| value.is_a? Promise}.each do |promise|
47
+ # erase whatever the promises wrote already
48
+ promise._rewind
49
+ end
50
+
51
+ first = true
52
+ values.each do |value|
53
+ if !first and delimiter
54
+ output << h(delimiter)
55
+ end
56
+ first = false
57
+
58
+ case value
59
+ when AbstractWidget
60
+ # todo: better deprecation
61
+ raise "Don't pass a widget to the text method. Use the widget method instead."
62
+ when Promise
63
+ value._mark # so the promise's rewind won't erase anything
64
+ value._render # render the promise to the output stream again
65
+ # note: we could let the promise cache its first effort, but
66
+ # here I think it's better to optimize for memory over speed
67
+ else
68
+ output << h(value)
69
+ end
70
+ end
71
+ nil
72
+ end
73
+
74
+ # Returns text which will *not* be HTML-escaped.
75
+ def raw(value)
76
+ RawString.new(value.to_s)
77
+ end
78
+
79
+ # Emits text which will *not* be HTML-escaped. Same effect as text(raw(s))
80
+ def text!(value)
81
+ text raw(value)
82
+ end
83
+
84
+ alias rawtext text!
85
+
86
+ # Returns a copy of value with spaces replaced by non-breaking space characters.
87
+ # With no arguments, return a single non-breaking space.
88
+ # The output uses the escaping format '&#160;' since that works
89
+ # in both HTML and XML (as opposed to '&nbsp;' which only works in HTML).
90
+ def nbsp(value = " ")
91
+ raw(h(value).gsub(/ /,'&#160;'))
92
+ end
93
+
94
+ # Returns an HTML-escaped version of its parameter. Leaves the output
95
+ # string untouched. This method is idempotent: h(h(text)) will not
96
+ # double-escape text. This means that it is safe to do something like
97
+ # text(h("2<4")) -- it will produce "2&lt;4", not "2&amp;lt;4".
98
+ def h(content)
99
+ if content.respond_to?(:html_safe?) && content.html_safe?
100
+ content
101
+ else
102
+ raw(CGI.escapeHTML(content.to_s))
103
+ end
104
+ end
105
+
106
+ # Return a character given its unicode code point or unicode name.
107
+ def character(code_point_or_name)
108
+ if code_point_or_name.is_a?(Symbol)
109
+ require "erector/unicode"
110
+ found = Erector::CHARACTERS[code_point_or_name]
111
+ if found.nil?
112
+ raise "Unrecognized character #{code_point_or_name}"
113
+ end
114
+ raw("&#x#{sprintf '%x', found};")
115
+ elsif code_point_or_name.is_a?(Integer)
116
+ raw("&#x#{sprintf '%x', code_point_or_name};")
117
+ else
118
+ raise "Unrecognized argument to character: #{code_point_or_name}"
119
+ end
120
+ end
121
+
122
+ end
123
+ end
@@ -6,7 +6,7 @@ module Erector
6
6
  if !Erector.const_defined?(:VERSION)
7
7
  dir = File.dirname(__FILE__)
8
8
  version = YAML.load_file(File.expand_path("#{dir}/../../VERSION.yml"))
9
- VERSION = "#{version[:major]}.#{version[:minor]}.#{version[:patch]}"
9
+ VERSION = [version[:major], version[:minor], version[:patch], version[:build]].compact.join('.')
10
10
  end
11
11
  end
12
12
 
@@ -1,5 +1,13 @@
1
+ require "erector/element"
2
+ require "erector/attributes"
3
+ require "erector/promise"
4
+ require "erector/text"
5
+ require "erector/tag"
6
+ require "erector/html_widget"
7
+ require "erector/needs"
8
+
1
9
  module Erector
2
-
10
+
3
11
  # A Widget is the center of the Erector universe.
4
12
  #
5
13
  # To create a widget, extend Erector::Widget and implement the +content+
@@ -36,19 +44,51 @@ module Erector
36
44
  #
37
45
  # This class extends AbstractWidget and includes several modules,
38
46
  # so be sure to check all of those places for API documentation for the
39
- # various methods of Widget. Also read the API Cheatsheet in the user guide
40
- # at http://erector.rubyforge.org/userguide#apicheatsheet
47
+ # various methods of Widget:
48
+ #
49
+ # * AbstractWidget
50
+ # * Element
51
+ # * Attributes
52
+ # * Text
53
+ # * Needs
54
+ # * Caching
55
+ # * Externals
56
+ # * AfterInitialize
41
57
  #
42
- # Now, seriously, after playing around a bit, go read the user guide. It's
43
- # fun!
44
- class Widget < AbstractWidget
45
- include Erector::HTML
46
- include Erector::Needs
47
- include Erector::Caching
48
- include Erector::Externals
49
- include Erector::Convenience
58
+ # * HTML
59
+ # * Convenience
60
+ # * JQuery
61
+ # * Sass
62
+ #
63
+ # Also read the API Cheatsheet in the user guide
64
+ # at http://erector.rubyforge.org/userguide#apicheatsheet
65
+ class Widget < HTMLWidget
66
+
67
+ # for some reason these need to be included in Widget and not AbstractWidget
68
+ include Needs
69
+ include Caching
70
+ include Externals
71
+
72
+ include HTML
73
+ include Convenience
50
74
  include Erector::JQuery
51
- include Erector::AfterInitialize
52
75
  include Erector::Sass if Object.const_defined?(:Sass)
76
+
77
+ # alias for AbstractWidget#render
78
+ def to_html(options = {})
79
+ raise "Erector::Widget#to_html takes an options hash, not a symbol. Try calling \"to_html(:content_method_name=> :#{options})\"" if options.is_a? Symbol
80
+ _render(options).to_s
81
+ end
82
+
83
+ # alias for #to_html
84
+ # @deprecated Please use {#to_html} instead
85
+ def to_s(*args)
86
+ unless defined? @@already_warned_to_s
87
+ $stderr.puts "Erector::Widget#to_s is deprecated. Please use #to_html instead. Called from #{caller.first}"
88
+ @@already_warned_to_s = true
89
+ end
90
+ to_html(*args)
91
+ end
92
+
53
93
  end
54
94
  end
@@ -7,14 +7,15 @@
7
7
  # declare it.
8
8
  #
9
9
  # The script and style declarations are accumulated at class load time, as
10
- # 'dependencies'. This technique allows all widgets to add their own requirements
11
- # to the page header without extra logic for declaring which pages include
12
- # which nested widgets. Fortunately, Page is now smart enough to figure out
13
- # which widgets were actually rendered during the body_content run, so it only
14
- # emits into its HEAD the dependencies that are relevant. If it misses some, or
15
- # if you want to add some extra dependencies -- for instance, styles that apply
16
- # to widgets that are rendered later via AJAX -- then return an array of those
17
- # widget classes in your subclass by overriding the #extra_widgets method.
10
+ # 'dependencies'. This technique allows all widgets to add their own
11
+ # requirements to the page header without extra logic for declaring which
12
+ # pages include which nested widgets. Fortunately, Page is now smart enough to
13
+ # figure out which widgets were actually rendered during the body_content run,
14
+ # so it only emits into its HEAD the dependencies that are relevant. If it
15
+ # misses some, or if you want to add some extra dependencies -- for instance,
16
+ # styles that apply to widgets that are rendered later via AJAX -- then return
17
+ # an array of those widget classes in your subclass by overriding the
18
+ # #extra_widgets method.
18
19
  #
19
20
  # If you want something to show up in the headers for just one page type
20
21
  # (subclass), then override #head_content, call super, and then emit it
@@ -93,8 +94,9 @@
93
94
  # end
94
95
  #
95
96
  # = Thoughts:
96
- # * It may be desirable to unify #js and #script, and #css and #style, and have the routine be
97
- # smart enough to analyze its parameter to decide whether to make it a file or a script.
97
+ # * It may be desirable to unify #js and #script, and #css and #style, and
98
+ # have the routine be smart enough to analyze its parameter to decide
99
+ # whether to make it a file or a script.
98
100
  #
99
101
  class Erector::Widgets::Page < Erector::InlineWidget
100
102
 
@@ -0,0 +1,131 @@
1
+ require 'erector/abstract_widget'
2
+ require 'erector/tag'
3
+ require 'erector/needs'
4
+
5
+ module Erector
6
+
7
+ # Abstract base class for XML Widgets and HTMLWidget.
8
+ # Declares "tags" which define methods that emit tags.
9
+ class XMLWidget < AbstractWidget
10
+ include Needs
11
+
12
+ def self.tag_named tag_name, checked = []
13
+ @tags ||= {}
14
+ @tags[tag_name] || begin
15
+ tag = nil
16
+ checked << self
17
+ taggy_ancestors = (ancestors - checked).select{|k| k.respond_to? :tag_named}
18
+ taggy_ancestors.each do |k|
19
+ tag = k.tag_named(tag_name, checked)
20
+ if tag
21
+ @tags[tag_name] = tag
22
+ break
23
+ end
24
+ end
25
+ tag
26
+ end
27
+ end
28
+
29
+ def self.tag *args
30
+ tag = Tag.new(*args)
31
+ @tags ||= {}
32
+ @tags[tag.name] = tag
33
+
34
+ if instance_methods.include?(tag.method_name.to_sym)
35
+ warn "method '#{tag.method_name}' is already defined; skipping #{caller[1]}"
36
+ return
37
+ end
38
+
39
+ if tag.self_closing?
40
+ self.class_eval(<<-SRC, __FILE__, __LINE__ + 1)
41
+ def #{tag.method_name}(*args, &block)
42
+ _empty_element('#{tag.name}', *args, &block)
43
+ end
44
+ SRC
45
+ else
46
+ self.class_eval(<<-SRC, __FILE__, __LINE__ + 1)
47
+ def #{tag.method_name}(*args, &block)
48
+ _element('#{tag.name}', *args, &block)
49
+ end
50
+
51
+ def #{tag.method_name}!(*args, &block)
52
+ _element('#{tag.name}', *(args.map{|a|raw(a)}), &block)
53
+ end
54
+ SRC
55
+ end
56
+ end
57
+
58
+ # Tags which are always self-closing
59
+ def self.self_closing_tags
60
+ @tags.values.select{|tag| tag.self_closing?}.map{|tag| tag.name}
61
+ end
62
+
63
+ # Tags which can contain other stuff
64
+ def self.full_tags
65
+ @tags.values.select{|tag| !tag.self_closing?}.map{|tag| tag.name}
66
+ end
67
+
68
+ def newliney?(tag_name)
69
+ tag = self.class.tag_named tag_name
70
+ if tag
71
+ tag.newliney?
72
+ else
73
+ true
74
+ end
75
+ end
76
+
77
+ # Emits an XML instruction, which looks like this: <?xml version=\"1.0\" encoding=\"UTF-8\" ?>
78
+ def instruct(attributes={:version => "1.0", :encoding => "UTF-8"})
79
+ output << raw("<?xml#{format_sorted(sort_for_xml_declaration(attributes))} ?>")
80
+ end
81
+
82
+ # Emits an XML/HTML comment (&lt;!-- ... --&gt;) surrounding +text+ and/or
83
+ # the output of +block+. see
84
+ # http://www.w3.org/TR/html4/intro/sgmltut.html#h-3.2.4
85
+ #
86
+ # If +text+ is an Internet Explorer conditional comment condition such as
87
+ # "[if IE]", the output includes the opening condition and closing
88
+ # "[endif]". See http://www.quirksmode.org/css/condcom.html
89
+ #
90
+ # Since "Authors should avoid putting two or more adjacent hyphens inside
91
+ # comments," we emit a warning if you do that.
92
+ def comment(text = '')
93
+ puts "Warning: Authors should avoid putting two or more adjacent hyphens inside comments." if text =~ /--/
94
+
95
+ conditional = text =~ /\[if .*\]/
96
+
97
+ rawtext "<!--"
98
+ rawtext text
99
+ rawtext ">" if conditional
100
+
101
+ if block_given?
102
+ rawtext "\n"
103
+ yield
104
+ rawtext "\n"
105
+ end
106
+
107
+ rawtext "<![endif]" if conditional
108
+ rawtext "-->\n"
109
+ end
110
+
111
+ alias_method :to_xml, :render
112
+
113
+ protected
114
+
115
+ def sort_for_xml_declaration(attributes)
116
+ # correct order is "version, encoding, standalone" (XML 1.0 section 2.8).
117
+ # But we only try to put version before encoding for now.
118
+ stringized = []
119
+ attributes.each do |key, value|
120
+ stringized << [key.to_s, value]
121
+ end
122
+ stringized.sort{|a, b| b <=> a}
123
+ end
124
+
125
+ end
126
+
127
+ public
128
+
129
+ XmlWidget = XMLWidget
130
+
131
+ end