Changeset 74

Show
Ignore:
Timestamp:
08/19/2006 15:00:19 (2 years ago)
Author:
why
Message:
  • lib/markaby/builder.rb: merged in all remaining changes from xhtml-careful. is now passing all tests.
  • lib/markaby/tags.rb: ditto.
  • lib/markaby/cssproxy.rb: ditto.
  • Rakefile: ditto.
  • test/test_markaby.rb: had to alter a few tests. for one, the html method no longer outputs doctype. use xhtml_transitional or xhtml_strict. secondly, if capture is passed back into a block, it is output. so, give the parent block nil in those cases.
Location:
trunk
Files:
6 modified

Legend:

Unmodified
Added
Removed
  • trunk/Rakefile

    r60 r74  
    88include FileUtils 
    99 
     10REV = File.read(".svn/entries")[/committed-rev="(\d+)"/, 1] rescue nil 
     11VERS = "0.5" + (REV ? ".#{REV}" : "") 
     12 
     13task :default => [:package] 
     14 
    1015setup_tests 
    1116setup_rdoc ['README', 'CHANGELOG', 'lib/**/*.rb'] 
     
    1318summary = "Markup as Ruby, write HTML in your native Ruby tongue" 
    1419test_file = "test/test_markaby.rb" 
    15 setup_gem("markaby", "0.5",  "Tim Fletcher and _why", summary, [['builder', '>=2.0.0']], test_file) 
     20setup_gem("markaby", VERS,  "Tim Fletcher and _why", summary, [['builder', '>=2.0.0']], test_file) 
  • trunk/lib/markaby.rb

    r60 r74  
    3333require 'markaby/cssproxy' 
    3434require 'markaby/metaid' 
    35 require 'markaby/tags' 
    3635require 'markaby/template' 
  • trunk/lib/markaby/builder.rb

    r62 r74  
     1require 'markaby/tags' 
     2 
    13module Markaby 
    24  # The Markaby::Builder class is the central gear in the system.  When using 
     
    2022 
    2123    @@default = { 
    22       :indent => 2, 
     24      :indent => 0, 
    2325      :output_helpers => true, 
     26      :output_xml_instruction => true, 
    2427      :output_meta_tag => true, 
    25       :image_tag_options => { :border => '0', :alt => '' } 
     28      :auto_validation => true, 
     29      :image_tag_options => { :border => '0', :alt => '' }, 
     30      :tagset => Markaby::XHTMLTransitional 
    2631    } 
    2732 
     
    3035    end 
    3136 
    32     def self.ignored_helpers 
    33       @@ignored_helpers ||= [] 
    34     end 
    35  
    36     def self.ignore_helpers(*helpers) 
    37       ignored_helpers.concat helpers 
    38     end 
    39  
    40     XHTMLTransitional = ["-//W3C//DTD XHTML 1.0 Transitional//EN", "DTD/xhtml1-transitional.dtd"] 
    41      
    42     XHTMLStrict = ["-//W3C//DTD XHTML 1.0 Strict//EN", "DTD/xhtml1-strict.dtd"] 
    43  
    44     attr_accessor :output_helpers 
     37    def self.ignored_helpers  
     38      @@ignored_helpers ||= []  
     39    end  
     40  
     41    def self.ignore_helpers(*helpers)  
     42      ignored_helpers.concat helpers  
     43    end  
     44 
     45    attr_accessor :output_helpers, :tagset 
    4546 
    4647    # Create a Markaby builder object.  Pass in a hash of variable assignments to 
     
    6263      @streams = [[]] 
    6364      @assigns = assigns 
    64       @margin = -1 
    65  
    66       @indent = @@default[:indent] 
    67       @output_helpers = @@default[:output_helpers] 
    68       @output_meta_tag = @@default[:output_meta_tag] 
    69  
    70       use_tagset Markaby::XHTMLTransitionalTags 
    71  
    72       if helpers 
     65      @elements = {} 
     66 
     67      @@default.each do |k, v| 
     68        instance_variable_set("@#{k}", @assigns[k] || v) 
     69      end 
     70 
     71      if helpers.nil? 
     72        @helpers = nil 
     73      else 
    7374        @helpers = helpers.dup 
    7475        for iv in helpers.instance_variables 
     
    8687      end 
    8788 
    88       @margin += 1 
    89       @builder = ::Builder::XmlMarkup.new(:indent => @indent, :margin => @margin, :target => @streams.last) 
    90  
    91       def @builder.target=(io) 
    92         @target = io 
    93       end 
    94  
    95       instance_eval(&block) if block 
     89      @builder = ::Builder::XmlMarkup.new(:indent => @indent, :target => @streams.last) 
     90      class << @builder 
     91          attr_accessor :target, :level 
     92      end 
     93 
     94      if block 
     95        text(capture(&block)) 
     96      end 
    9697    end 
    9798 
    9899    # Returns a string containing the HTML stream.  Internally, the stream is stored as an Array. 
    99100    def to_s 
    100       @streams.last.join 
     101      @streams.last.to_s 
    101102    end 
    102103 
     
    109110    alias_method :concat, :text 
    110111 
     112    # Emulate ERB to satisfy helpers like <tt>form_for</tt>. 
     113    def _erbout; self end 
     114 
    111115    # Captures the HTML code built inside the +block+.  This is done by creating a new 
    112116    # stream for the builder object, running the block and passing back its stream as a string. 
     
    117121    def capture(&block) 
    118122      @streams.push(builder.target = []) 
     123      @builder.level += 1 
    119124      str = instance_eval(&block) 
    120       str = to_s unless @streams.last.compact.empty? 
     125      str = @streams.last.join if @streams.last.any? 
    121126      @streams.pop 
     127      @builder.level -= 1 
    122128      builder.target = @streams.last 
    123129      str 
     130    end 
     131 
     132    # Content_for will store the given block in an instance variable for later use  
     133    # in another template or in the layout. 
     134    # 
     135    # The name of the instance variable is content_for_<name> to stay consistent  
     136    # with @content_for_layout which is used by ActionView's layouts. 
     137    # 
     138    # Example: 
     139    # 
     140    #   content_for("header") do 
     141    #     h1 "Half Shark and Half Lion" 
     142    #   end 
     143    # 
     144    # If used several times, the variable will contain all the parts concatenated. 
     145    def content_for(name, &block) 
     146      @helpers.assigns["content_for_#{name}"] = 
     147        eval("@content_for_#{name} = (@content_for_#{name} || '') + capture(&block)") 
    124148    end 
    125149 
     
    127151    # the arguments are the same as the tags implemented via method_missing. 
    128152    def tag!(tag, *args, &block) 
    129       if @tagset 
    130         if !@tagset.has_key?(tag) 
    131           raise InvalidXhtmlError, "no element `#{tag}' for #{doctype}" 
    132         elsif args.last.respond_to?(:to_hash) 
    133           attrs = args.last.to_hash 
    134           attrs.each do |k, v| 
    135             unless k =~ /:/ or @tagset[tag].include? k.to_s.downcase.intern 
    136               raise InvalidXhtmlError, "no attribute `#{k}' on #{tag} elements" 
    137             end 
     153      ele_id = nil 
     154      if @auto_validation and @tagset 
     155          if !@tagset.tagset.has_key?(tag) 
     156              raise InvalidXhtmlError, "no element `#{tag}' for #{tagset.doctype}" 
     157          elsif args.last.respond_to?(:to_hash) 
     158              attrs = args.last.to_hash 
     159              attrs.each do |k, v| 
     160                  atname = k.to_s.downcase.intern 
     161                  unless k =~ /:/ or @tagset.tagset[tag].include? atname 
     162                      raise InvalidXhtmlError, "no attribute `#{k}' on #{tag} elements" 
     163                  end 
     164                  if atname == :id 
     165                      ele_id = v.to_s 
     166                      if @elements.has_key? ele_id 
     167                          raise InvalidXhtmlError, "id `#{ele_id}' already used (id's must be unique)." 
     168                      end 
     169                  end 
     170              end 
    138171          end 
    139         end 
    140172      end 
    141173      if block 
     
    143175        block = proc { text(str) } 
    144176      end 
    145       fragment { @builder.method_missing(tag, *args, &block) } 
     177 
     178      f = fragment { @builder.method_missing(tag, *args, &block) } 
     179      @elements[ele_id] = f if ele_id 
     180      f 
    146181    end 
    147182 
     
    152187    # variables.  Here is the order of interception: 
    153188    # 
    154     # * If +sym+ is a helper method, the helper method is called 
    155     #   and output to the stream. 
    156189    # * If +sym+ is a recognized HTML tag, the tag is output 
    157190    #   or a CssProxy is returned if no arguments are given. 
     
    160193    # * If +sym+ is also the name of an instance variable, the 
    161194    #   value of the instance variable is returned. 
     195    # * If +sym+ is a helper method, the helper method is called 
     196    #   and output to the stream. 
    162197    # * Otherwise, +sym+ and its arguments are passed to tag! 
    163198    def method_missing(sym, *args, &block) 
    164       if @helpers.respond_to?(sym) && !self.class.ignored_helpers.include?(sym) 
     199      if @helpers.respond_to?(sym, true) && !self.class.ignored_helpers.include?(sym) 
    165200        r = @helpers.send(sym, *args, &block) 
    166         @output_helpers ? fragment { @builder << r } : r 
    167       elsif ::Builder::XmlMarkup.instance_methods.include?(sym.to_s) 
     201        if @output_helpers 
     202          fragment { @builder << r } 
     203        else 
     204          r 
     205        end 
     206      elsif ::Builder::XmlMarkup.instance_methods.include?(sym.to_s)  
    168207        @builder.__send__(sym, *args, &block) 
    169       elsif @tagset and @tagset_tags.include?(sym) 
    170         if @tagset_self_closing.include?(sym) and block 
    171           raise InvalidXhtmlError, "the `#{sym}' element is self-closing, please remove the block" 
    172         end 
    173         if args.empty? and block.nil? and not NO_PROXY.include?(sym) 
    174           return CssProxy.new do |args, block| 
    175             if @tagset_forms.include?(sym) and args.last.respond_to?(:to_hash) and args.last[:id] 
    176               args.last[:name] ||= args.last[:id] 
    177             end 
    178             tag!(sym, *args, &block) 
    179           end 
    180         end 
    181         #if not @tagset_self_closing.include?(sym) and args.first.respond_to?(:to_hash) 
    182         #  block ||= proc{} 
    183         #end 
    184         tag!(sym, *args, &block) 
    185208      elsif instance_variable_get("@#{sym}") 
    186209        instance_variable_get("@#{sym}") 
     
    192215    end 
    193216 
    194     undef_method :p, :select, :sub 
    195  
    196     # Builds a image tag.  Assumes <tt>:border => '0', :alt => ''</tt>. 
    197     def img(opts = {}) 
    198       tag!(:img, @@default[:image_tag_options].merge(opts)) 
     217    def html_tag(sym, *args, &block) 
     218      if @auto_validation and @tagset.self_closing.include?(sym) and block 
     219        raise InvalidXhtmlError, "the `\#{sym}' element is self-closing, please remove the block" 
     220      end 
     221      if args.empty? and block.nil? and not NO_PROXY.include?(sym) 
     222        return CssProxy.new do |args, block| 
     223          if @tagset.forms.include?(sym) and args.last.respond_to?(:to_hash) and args.last[:id] 
     224            args.last[:name] ||= args.last[:id] 
     225          end 
     226          tag!(sym, *args, &block) 
     227        end 
     228      end 
     229      if not @tagset.self_closing.include?(sym) and args.first.respond_to?(:to_hash) 
     230        block ||= proc{} 
     231      end 
     232      tag!(sym, *args, &block) 
     233    end 
     234 
     235    XHTMLTransitional.tags.each do |k| 
     236      class_eval %{ 
     237        def #{k}(*args, &block) 
     238          html_tag(#{k.inspect}, *args, &block) 
     239        end 
     240      } 
    199241    end 
    200242 
     
    204246      tag!(:head, *args) do 
    205247        tag!(:meta, "http-equiv" => "Content-Type", "content" => "text/html; charset=utf-8") if @output_meta_tag 
    206         instance_eval &block 
    207       end 
    208     end 
    209  
    210     def use_tagset(tagset) 
    211       return if @tagset == tagset 
    212       @tagset = tagset 
    213       @tagset_tags = tagset.keys 
    214       @tagset_forms = @tagset_tags & FORM_TAGS 
    215       @tagset_self_closing = @tagset_tags & SELF_CLOSING_TAGS 
     248        instance_eval(&block) 
     249      end 
    216250    end 
    217251 
     
    219253    # are prepended.  Also assumes <tt>:xmlns => "http://www.w3.org/1999/xhtml", 
    220254    # :lang => "en"</tt>. 
    221     def html(*doctype, &block) 
    222       if doctype.empty? 
    223         doctype = XHTMLTransitional 
    224         use_tagset Markaby::XHTMLTransitionalTags 
    225       end 
    226       declare!(:DOCTYPE, :html, :PUBLIC, *doctype) 
    227       tag!(:html, :xmlns => "http://www.w3.org/1999/xhtml", "xml:lang" => "en", :lang => "en", &block) 
    228     end 
    229     alias_method :xhtml_transitional, :html 
     255    def xhtml_transitional(&block) 
     256      self.tagset = Markaby::XHTMLTransitional 
     257      xhtml_html &block 
     258    end 
    230259 
    231260    # Builds an html tag with XHTML 1.0 Strict doctype instead. 
    232261    def xhtml_strict(&block) 
    233       use_tagset Markaby::XHTMLStrictTags 
    234       instruct! 
    235       html *XHTMLStrict, &block 
     262      self.tagset = Markaby::XHTMLStrict 
     263      xhtml_html &block 
    236264    end 
    237265 
    238266    private 
     267 
     268    def xhtml_html(&block) 
     269      instruct! if @output_xml_instruction 
     270      declare!(:DOCTYPE, :html, :PUBLIC, *tagset.doctype) 
     271      tag!(:html, :xmlns => "http://www.w3.org/1999/xhtml", "xml:lang" => "en", :lang => "en", &block) 
     272    end 
    239273 
    240274    def fragment 
     
    250284  class Fragment < ::Builder::BlankSlate 
    251285    def initialize(s, a, b) 
    252       @s, @f1, @f2 = s, a, b 
     286      @s, @f1, @f2 = s, a, b  
    253287    end 
    254288    def method_missing(*a) 
    255289      unless @str 
    256         @str = @s[@f1, @f2].to_s 
     290        @str = @s[@f1, @f2].to_s   
    257291        @s[@f1, @f2] = [nil] * @f2 
    258292        @str 
  • trunk/lib/markaby/cssproxy.rb

    r20 r74  
    1313    end 
    1414   
     15    # Adds attributes to an element, for internal use only.  For example, if you 
     16    # want to write a wrapper which sets a bunch of default attributes for a certain 
     17    # tag.  Like the default `img' method included with Markaby automatically sets an 
     18    # empty alt attribute. 
     19    def merge!(opts) 
     20      @opts.merge! opts 
     21      self 
     22    end 
     23 
    1524    # Adds attributes to an element.  Bang methods set the :id attribute. 
    1625    # Other methods add to the :class attribute.  If a block is supplied, 
     
    1928      idc = id_or_class.to_s 
    2029      case idc 
     30      when "pass" 
    2131      when /!$/ 
    2232        @opts[:id] = $` 
     
    3444      end 
    3545    end 
     46 
     47    def to_str 
     48      @blk.call([[@opts]]).to_s 
     49    end 
     50    alias_method :to_s, :to_str 
     51 
    3652  end 
    3753end 
  • trunk/lib/markaby/tags.rb

    r60 r74  
    11module Markaby 
    2  
    3   # Common sets of attributes. 
    4   AttrCustom = [:id, :class, :style] 
    5   AttrEvents = [:onclick, :ondblclick, :onmousedown, :onmouseup, :onmouseover, :onmousemove,  
    6       :onmouseout, :onkeypress, :onkeydown, :onkeyup] 
    7   AttrAnno = [:title, :lang, :dir] 
    8   Attrs = AttrCustom + AttrEvents + AttrAnno 
    9  
    10   # Very basic rules from the XHTML 1.0 Strict DTD. 
    11   XHTMLStrictTags = { 
    12     :pre => AttrCustom + AttrAnno + [:space],  
    13     :em => Attrs, 
    14     :code => Attrs, 
    15     :h2 => Attrs, 
    16     :h3 => Attrs, 
    17     :h1 => Attrs, 
    18     :h6 => Attrs, 
    19     :dl => Attrs, 
    20     :h4 => Attrs, 
    21     :h5 => Attrs, 
    22     :area => Attrs + [:accesskey, :tabindex, :onfocus, :onblur, :shape, :coords, :href, :nohref, :alt],  
    23     :meta => [:lang, :dir, :id, :name, :content, :scheme, "http-equiv".intern],  
    24     :table => [],  
    25     :dfn => Attrs, 
    26     :label => Attrs + [:for, :accesskey, :onfocus, :onblur],  
    27     :select => Attrs + [:name, :size, :multiple, :disabled, :tabindex, :onfocus, :onblur, :onchange],  
    28     :noscript => Attrs, 
    29     :style => [:lang, :dir, :id, :type, :media, :title, :space],  
    30     :strong => Attrs, 
    31     :span => Attrs, 
    32     :sub => Attrs, 
    33     :img => Attrs + [:src, :alt, :longdesc, :height, :width, :usemap, :ismap],  
    34     :title => [:lang, :dir, :id],  
    35     :bdo => AttrCustom + [:title] + AttrEvents + [:lang, :dir],  
    36     :tr => [],  
    37     :tbody => [],  
    38     :param => [:id, :name, :value, :valuetype, :type],  
    39     :li => Attrs, 
    40     :acronym => Attrs, 
    41     :html => [:lang, :dir, :id, :xmlns],  
    42     :caption => [],  
    43     :tfoot => [],  
    44     :th => [],  
    45     :sup => Attrs, 
    46     :var => Attrs, 
    47     :input => Attrs + [:accesskey, :tabindex, :onfocus, :onblur, :type, :name, :value, :checked, :disabled, :readonly, :size, :maxlength, :src, :alt, :usemap, :onselect, :onchange, :accept],  
    48     :td => Attrs + [:summary, :width, :border, :frame, :rules, :cellspacing, :cellpadding, :span, :align, :char, :charoff, :valign, :abbr, :axis, :headers, :scope, :rowspan, :colspan],  
    49     :samp => Attrs, 
    50     :cite => Attrs, 
    51     :thead => [],  
    52     :body => Attrs + [:onload, :onunload],  
    53     :map => AttrCustom + [:lang, :dir, ] + AttrEvents + [:title, :name],  
    54     :head => [:lang, :dir, :id, :profile],  
    55     :blockquote => Attrs + [:cite],  
    56     :fieldset => Attrs, 
    57     :option => Attrs + [:selected, :disabled, :label, :value],  
    58     :form => Attrs + [:action, :method, :enctype, :onsubmit, :onreset, :accept, 'accept-charset'],  
    59     :hr => Attrs, 
    60     :big => Attrs, 
    61     :dd => Attrs, 
    62     :object => Attrs + [:declare, :classid, :codebase, :data, :type, :codetype, :archive, :standby, :height, :width, :usemap, :name, :tabindex],  
    63     :base => [:href, :id],  
    64     :link => Attrs + [:charset, :href, :hreflang, :type, :rel, :rev, :media],  
    65     :kbd => Attrs, 
    66     :br => AttrCustom + [:title],  
    67     :address => Attrs, 
    68     :optgroup => Attrs + [:disabled, :label],  
    69     :dt => Attrs, 
    70     :ins => Attrs + [:cite, :datetime],  
    71     :b => Attrs, 
    72     :legend => Attrs + [:accesskey],  
    73     :abbr => Attrs, 
    74     :a => Attrs + [:accesskey, :tabindex, :onfocus, :onblur, :charset, :type, :name, :href, :hreflang, :rel, :rev, :shape, :coords],  
    75     :ol => Attrs, 
    76     :textarea => Attrs + [:accesskey, :tabindex, :onfocus, :onblur, :name, :rows, :cols, :disabled, :readonly, :onselect, :onchange],  
    77     :colgroup => [],  
    78     :i => Attrs, 
    79     :button => Attrs + [:accesskey, :tabindex, :onfocus, :onblur, :name, :value, :type, :disabled],  
    80     :script => [:id, :charset, :type, :src, :defer, :space],  
    81     :col => [],  
    82     :q => Attrs + [:cite],  
    83     :p => Attrs, 
    84     :del => Attrs + [:cite, :datetime],  
    85     :small => Attrs, 
    86     :div => Attrs, 
    87     :tt => Attrs, 
    88     :ul => Attrs   
    89   } 
    90  
    91   # Additional tags found in XHTML 1.0 Transitional 
    92   XHTMLTransitionalTags = XHTMLStrictTags.merge \ 
    93     :strike => Attrs, 
    94     :center => Attrs, 
    95     :dir => Attrs + [:compact],  
    96     :noframes => Attrs, 
    97     :basefont => [:id, :size, :color, :face],  
    98     :u => Attrs, 
    99     :menu => Attrs + [:compact],  
    100     :iframe => AttrCustom + [:title, :longdesc, :name, :src, :frameborder, :marginwidth, :marginheight, :scrolling, :align, :height, :width],  
    101     :font => AttrCustom + AttrAnno + [:size, :color, :face],  
    102     :s => Attrs, 
    103     :applet => AttrCustom + [:title, :codebase, :archive, :code, :object, :alt, :name, :width, :height, :align, :hspace, :vspace],  
    104     :isindex => AttrCustom + AttrAnno + [:prompt] 
    105  
    106   # Additional attributes found in XHTML 1.0 Transitional 
    107   { :script => [:language], 
    108     :a => [:target], 
    109     :td => [:bgcolor, :nowrap, :height], 
    110     :p => [:align], 
    111     :h5 => [:align], 
    112     :h3 => [:align], 
    113     :li => [:type, :value], 
    114     :div => [:align], 
    115     :pre => AttrEvents + [:width], 
    116     :body => [:background, :bgcolor, :text, :link, :vlink, :alink], 
    117     :ol => [:type, :compact, :start], 
    118     :h4 => [:align], 
    119     :h2 => [:align], 
    120     :object => [:align, :border, :hspace, :vspace], 
    121     :img => [:name, :align, :border, :hspace, :vspace], 
    122     :link => [:target], 
    123     :legend => [:align], 
    124     :dl => [:compact], 
    125     :input => [:align], 
    126     :h6 => [:align], 
    127     :hr => [:align, :noshade, :size, :width], 
    128     :base => [:target], 
    129     :ul => [:type, :compact], 
    130     :br => [:clear], 
    131     :form => [:name, :target], 
    132     :area => [:target], 
    133     :h1 => [:align] 
    134   }.each do |k, v| 
    135       XHTMLTransitionalTags[k] += v 
    136   end 
    1372 
    1383  FORM_TAGS = [ :form, :input, :select, :textarea ] 
     
    1405  NO_PROXY = [ :hr, :br ] 
    1416 
     7  # Common sets of attributes. 
     8  AttrCore = [:id, :class, :style, :title] 
     9  AttrI18n = [:lang, :'xml:lang', :dir] 
     10  AttrEvents = [:onclick, :ondblclick, :onmousedown, :onmouseup, :onmouseover, :onmousemove,  
     11      :onmouseout, :onkeypress, :onkeydown, :onkeyup] 
     12  AttrFocus = [:accesskey, :tabindex, :onfocus, :onblur] 
     13  AttrHAlign = [:align, :char, :charoff] 
     14  AttrVAlign = [:valign] 
     15  Attrs = AttrCore + AttrI18n + AttrEvents 
     16 
     17  # All the tags and attributes from XHTML 1.0 Strict 
     18  class XHTMLStrict 
     19    class << self 
     20      attr_accessor :tags, :tagset, :forms, :self_closing, :doctype 
     21    end 
     22    @doctype = ["-//W3C//DTD XHTML 1.0 Strict//EN", "DTD/xhtml1-strict.dtd"] 
     23    @tagset = { 
     24      :html => AttrI18n + [:id, :xmlns], 
     25      :head => AttrI18n + [:id, :profile], 
     26      :title => AttrI18n + [:id], 
     27      :base => [:href, :id], 
     28      :meta => AttrI18n + [:id, :http, :name, :content, :scheme, :'http-equiv'], 
     29      :link => Attrs + [:charset, :href, :hreflang, :type, :rel, :rev, :media], 
     30      :style => AttrI18n + [:id, :type, :media, :title, :'xml:space'], 
     31      :script => [:id, :charset, :type, :src, :defer, :'xml:space'], 
     32      :noscript => Attrs, 
     33      :body => Attrs + [:onload, :onunload], 
     34      :div => Attrs, 
     35      :p => Attrs, 
     36      :ul => Attrs, 
     37      :ol => Attrs, 
     38      :li => Attrs, 
     39      :dl => Attrs, 
     40      :dt => Attrs, 
     41      :dd => Attrs, 
     42      :address => Attrs, 
     43      :hr => Attrs, 
     44      :pre => Attrs + [:'xml:space'], 
     45      :blockquote => Attrs + [:cite], 
     46      :ins => Attrs + [:cite, :datetime], 
     47      :del => Attrs + [:cite, :datetime], 
     48      :a => Attrs + AttrFocus + [:charset, :type, :name, :href, :hreflang, :rel, :rev, :shape, :coords], 
     49      :span => Attrs, 
     50      :bdo => AttrCore + AttrEvents + [:lang, :'xml:lang', :dir], 
     51      :br => AttrCore, 
     52      :em => Attrs, 
     53      :strong => Attrs, 
     54      :dfn => Attrs, 
     55      :code => Attrs, 
     56      :samp => Attrs, 
     57      :kbd => Attrs, 
     58      :var => Attrs, 
     59      :cite => Attrs, 
     60      :abbr => Attrs, 
     61      :acronym => Attrs, 
     62      :q => Attrs + [:cite], 
     63      :sub => Attrs, 
     64      :sup => Attrs, 
     65      :tt => Attrs, 
     66      :i => Attrs, 
     67      :b => Attrs, 
     68      :big => Attrs, 
     69      :small => Attrs, 
     70      :object => Attrs + [:declare, :classid, :codebase, :data, :type, :codetype, :archive, :standby, :height, :width, :usemap, :name, :tabindex], 
     71      :param => [:id, :name, :value, :valuetype, :type], 
     72      :img => Attrs + [:src, :alt, :longdesc, :height, :width, :usemap, :ismap], 
     73      :map => AttrI18n + AttrEvents + [:id, :class, :style, :title, :name], 
     74      :area => Attrs + AttrFocus + [:shape, :coords, :href, :nohref, :alt], 
     75      :form => Attrs + [:action, :method, :enctype, :onsubmit, :onreset, :accept, :accept], 
     76      :label => Attrs + [:for, :accesskey, :onfocus, :onblur], 
     77      :input => Attrs + AttrFocus + [:type, :name, :value, :checked, :disabled, :readonly, :size, :maxlength, :src, :alt, :usemap, :onselect, :onchange, :accept], 
     78      :select => Attrs + [:name, :size, :multiple, :disabled, :tabindex, :onfocus, :onblur, :onchange], 
     79      :optgroup => Attrs + [:disabled, :label], 
     80      :option => Attrs + [:selected, :disabled, :label, :value], 
     81      :textarea => Attrs + AttrFocus + [:name, :rows, :cols, :disabled, :readonly, :onselect, :onchange], 
     82      :fieldset => Attrs, 
     83      :legend => Attrs + [:accesskey], 
     84      :button => Attrs + AttrFocus + [:name, :value, :type, :disabled], 
     85      :table => Attrs + [:summary, :width, :border, :frame, :rules, :cellspacing, :cellpadding], 
     86      :caption => Attrs, 
     87      :colgroup => Attrs + AttrHAlign + AttrVAlign + [:span, :width], 
     88      :col => Attrs + AttrHAlign + AttrVAlign + [:span, :width], 
     89      :thead => Attrs + AttrHAlign + AttrVAlign, 
     90      :tfoot => Attrs + AttrHAlign + AttrVAlign, 
     91      :tbody => Attrs + AttrHAlign + AttrVAlign, 
     92      :tr => Attrs + AttrHAlign + AttrVAlign, 
     93      :th => Attrs + AttrHAlign + AttrVAlign + [:abbr, :axis, :headers, :scope, :rowspan, :colspan], 
     94      :td => Attrs + AttrHAlign + AttrVAlign + [:abbr, :axis, :headers, :scope, :rowspan, :colspan], 
     95      :h1 => Attrs, 
     96      :h2 => Attrs, 
     97      :h3 => Attrs, 
     98      :h4 => Attrs, 
     99      :h5 => Attrs, 
     100      :h6 => Attrs 
     101    } 
     102 
     103    @tags = @tagset.keys 
     104    @forms = @tags & FORM_TAGS 
     105    @self_closing = @tags & SELF_CLOSING_TAGS 
     106  end 
     107 
     108  # Additional tags found in XHTML 1.0 Transitional 
     109  class XHTMLTransitional 
     110    class << self 
     111      attr_accessor :tags, :tagset, :forms, :self_closing, :doctype 
     112    end 
     113    @doctype = ["-//W3C//DTD XHTML 1.0 Transitional//EN", "DTD/xhtml1-transitional.dtd"] 
     114    @tagset = XHTMLStrict.tagset.merge \ 
     115      :strike => Attrs, 
     116      :center => Attrs, 
     117      :dir => Attrs + [:compact],  
     118      :noframes => Attrs, 
     119      :basefont => [:id, :size, :color, :face],  
     120      :u => Attrs, 
     121      :menu => Attrs + [:compact],  
     122      :iframe => AttrCore + [:longdesc, :name, :src, :frameborder, :marginwidth, :marginheight, :scrolling, :align, :height, :width], 
     123      :font => AttrCore + AttrI18n + [:size, :color, :face], 
     124      :s => Attrs, 
     125      :applet => AttrCore + [:codebase, :archive, :code, :object, :alt, :name, :width, :height, :align, :hspace, :vspace], 
     126      :isindex => AttrCore + AttrI18n + [:prompt] 
     127 
     128    # Additional attributes found in XHTML 1.0 Transitional 
     129    { :script => [:language], 
     130      :a => [:target], 
     131      :td => [:bgcolor, :nowrap, :width, :height], 
     132      :p => [:align], 
     133      :h5 => [:align], 
     134      :h3 => [:align], 
     135      :li => [:type, :value], 
     136      :div => [:align], 
     137      :pre => [:width], 
     138      :body => [:background, :bgcolor, :text, :link, :vlink, :alink], 
     139      :ol => [:type, :compact, :start], 
     140      :h4 => [:align], 
     141      :h2 => [:align], 
     142      :object => [:align, :border, :hspace, :vspace], 
     143      :img => [:name, :align, :border, :hspace, :vspace], 
     144      :link => [:target], 
     145      :legend => [:align], 
     146