Module: HTree

Included in:
Context, Location, Name, Node, Tag
Defined in:
lib/htree/gencode.rb,
lib/htree/doc.rb,
lib/htree/loc.rb,
lib/htree/tag.rb,
lib/htree/elem.rb,
lib/htree/fstr.rb,
lib/htree/leaf.rb,
lib/htree/name.rb,
lib/htree/scan.rb,
lib/htree/text.rb,
lib/htree/parse.rb,
lib/htree/rexml.rb,
lib/htree/output.rb,
lib/htree/context.rb,
lib/htree/display.rb,
lib/htree/encoder.rb,
lib/htree/inspect.rb,
lib/htree/modules.rb,
lib/htree/equality.rb,
lib/htree/htmlinfo.rb,
lib/htree/template.rb,
lib/htree/traverse.rb,
lib/htree/raw_string.rb,
lib/htree/extract_text.rb

Overview

Template Engine

The htree template engine converts HTML and some data to HTML or XML.

Template Method Summary

  • HTree.expand_template(template_pathname) -> $stdout

  • HTree.expand_template(template_pathname, obj) -> $stdout

  • HTree.expand_template(template_pathname, obj, out) -> out

  • HTree.expand_template(template_pathname, obj, out, encoding) -> out

  • HTree.expand_template<em>template_string</em> -> $stdout

  • HTree.expand_template(out) <em>template_string</em> -> out

  • HTree.expand_template(out, encoding) <em>template_string</em> -> out

  • HTree.compile_template(template_string) -> Module

  • HTree<em>template_string</em> -> HTree::Doc

Note that the following method, HTree(), is not a template method.

  • HTree(html_string) -> HTree::Doc

Template Directives.

A template directive is described as a special HTML attribute which name begins with underscore.

The template directives are listed as follows.

  • <elem _attr_name=“expr”>content</elem>

  • <elem _text=“expr”>dummy-content</elem>

  • <elem _text>expr</elem>

  • <elem _tree=“expr”>dummy-content</elem>

  • <elem _tree>expr</elem>

  • <elem _if=“expr” _else=“mod.name(args)”>then-content</elem>

  • <elem _iter=“expr.meth(args)//vars”>content</elem>

  • <elem _iter_content=“expr.meth(args)//vars”>content</elem>

  • <elem _call=“mod.name(args)”>dummy-content</elem>

  • <elem _template=“name(vars)”>body</elem>

Template Semantics

  • attribute substitution

    • <elem _attr_name=“expr”>content</elem>

    _attr_name is used for a dynamic attribute.

       <elem _attr_xxx="..."/>
    -> <elem xxx="..."/>
    

    It is expanded to name=“content”. The content is generated by evaluating expr. Usually you don’t need to care escaping: &, <, > and “ are automatically escaped. If you need to output character references, the value of expr should be an object which have a rcdata method such as an HTree::Text. If the value has a rcdata method, it is called and the result is used as the content with escaping <, > and ”.

    _attr_name can be used multiple times in single element.

  • text substitution

    • <elem _text=“expr”>dummy-content</elem>

    • <elem _text>expr</elem>

    _text substitutes the content of the element by the string evaluated from expr. expr is described in the attribute value or the content of the element.

    If a result of expr have &, < and/or >, they are automatically escaped. If you need to output character references, the value of expr should be an object which have a rcdata method such as an HTree::Text. If the value has a rcdata method, it is called and the result is used as the content with escaping < and >.

    If the element is span or div, and there is no other attributes, no tags are produced.

       <elem _text="...">dummy-content</elem>
    -> <elem>...</elem>
    
  • tree substitution

    • <elem _tree=“expr”>dummy-content</elem>

    • <elem _tree>expr</elem>

    _tree substitutes the content of the element by the htree object evaluated from expr. expr is described in the attribute value or the content of the element.

    If the element is span or div, and there is no other attributes, no tags are produced.

       <elem _tree="...">dummy-content</elem>
    -> <elem>...</elem>
    
  • conditional

    • <elem _if=“expr”>then-content</elem>

    • <elem _if=“expr” _else=“name(args)”>then-content</elem>

    _if is used for conditional.

    If expr is evaluated to true, it expands as follows regardless of existence of _else.

       <elem _if="<i>expr</i>">then-content</elem>
    -> <elem>then-content</elem>
    

    If expr is evaluated to false, it expands using _else. If _else is not given, it expands to empty. If _else is given, it expands as follows.

       <elem _if="<i>expr</i>" _else="<i>name(args)</i>">then-content</elem>
    -> <elem _call="<i>name(args)</i>">then-content</elem>
    -> see _call for further expansion.
    

    It is expanded to <elem>then-content</elem> if expr is evaluated to a true value. Otherwise, it is replaced by other template specified by _else attribute. If _else attribute is not given, it just replaced by empty.

  • iteration

    • <elem _iter=“expr.meth(args)//vars”>content</elem>

    • <elem _iter_content=“expr.meth(args)//vars”>content</elem>

    _iter and _iter_content is used for iteration. _iter iterates the element itself but _iter_content iterates the content.

       <outer _iter="..."><inner/></outer>
    -> <outer><inner/></outer><outer><inner/></outer>...
    
       <outer _iter_content="..."><inner/></outer>
    -> <outer><inner/><inner/>...</outer>
    

    expr.meth(args) specifies iterator method call. It is actually called with a block. The block have block parameters vars. vars must be variables separated by comma.

  • template call

    • <elem _call=“name(args)”>dummy-content</elem>

    • <elem _call=“mod.name(args)”>dummy-content</elem>

    _call is used to expand a template function. The template function is defined by _template.

       <d _template="m">...</d>
       <c _call="m">...</c>
    -> <d>...</d>
    

    A local template can be called as follows:

    HTree.expand_template{<<'End'}
    <a _template=ruby_talk(num)
       _attr_href='"http://ruby-talk.org/#{num}"'
       >[ruby-talk:<span _text=num>nnn</span>]</a>
    Ruby 1.8.0 is released at <span _call=ruby_talk(77946) />.
    Ruby 1.8.1 is released at <span _call=ruby_talk(88814) />.
    End
    

    mod should be the result of HTree.compile_template.

    M = HTree.compile_template(<<'End')
    <a _template=ruby_talk(num)
       _attr_href='"http://ruby-talk.org/#{num}"'
       >[ruby-talk:<span _text=num>nnn</span>]</a>
    End
    HTree.expand_template{<<'End'}
    <html>
    Ruby 1.8.0 is released at <span _call=M.ruby_talk(77946) />.
    Ruby 1.8.1 is released at <span _call=M.ruby_talk(88814) />.
    </html>
    End
    

    The module can included. In such case, the template function can be called without mod. prefix.

    include HTree.compile_template(<<'End')
    <a _template=ruby_talk(num)
       _attr_href='"http://ruby-talk.org/#{num}"'
       >[ruby-talk:<span _text=num>nnn</span>]</a>
    End
    HTree.expand_template{<<'End'}
    <html>
    Ruby 1.8.0 is released at <span _call=ruby_talk(77946) />.
    Ruby 1.8.1 is released at <span _call=ruby_talk(88814) />.
    </html>
    End
    
  • template definition

    • <elem _template=“name(vars)”>body</elem>

    _template defines a template function which is usable by _call.

    When a template is compiled to a module by HTree.compile_template, the module have a module function for each template function defined by outermost _template attribute.

White Space Handling

The htree template engine strips whitespace text nodes in a template except under HTML pre element.

For example the white space text node between two spans in following template is stripped.

<span _text="'a'"/> <span _text="'b'"/> -> "ab"

Character entity references are not stripped.

<span _text="'a'"/>&#32;<span _text="'b'"/> -> "a&#32;b"

Text nodes generated by _text is not stripped.

<span _text="'a'"/><span _text="' '"> </span><span _text="'b'"/> -> "a b"

HTML and XML

The htree template engine outputs HTML or XML.

If a template has no XML declaration and the top element is HTML, the result is HTML. Otherwise the result is XML.

They differs as follows.

  • XML declaration is (re-)generated for XML.

  • empty elements ends with a slash for XML.

  • script and style element is escaped for XML.

Design Decision on Design/Logic Separation

HTree template engine doesn’t force you to separate design and logic. Any logic (Ruby code) can be embedded in design (HTML).

However the template engine cares the separation by logic refactorings. The logic is easy to move between a template and an application. For example, following tangled template

tmpl.html:
  <html>
    <head>
      <title _text="very-complex-ruby-code">dummy</title>
    </head>
    ...
  </html>

app.rb:
  HTree.expand_template('tmpl.html', obj)

can be refactored as follows.

tmpl.html:
  <html>
    <head>
      <title _text="title">dummy</title>
    </head>
    ...
  </html>

app.rb:
  def obj.title
    very-complex-ruby-code
  end
  HTree.expand_template('tmpl.html', obj)

In general, any expression in a template can be refactored to an application by extracting it as a method. In JSP, this is difficult especially for a code fragment of an iteration.

Also HTree encourages to separate business logic (Ruby code in an application) and presentation logic (Ruby code in a template). For example, presentation logic to color table rows stripe can be embedded in a template. It doesn’t need to tangle an application.

Defined Under Namespace

Modules: Container, Leaf, Node, Pat, Tag, Traverse, Util Classes: BogusETag, Comment, Context, Doc, DocType, ETag, Elem, Encoder, Error, GenCode, Location, Name, ProcIns, STag, TemplateCompiler, Text, XMLDecl

Constant Summary collapse

DefaultContext =

:stopdoc:

Context.new
HTMLContext =
DefaultContext.subst_namespaces(nil=>"http://www.w3.org/1999/xhtml")
NamedCharacters =

:stopdoc:

{"AElig"=>198, "Aacute"=>193, "Acirc"=>194, "Agrave"=>192, "Alpha"=>913,
"Aring"=>197, "Atilde"=>195, "Auml"=>196, "Beta"=>914, "Ccedil"=>199,
"Chi"=>935, "Dagger"=>8225, "Delta"=>916, "ETH"=>208, "Eacute"=>201,
"Ecirc"=>202, "Egrave"=>200, "Epsilon"=>917, "Eta"=>919, "Euml"=>203,
"Gamma"=>915, "Iacute"=>205, "Icirc"=>206, "Igrave"=>204, "Iota"=>921,
"Iuml"=>207, "Kappa"=>922, "Lambda"=>923, "Mu"=>924, "Ntilde"=>209, "Nu"=>925,
"OElig"=>338, "Oacute"=>211, "Ocirc"=>212, "Ograve"=>210, "Omega"=>937,
"Omicron"=>927, "Oslash"=>216, "Otilde"=>213, "Ouml"=>214, "Phi"=>934,
"Pi"=>928, "Prime"=>8243, "Psi"=>936, "Rho"=>929, "Scaron"=>352, "Sigma"=>931,
"THORN"=>222, "Tau"=>932, "Theta"=>920, "Uacute"=>218, "Ucirc"=>219,
"Ugrave"=>217, "Upsilon"=>933, "Uuml"=>220, "Xi"=>926, "Yacute"=>221,
"Yuml"=>376, "Zeta"=>918, "aacute"=>225, "acirc"=>226, "acute"=>180,
"aelig"=>230, "agrave"=>224, "alefsym"=>8501, "alpha"=>945, "amp"=>38,
"and"=>8743, "ang"=>8736, "apos"=>39, "aring"=>229, "asymp"=>8776,
"atilde"=>227, "auml"=>228, "bdquo"=>8222, "beta"=>946, "brvbar"=>166,
"bull"=>8226, "cap"=>8745, "ccedil"=>231, "cedil"=>184, "cent"=>162,
"chi"=>967, "circ"=>710, "clubs"=>9827, "cong"=>8773, "copy"=>169,
"crarr"=>8629, "cup"=>8746, "curren"=>164, "dArr"=>8659, "dagger"=>8224,
"darr"=>8595, "deg"=>176, "delta"=>948, "diams"=>9830, "divide"=>247,
"eacute"=>233, "ecirc"=>234, "egrave"=>232, "empty"=>8709, "emsp"=>8195,
"ensp"=>8194, "epsilon"=>949, "equiv"=>8801, "eta"=>951, "eth"=>240,
"euml"=>235, "euro"=>8364, "exist"=>8707, "fnof"=>402, "forall"=>8704,
"frac12"=>189, "frac14"=>188, "frac34"=>190, "frasl"=>8260, "gamma"=>947,
"ge"=>8805, "gt"=>62, "hArr"=>8660, "harr"=>8596, "hearts"=>9829,
"hellip"=>8230, "iacute"=>237, "icirc"=>238, "iexcl"=>161, "igrave"=>236,
"image"=>8465, "infin"=>8734, "int"=>8747, "iota"=>953, "iquest"=>191,
"isin"=>8712, "iuml"=>239, "kappa"=>954, "lArr"=>8656, "lambda"=>955,
"lang"=>9001, "laquo"=>171, "larr"=>8592, "lceil"=>8968, "ldquo"=>8220,
"le"=>8804, "lfloor"=>8970, "lowast"=>8727, "loz"=>9674, "lrm"=>8206,
"lsaquo"=>8249, "lsquo"=>8216, "lt"=>60, "macr"=>175, "mdash"=>8212,
"micro"=>181, "middot"=>183, "minus"=>8722, "mu"=>956, "nabla"=>8711,
"nbsp"=>160, "ndash"=>8211, "ne"=>8800, "ni"=>8715, "not"=>172, "notin"=>8713,
"nsub"=>8836, "ntilde"=>241, "nu"=>957, "oacute"=>243, "ocirc"=>244,
"oelig"=>339, "ograve"=>242, "oline"=>8254, "omega"=>969, "omicron"=>959,
"oplus"=>8853, "or"=>8744, "ordf"=>170, "ordm"=>186, "oslash"=>248,
"otilde"=>245, "otimes"=>8855, "ouml"=>246, "para"=>182, "part"=>8706,
"permil"=>8240, "perp"=>8869, "phi"=>966, "pi"=>960, "piv"=>982,
"plusmn"=>177, "pound"=>163, "prime"=>8242, "prod"=>8719, "prop"=>8733,
"psi"=>968, "quot"=>34, "rArr"=>8658, "radic"=>8730, "rang"=>9002,
"raquo"=>187, "rarr"=>8594, "rceil"=>8969, "rdquo"=>8221, "real"=>8476,
"reg"=>174, "rfloor"=>8971, "rho"=>961, "rlm"=>8207, "rsaquo"=>8250,
"rsquo"=>8217, "sbquo"=>8218, "scaron"=>353, "sdot"=>8901, "sect"=>167,
"shy"=>173, "sigma"=>963, "sigmaf"=>962, "sim"=>8764, "spades"=>9824,
"sub"=>8834, "sube"=>8838, "sum"=>8721, "sup"=>8835, "sup1"=>185, "sup2"=>178,
"sup3"=>179, "supe"=>8839, "szlig"=>223, "tau"=>964, "there4"=>8756,
"theta"=>952, "thetasym"=>977, "thinsp"=>8201, "thorn"=>254, "tilde"=>732,
"times"=>215, "trade"=>8482, "uArr"=>8657, "uacute"=>250, "uarr"=>8593,
"ucirc"=>251, "ugrave"=>249, "uml"=>168, "upsih"=>978, "upsilon"=>965,
"uuml"=>252, "weierp"=>8472, "xi"=>958, "yacute"=>253, "yen"=>165,
"yuml"=>255, "zeta"=>950, "zwj"=>8205, "zwnj"=>8204}
NamedCharactersPattern =
/\A(?-mix:AElig|Aacute|Acirc|Agrave|Alpha|Aring|Atilde|Auml|Beta|Ccedil|Chi|Dagger|Delta|ETH|Eacute|Ecirc|Egrave|Epsilon|Eta|Euml|Gamma|Iacute|Icirc|Igrave|Iota|Iuml|Kappa|Lambda|Mu|Ntilde|Nu|OElig|Oacute|Ocirc|Ograve|Omega|Omicron|Oslash|Otilde|Ouml|Phi|Pi|Prime|Psi|Rho|Scaron|Sigma|THORN|Tau|Theta|Uacute|Ucirc|Ugrave|Upsilon|Uuml|Xi|Yacute|Yuml|Zeta|aacute|acirc|acute|aelig|agrave|alefsym|alpha|amp|and|ang|apos|aring|asymp|atilde|auml|bdquo|beta|brvbar|bull|cap|ccedil|cedil|cent|chi|circ|clubs|cong|copy|crarr|cup|curren|dArr|dagger|darr|deg|delta|diams|divide|eacute|ecirc|egrave|empty|emsp|ensp|epsilon|equiv|eta|eth|euml|euro|exist|fnof|forall|frac12|frac14|frac34|frasl|gamma|ge|gt|hArr|harr|hearts|hellip|iacute|icirc|iexcl|igrave|image|infin|int|iota|iquest|isin|iuml|kappa|lArr|lambda|lang|laquo|larr|lceil|ldquo|le|lfloor|lowast|loz|lrm|lsaquo|lsquo|lt|macr|mdash|micro|middot|minus|mu|nabla|nbsp|ndash|ne|ni|not|notin|nsub|ntilde|nu|oacute|ocirc|oelig|ograve|oline|omega|omicron|oplus|or|ordf|ordm|oslash|otilde|otimes|ouml|para|part|permil|perp|phi|pi|piv|plusmn|pound|prime|prod|prop|psi|quot|rArr|radic|rang|raquo|rarr|rceil|rdquo|real|reg|rfloor|rho|rlm|rsaquo|rsquo|sbquo|scaron|sdot|sect|shy|sigma|sigmaf|sim|spades|sub|sube|sum|sup|sup1|sup2|sup3|supe|szlig|tau|there4|theta|thetasym|thinsp|thorn|tilde|times|trade|uArr|uacute|uarr|ucirc|ugrave|uml|upsih|upsilon|uuml|weierp|xi|yacute|yen|yuml|zeta|zwj|zwnj)\z/
ElementContent =
{"h6"=>
  ["a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br",
"button", "cite", "code", "dfn", "em", "font", "i", "iframe", "img",
"input", "kbd", "label", "map", "object", "q", "s", "samp", "script",
"select", "small", "span", "strike", "strong", "sub", "sup", "textarea",
"tt", "u", "var"],
 "object"=>
  ["a", "abbr", "acronym", "address", "applet", "b", "basefont", "bdo", "big",
"blockquote", "br", "button", "center", "cite", "code", "dfn", "dir", "div",
"dl", "em", "fieldset", "font", "form", "h1", "h2", "h3", "h4", "h5", "h6",
"hr", "i", "iframe", "img", "input", "isindex", "kbd", "label", "map",
"menu", "noframes", "noscript", "object", "ol", "p", "param", "pre", "q",
"s", "samp", "script", "select", "small", "span", "strike", "strong", "sub",
"sup", "table", "textarea", "tt", "u", "ul", "var"],
 "dl"=>["dd", "dt"],
 "p"=>
  ["a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br",
"button", "cite", "code", "dfn", "em", "font", "i", "iframe", "img",
"input", "kbd", "label", "map", "object", "q", "s", "samp", "script",
"select", "small", "span", "strike", "strong", "sub", "sup", "textarea",
"tt", "u", "var"],
 "acronym"=>
  ["a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br",
"button", "cite", "code", "dfn", "em", "font", "i", "iframe", "img",
"input", "kbd", "label", "map", "object", "q", "s", "samp", "script",
"select", "small", "span", "strike", "strong", "sub", "sup", "textarea",
"tt", "u", "var"],
 "code"=>
  ["a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br",
"button", "cite", "code", "dfn", "em", "font", "i", "iframe", "img",
"input", "kbd", "label", "map", "object", "q", "s", "samp", "script",
"select", "small", "span", "strike", "strong", "sub", "sup", "textarea",
"tt", "u", "var"],
 "ul"=>["li"],
 "tt"=>
  ["a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br",
"button", "cite", "code", "dfn", "em", "font", "i", "iframe", "img",
"input", "kbd", "label", "map", "object", "q", "s", "samp", "script",
"select", "small", "span", "strike", "strong", "sub", "sup", "textarea",
"tt", "u", "var"],
 "label"=>
  ["a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br",
"button", "cite", "code", "dfn", "em", "font", "i", "iframe", "img",
"input", "kbd", "label", "map", "object", "q", "s", "samp", "script",
"select", "small", "span", "strike", "strong", "sub", "sup", "textarea",
"tt", "u", "var"],
 "form"=>
  ["a", "abbr", "acronym", "address", "applet", "b", "basefont", "bdo", "big",
"blockquote", "br", "button", "center", "cite", "code", "dfn", "dir", "div",
"dl", "em", "fieldset", "font", "form", "h1", "h2", "h3", "h4", "h5", "h6",
"hr", "i", "iframe", "img", "input", "isindex", "kbd", "label", "map",
"menu", "noframes", "noscript", "object", "ol", "p", "pre", "q", "s",
"samp", "script", "select", "small", "span", "strike", "strong", "sub",
"sup", "table", "textarea", "tt", "u", "ul", "var"],
 "q"=>
  ["a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br",
"button", "cite", "code", "dfn", "em", "font", "i", "iframe", "img",
"input", "kbd", "label", "map", "object", "q", "s", "samp", "script",
"select", "small", "span", "strike", "strong", "sub", "sup", "textarea",
"tt", "u", "var"],
 "thead"=>["tr"],
 "area"=>:EMPTY,
 "td"=>
  ["a", "abbr", "acronym", "address", "applet", "b", "basefont", "bdo", "big",
"blockquote", "br", "button", "center", "cite", "code", "dfn", "dir", "div",
"dl", "em", "fieldset", "font", "form", "h1", "h2", "h3", "h4", "h5", "h6",
"hr", "i", "iframe", "img", "input", "isindex", "kbd", "label", "map",
"menu", "noframes", "noscript", "object", "ol", "p", "pre", "q", "s",
"samp", "script", "select", "small", "span", "strike", "strong", "sub",
"sup", "table", "textarea", "tt", "u", "ul", "var"],
 "title"=>[],
 "dir"=>["li"],
 "s"=>
  ["a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br",
"button", "cite", "code", "dfn", "em", "font", "i", "iframe", "img",
"input", "kbd", "label", "map", "object", "q", "s", "samp", "script",
"select", "small", "span", "strike", "strong", "sub", "sup", "textarea",
"tt", "u", "var"],
 "ol"=>["li"],
 "hr"=>:EMPTY,
 "applet"=>
  ["a", "abbr", "acronym", "address", "applet", "b", "basefont", "bdo", "big",
"blockquote", "br", "button", "center", "cite", "code", "dfn", "dir", "div",
"dl", "em", "fieldset", "font", "form", "h1", "h2", "h3", "h4", "h5", "h6",
"hr", "i", "iframe", "img", "input", "isindex", "kbd", "label", "map",
"menu", "noframes", "noscript", "object", "ol", "p", "param", "pre", "q",
"s", "samp", "script", "select", "small", "span", "strike", "strong", "sub",
"sup", "table", "textarea", "tt", "u", "ul", "var"],
 "table"=>["caption", "col", "colgroup", "tbody", "tfoot", "thead", "tr"],
 "legend"=>
  ["a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br",
"button", "cite", "code", "dfn", "em", "font", "i", "iframe", "img",
"input", "kbd", "label", "map", "object", "q", "s", "samp", "script",
"select", "small", "span", "strike", "strong", "sub", "sup", "textarea",
"tt", "u", "var"],
 "cite"=>
  ["a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br",
"button", "cite", "code", "dfn", "em", "font", "i", "iframe", "img",
"input", "kbd", "label", "map", "object", "q", "s", "samp", "script",
"select", "small", "span", "strike", "strong", "sub", "sup", "textarea",
"tt", "u", "var"],
 "a"=>
  ["a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br",
"button", "cite", "code", "dfn", "em", "font", "i", "iframe", "img",
"input", "kbd", "label", "map", "object", "q", "s", "samp", "script",
"select", "small", "span", "strike", "strong", "sub", "sup", "textarea",
"tt", "u", "var"],
 "html"=>
  ["a", "abbr", "acronym", "address", "applet", "b", "base", "basefont", "bdo",
"big", "blockquote", "body", "br", "button", "center", "cite", "code",
"dfn", "dir", "div", "dl", "em", "fieldset", "font", "form", "h1", "h2",
"h3", "h4", "h5", "h6", "head", "hr", "i", "iframe", "img", "input",
"isindex", "kbd", "label", "map", "menu", "noframes", "noscript", "object",
"ol", "p", "pre", "q", "s", "samp", "script", "select", "small", "span",
"strike", "strong", "sub", "sup", "table", "textarea", "title", "tt", "u",
"ul", "var"],
 "u"=>
  ["a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br",
"button", "cite", "code", "dfn", "em", "font", "i", "iframe", "img",
"input", "kbd", "label", "map", "object", "q", "s", "samp", "script",
"select", "small", "span", "strike", "strong", "sub", "sup", "textarea",
"tt", "u", "var"],
 "blockquote"=>
  ["a", "abbr", "acronym", "address", "applet", "b", "basefont", "bdo", "big",
"blockquote", "br", "button", "center", "cite", "code", "dfn", "dir", "div",
"dl", "em", "fieldset", "font", "form", "h1", "h2", "h3", "h4", "h5", "h6",
"hr", "i", "iframe", "img", "input", "isindex", "kbd", "label", "map",
"menu", "noframes", "noscript", "object", "ol", "p", "pre", "q", "s",
"samp", "script", "select", "small", "span", "strike", "strong", "sub",
"sup", "table", "textarea", "tt", "u", "ul", "var"],
 "center"=>
  ["a", "abbr", "acronym", "address", "applet", "b", "basefont", "bdo", "big",
"blockquote", "br", "button", "center", "cite", "code", "dfn", "dir", "div",
"dl", "em", "fieldset", "font", "form", "h1", "h2", "h3", "h4", "h5", "h6",
"hr", "i", "iframe", "img", "input", "isindex", "kbd", "label", "map",
"menu", "noframes", "noscript", "object", "ol", "p", "pre", "q", "s",
"samp", "script", "select", "small", "span", "strike", "strong", "sub",
"sup", "table", "textarea", "tt", "u", "ul", "var"],
 "b"=>
  ["a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br",
"button", "cite", "code", "dfn", "em", "font", "i", "iframe", "img",
"input", "kbd", "label", "map", "object", "q", "s", "samp", "script",
"select", "small", "span", "strike", "strong", "sub", "sup", "textarea",
"tt", "u", "var"],
 "base"=>:EMPTY,
 "th"=>
  ["a", "abbr", "acronym", "address", "applet", "b", "basefont", "bdo", "big",
"blockquote", "br", "button", "center", "cite", "code", "dfn", "dir", "div",
"dl", "em", "fieldset", "font", "form", "h1", "h2", "h3", "h4", "h5", "h6",
"hr", "i", "iframe", "img", "input", "isindex", "kbd", "label", "map",
"menu", "noframes", "noscript", "object", "ol", "p", "pre", "q", "s",
"samp", "script", "select", "small", "span", "strike", "strong", "sub",
"sup", "table", "textarea", "tt", "u", "ul", "var"],
 "link"=>:EMPTY,
 "var"=>
  ["a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br",
"button", "cite", "code", "dfn", "em", "font", "i", "iframe", "img",
"input", "kbd", "label", "map", "object", "q", "s", "samp", "script",
"select", "small", "span", "strike", "strong", "sub", "sup", "textarea",
"tt", "u", "var"],
 "samp"=>
  ["a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br",
"button", "cite", "code", "dfn", "em", "font", "i", "iframe", "img",
"input", "kbd", "label", "map", "object", "q", "s", "samp", "script",
"select", "small", "span", "strike", "strong", "sub", "sup", "textarea",
"tt", "u", "var"],
 "div"=>
  ["a", "abbr", "acronym", "address", "applet", "b", "basefont", "bdo", "big",
"blockquote", "br", "button", "center", "cite", "code", "dfn", "dir", "div",
"dl", "em", "fieldset", "font", "form", "h1", "h2", "h3", "h4", "h5", "h6",
"hr", "i", "iframe", "img", "input", "isindex", "kbd", "label", "map",
"menu", "noframes", "noscript", "object", "ol", "p", "pre", "q", "s",
"samp", "script", "select", "small", "span", "strike", "strong", "sub",
"sup", "table", "textarea", "tt", "u", "ul", "var"],
 "textarea"=>[],
 "pre"=>
  ["a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br",
"button", "cite", "code", "dfn", "em", "font", "i", "iframe", "img",
"input", "kbd", "label", "map", "object", "q", "s", "samp", "script",
"select", "small", "span", "strike", "strong", "sub", "sup", "textarea",
"tt", "u", "var"],
 "head"=>["base", "isindex", "title"],
 "span"=>
  ["a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br",
"button", "cite", "code", "dfn", "em", "font", "i", "iframe", "img",
"input", "kbd", "label", "map", "object", "q", "s", "samp", "script",
"select", "small", "span", "strike", "strong", "sub", "sup", "textarea",
"tt", "u", "var"],
 "br"=>:EMPTY,
 "script"=>:CDATA,
 "noframes"=>
  ["a", "abbr", "acronym", "address", "applet", "b", "basefont", "bdo", "big",
"blockquote", "br", "button", "center", "cite", "code", "dfn", "dir", "div",
"dl", "em", "fieldset", "font", "form", "h1", "h2", "h3", "h4", "h5", "h6",
"hr", "i", "iframe", "img", "input", "isindex", "kbd", "label", "map",
"menu", "noframes", "noscript", "object", "ol", "p", "pre", "q", "s",
"samp", "script", "select", "small", "span", "strike", "strong", "sub",
"sup", "table", "textarea", "tt", "u", "ul", "var"],
 "style"=>:CDATA,
 "meta"=>:EMPTY,
 "dt"=>
  ["a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br",
"button", "cite", "code", "dfn", "em", "font", "i", "iframe", "img",
"input", "kbd", "label", "map", "object", "q", "s", "samp", "script",
"select", "small", "span", "strike", "strong", "sub", "sup", "textarea",
"tt", "u", "var"],
 "option"=>[],
 "kbd"=>
  ["a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br",
"button", "cite", "code", "dfn", "em", "font", "i", "iframe", "img",
"input", "kbd", "label", "map", "object", "q", "s", "samp", "script",
"select", "small", "span", "strike", "strong", "sub", "sup", "textarea",
"tt", "u", "var"],
 "big"=>
  ["a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br",
"button", "cite", "code", "dfn", "em", "font", "i", "iframe", "img",
"input", "kbd", "label", "map", "object", "q", "s", "samp", "script",
"select", "small", "span", "strike", "strong", "sub", "sup", "textarea",
"tt", "u", "var"],
 "tfoot"=>["tr"],
 "sup"=>
  ["a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br",
"button", "cite", "code", "dfn", "em", "font", "i", "iframe", "img",
"input", "kbd", "label", "map", "object", "q", "s", "samp", "script",
"select", "small", "span", "strike", "strong", "sub", "sup", "textarea",
"tt", "u", "var"],
 "bdo"=>
  ["a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br",
"button", "cite", "code", "dfn", "em", "font", "i", "iframe", "img",
"input", "kbd", "label", "map", "object", "q", "s", "samp", "script",
"select", "small", "span", "strike", "strong", "sub", "sup", "textarea",
"tt", "u", "var"],
 "isindex"=>:EMPTY,
 "dfn"=>
  ["a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br",
"button", "cite", "code", "dfn", "em", "font", "i", "iframe", "img",
"input", "kbd", "label", "map", "object", "q", "s", "samp", "script",
"select", "small", "span", "strike", "strong", "sub", "sup", "textarea",
"tt", "u", "var"],
 "fieldset"=>
  ["a", "abbr", "acronym", "address", "applet", "b", "basefont", "bdo", "big",
"blockquote", "br", "button", "center", "cite", "code", "dfn", "dir", "div",
"dl", "em", "fieldset", "font", "form", "h1", "h2", "h3", "h4", "h5", "h6",
"hr", "i", "iframe", "img", "input", "isindex", "kbd", "label", "legend",
"map", "menu", "noframes", "noscript", "object", "ol", "p", "pre", "q", "s",
"samp", "script", "select", "small", "span", "strike", "strong", "sub",
"sup", "table", "textarea", "tt", "u", "ul", "var"],
 "em"=>
  ["a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br",
"button", "cite", "code", "dfn", "em", "font", "i", "iframe", "img",
"input", "kbd", "label", "map", "object", "q", "s", "samp", "script",
"select", "small", "span", "strike", "strong", "sub", "sup", "textarea",
"tt", "u", "var"],
 "font"=>
  ["a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br",
"button", "cite", "code", "dfn", "em", "font", "i", "iframe", "img",
"input", "kbd", "label", "map", "object", "q", "s", "samp", "script",
"select", "small", "span", "strike", "strong", "sub", "sup", "textarea",
"tt", "u", "var"],
 "tbody"=>["tr"],
 "noscript"=>
  ["a", "abbr", "acronym", "address", "applet", "b", "basefont", "bdo", "big",
"blockquote", "br", "button", "center", "cite", "code", "dfn", "dir", "div",
"dl", "em", "fieldset", "font", "form", "h1", "h2", "h3", "h4", "h5", "h6",
"hr", "i", "iframe", "img", "input", "isindex", "kbd", "label", "map",
"menu", "noframes", "noscript", "object", "ol", "p", "pre", "q", "s",
"samp", "script", "select", "small", "span", "strike", "strong", "sub",
"sup", "table", "textarea", "tt", "u", "ul", "var"],
 "li"=>
  ["a", "abbr", "acronym", "address", "applet", "b", "basefont", "bdo", "big",
"blockquote", "br", "button", "center", "cite", "code", "dfn", "dir", "div",
"dl", "em", "fieldset", "font", "form", "h1", "h2", "h3", "h4", "h5", "h6",
"hr", "i", "iframe", "img", "input", "isindex", "kbd", "label", "map",
"menu", "noframes", "noscript", "object", "ol", "p", "pre", "q", "s",
"samp", "script", "select", "small", "span", "strike", "strong", "sub",
"sup", "table", "textarea", "tt", "u", "ul", "var"],
 "col"=>:EMPTY,
 "small"=>
  ["a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br",
"button", "cite", "code", "dfn", "em", "font", "i", "iframe", "img",
"input", "kbd", "label", "map", "object", "q", "s", "samp", "script",
"select", "small", "span", "strike", "strong", "sub", "sup", "textarea",
"tt", "u", "var"],
 "dd"=>
  ["a", "abbr", "acronym", "address", "applet", "b", "basefont", "bdo", "big",
"blockquote", "br", "button", "center", "cite", "code", "dfn", "dir", "div",
"dl", "em", "fieldset", "font", "form", "h1", "h2", "h3", "h4", "h5", "h6",
"hr", "i", "iframe", "img", "input", "isindex", "kbd", "label", "map",
"menu", "noframes", "noscript", "object", "ol", "p", "pre", "q", "s",
"samp", "script", "select", "small", "span", "strike", "strong", "sub",
"sup", "table", "textarea", "tt", "u", "ul", "var"],
 "i"=>
  ["a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br",
"button", "cite", "code", "dfn", "em", "font", "i", "iframe", "img",
"input", "kbd", "label", "map", "object", "q", "s", "samp", "script",
"select", "small", "span", "strike", "strong", "sub", "sup", "textarea",
"tt", "u", "var"],
 "menu"=>["li"],
 "strong"=>
  ["a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br",
"button", "cite", "code", "dfn", "em", "font", "i", "iframe", "img",
"input", "kbd", "label", "map", "object", "q", "s", "samp", "script",
"select", "small", "span", "strike", "strong", "sub", "sup", "textarea",
"tt", "u", "var"],
 "basefont"=>:EMPTY,
 "img"=>:EMPTY,
 "optgroup"=>["option"],
 "map"=>
  ["address", "area", "blockquote", "center", "dir", "div", "dl", "fieldset",
"form", "h1", "h2", "h3", "h4", "h5", "h6", "hr", "isindex", "menu",
"noframes", "noscript", "ol", "p", "pre", "table", "ul"],
 "h1"=>
  ["a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br",
"button", "cite", "code", "dfn", "em", "font", "i", "iframe", "img",
"input", "kbd", "label", "map", "object", "q", "s", "samp", "script",
"select", "small", "span", "strike", "strong", "sub", "sup", "textarea",
"tt", "u", "var"],
 "address"=>
  ["a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br",
"button", "cite", "code", "dfn", "em", "font", "i", "iframe", "img",
"input", "kbd", "label", "map", "object", "p", "q", "s", "samp", "script",
"select", "small", "span", "strike", "strong", "sub", "sup", "textarea",
"tt", "u", "var"],
 "sub"=>
  ["a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br",
"button", "cite", "code", "dfn", "em", "font", "i", "iframe", "img",
"input", "kbd", "label", "map", "object", "q", "s", "samp", "script",
"select", "small", "span", "strike", "strong", "sub", "sup", "textarea",
"tt", "u", "var"],
 "param"=>:EMPTY,
 "input"=>:EMPTY,
 "h2"=>
  ["a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br",
"button", "cite", "code", "dfn", "em", "font", "i", "iframe", "img",
"input", "kbd", "label", "map", "object", "q", "s", "samp", "script",
"select", "small", "span", "strike", "strong", "sub", "sup", "textarea",
"tt", "u", "var"],
 "abbr"=>
  ["a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br",
"button", "cite", "code", "dfn", "em", "font", "i", "iframe", "img",
"input", "kbd", "label", "map", "object", "q", "s", "samp", "script",
"select", "small", "span", "strike", "strong", "sub", "sup", "textarea",
"tt", "u", "var"],
 "h3"=>
  ["a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br",
"button", "cite", "code", "dfn", "em", "font", "i", "iframe", "img",
"input", "kbd", "label", "map", "object", "q", "s", "samp", "script",
"select", "small", "span", "strike", "strong", "sub", "sup", "textarea",
"tt", "u", "var"],
 "strike"=>
  ["a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br",
"button", "cite", "code", "dfn", "em", "font", "i", "iframe", "img",
"input", "kbd", "label", "map", "object", "q", "s", "samp", "script",
"select", "small", "span", "strike", "strong", "sub", "sup", "textarea",
"tt", "u", "var"],
 "body"=>
  ["a", "abbr", "acronym", "address", "applet", "b", "basefont", "bdo", "big",
"blockquote", "br", "button", "center", "cite", "code", "dfn", "dir", "div",
"dl", "em", "fieldset", "font", "form", "h1", "h2", "h3", "h4", "h5", "h6",
"hr", "i", "iframe", "img", "input", "isindex", "kbd", "label", "map",
"menu", "noframes", "noscript", "object", "ol", "p", "pre", "q", "s",
"samp", "script", "select", "small", "span", "strike", "strong", "sub",
"sup", "table", "textarea", "tt", "u", "ul", "var"],
 "ins"=>
  ["a", "abbr", "acronym", "address", "applet", "b", "basefont", "bdo", "big",
"blockquote", "br", "button", "center", "cite", "code", "dfn", "dir", "div",
"dl", "em", "fieldset", "font", "form", "h1", "h2", "h3", "h4", "h5", "h6",
"hr", "i", "iframe", "img", "input", "isindex", "kbd", "label", "map",
"menu", "noframes", "noscript", "object", "ol", "p", "pre", "q", "s",
"samp", "script", "select", "small", "span", "strike", "strong", "sub",
"sup", "table", "textarea", "tt", "u", "ul", "var"],
 "button"=>
  ["a", "abbr", "acronym", "address", "applet", "b", "basefont", "bdo", "big",
"blockquote", "br", "button", "center", "cite", "code", "dfn", "dir", "div",
"dl", "em", "fieldset", "font", "form", "h1", "h2", "h3", "h4", "h5", "h6",
"hr", "i", "iframe", "img", "input", "isindex", "kbd", "label", "map",
"menu", "noframes", "noscript", "object", "ol", "p", "pre", "q", "s",
"samp", "script", "select", "small", "span", "strike", "strong", "sub",
"sup", "table", "textarea", "tt", "u", "ul", "var"],
 "h4"=>
  ["a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br",
"button", "cite", "code", "dfn", "em", "font", "i", "iframe", "img",
"input", "kbd", "label", "map", "object", "q", "s", "samp", "script",
"select", "small", "span", "strike", "strong", "sub", "sup", "textarea",
"tt", "u", "var"],
 "select"=>["optgroup", "option"],
 "caption"=>
  ["a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br",
"button", "cite", "code", "dfn", "em", "font", "i", "iframe", "img",
"input", "kbd", "label", "map", "object", "q", "s", "samp", "script",
"select", "small", "span", "strike", "strong", "sub", "sup", "textarea",
"tt", "u", "var"],
 "colgroup"=>["col"],
 "tr"=>["td", "th"],
 "del"=>
  ["a", "abbr", "acronym", "address", "applet", "b", "basefont", "bdo", "big",
"blockquote", "br", "button", "center", "cite", "code", "dfn", "dir", "div",
"dl", "em", "fieldset", "font", "form", "h1", "h2", "h3", "h4", "h5", "h6",
"hr", "i", "iframe", "img", "input", "isindex", "kbd", "label", "map",
"menu", "noframes", "noscript", "object", "ol", "p", "pre", "q", "s",
"samp", "script", "select", "small", "span", "strike", "strong", "sub",
"sup", "table", "textarea", "tt", "u", "ul", "var"],
 "h5"=>
  ["a", "abbr", "acronym", "applet", "b", "basefont", "bdo", "big", "br",
"button", "cite", "code", "dfn", "em", "font", "i", "iframe", "img",
"input", "kbd", "label", "map", "object", "q", "s", "samp", "script",
"select", "small", "span", "strike", "strong", "sub", "sup", "textarea",
"tt", "u", "var"],
 "iframe"=>
  ["a", "abbr", "acronym", "address", "applet", "b", "basefont", "bdo", "big",
"blockquote", "br", "button", "center", "cite", "code", "dfn", "dir", "div",
"dl", "em", "fieldset", "font", "form", "h1", "h2", "h3", "h4", "h5", "h6",
"hr", "i", "iframe", "img", "input", "isindex", "kbd", "label", "map",
"menu", "noframes", "noscript", "object", "ol", "p", "pre", "q", "s",
"samp", "script", "select", "small", "span", "strike", "strong", "sub",
"sup", "table", "textarea", "tt", "u", "ul", "var"]}
ElementInclusions =
{"head"=>["link", "meta", "object", "script", "style"], "body"=>["del", "ins"]}
ElementExclusions =
{"button"=>
 ["a", "button", "fieldset", "form", "iframe", "input", "isindex", "label",
  "select", "textarea"],
"a"=>["a"],
"dir"=>
 ["address", "blockquote", "center", "dir", "div", "dl", "fieldset", "form",
  "h1", "h2", "h3", "h4", "h5", "h6", "hr", "isindex", "menu", "noframes",
  "noscript", "ol", "p", "pre", "table", "ul"],
"title"=>["link", "meta", "object", "script", "style"],
"pre"=>
 ["applet", "basefont", "big", "font", "img", "object", "small", "sub",
  "sup"],
"form"=>["form"],
"menu"=>
 ["address", "blockquote", "center", "dir", "div", "dl", "fieldset", "form",
  "h1", "h2", "h3", "h4", "h5", "h6", "hr", "isindex", "menu", "noframes",
  "noscript", "ol", "p", "pre", "table", "ul"],
"label"=>["label"]}
OmittedAttrName =
{"h6"=>
  {"center"=>"align", "justify"=>"align", "left"=>"align", "ltr"=>"dir",
"right"=>"align", "rtl"=>"dir"},
 "object"=>
  {"bottom"=>"align", "declare"=>"declare", "left"=>"align", "ltr"=>"dir",
"middle"=>"align", "right"=>"align", "rtl"=>"dir", "top"=>"align"},
 "dl"=>{"compact"=>"compact", "ltr"=>"dir", "rtl"=>"dir"},
 "p"=>
  {"center"=>"align", "justify"=>"align", "left"=>"align", "ltr"=>"dir",
"right"=>"align", "rtl"=>"dir"},
 "acronym"=>{"ltr"=>"dir", "rtl"=>"dir"},
 "code"=>{"ltr"=>"dir", "rtl"=>"dir"},
 "ul"=>
  {"circle"=>"type", "compact"=>"compact", "disc"=>"type", "ltr"=>"dir",
"rtl"=>"dir", "square"=>"type"},
 "tt"=>{"ltr"=>"dir", "rtl"=>"dir"},
 "label"=>{"ltr"=>"dir", "rtl"=>"dir"},
 "form"=>{"get"=>"method", "ltr"=>"dir", "post"=>"method", "rtl"=>"dir"},
 "q"=>{"ltr"=>"dir", "rtl"=>"dir"},
 "thead"=>
  {"baseline"=>"valign", "bottom"=>"valign", "center"=>"align",
"char"=>"align", "justify"=>"align", "left"=>"align", "ltr"=>"dir",
"middle"=>"valign", "right"=>"align", "rtl"=>"dir", "top"=>"valign"},
 "area"=>
  {"circle"=>"shape", "default"=>"shape", "ltr"=>"dir", "nohref"=>"nohref",
"poly"=>"shape", "rect"=>"shape", "rtl"=>"dir"},
 "td"=>
  {"baseline"=>"valign", "bottom"=>"valign", "center"=>"align",
"char"=>"align", "col"=>"scope", "colgroup"=>"scope", "justify"=>"align",
"left"=>"align", "ltr"=>"dir", "middle"=>"valign", "nowrap"=>"nowrap",
"right"=>"align", "row"=>"scope", "rowgroup"=>"scope", "rtl"=>"dir",
"top"=>"valign"},
 "title"=>{"ltr"=>"dir", "rtl"=>"dir"},
 "dir"=>{"compact"=>"compact", "ltr"=>"dir", "rtl"=>"dir"},
 "s"=>{"ltr"=>"dir", "rtl"=>"dir"},
 "ol"=>{"compact"=>"compact", "ltr"=>"dir", "rtl"=>"dir"},
 "hr"=>
  {"center"=>"align", "left"=>"align", "ltr"=>"dir", "noshade"=>"noshade",
"right"=>"align", "rtl"=>"dir"},
 "applet"=>
  {"bottom"=>"align", "left"=>"align", "middle"=>"align", "right"=>"align",
"top"=>"align"},
 "table"=>
  {"above"=>"frame", "all"=>"rules", "below"=>"frame", "border"=>"frame",
"box"=>"frame", "center"=>"align", "cols"=>"rules", "groups"=>"rules",
"hsides"=>"frame", "left"=>"align", "lhs"=>"frame", "ltr"=>"dir",
"none"=>"rules", "rhs"=>"frame", "right"=>"align", "rows"=>"rules",
"rtl"=>"dir", "void"=>"frame", "vsides"=>"frame"},
 "legend"=>
  {"bottom"=>"align", "left"=>"align", "ltr"=>"dir", "right"=>"align",
"rtl"=>"dir", "top"=>"align"},
 "cite"=>{"ltr"=>"dir", "rtl"=>"dir"},
 "a"=>
  {"circle"=>"shape", "default"=>"shape", "ltr"=>"dir", "poly"=>"shape",
"rect"=>"shape", "rtl"=>"dir"},
 "html"=>{"ltr"=>"dir", "rtl"=>"dir"},
 "u"=>{"ltr"=>"dir", "rtl"=>"dir"},
 "blockquote"=>{"ltr"=>"dir", "rtl"=>"dir"},
 "center"=>{"ltr"=>"dir", "rtl"=>"dir"},
 "b"=>{"ltr"=>"dir", "rtl"=>"dir"},
 "th"=>
  {"baseline"=>"valign", "bottom"=>"valign", "center"=>"align",
"char"=>"align", "col"=>"scope", "colgroup"=>"scope", "justify"=>"align",
"left"=>"align", "ltr"=>"dir", "middle"=>"valign", "nowrap"=>"nowrap",
"right"=>"align", "row"=>"scope", "rowgroup"=>"scope", "rtl"=>"dir",
"top"=>"valign"},
 "link"=>{"ltr"=>"dir", "rtl"=>"dir"},
 "var"=>{"ltr"=>"dir", "rtl"=>"dir"},
 "samp"=>{"ltr"=>"dir", "rtl"=>"dir"},
 "div"=>
  {"center"=>"align", "justify"=>"align", "left"=>"align", "ltr"=>"dir",
"right"=>"align", "rtl"=>"dir"},
 "textarea"=>
  {"disabled"=>"disabled", "ltr"=>"dir", "readonly"=>"readonly", "rtl"=>"dir"},
 "pre"=>{"ltr"=>"dir", "rtl"=>"dir"},
 "head"=>{"ltr"=>"dir", "rtl"=>"dir"},
 "span"=>{"ltr"=>"dir", "rtl"=>"dir"},
 "br"=>{"all"=>"clear", "left"=>"clear", "none"=>"clear", "right"=>"clear"},
 "script"=>{"defer"=>"defer"},
 "noframes"=>{"ltr"=>"dir", "rtl"=>"dir"},
 "style"=>{"ltr"=>"dir", "rtl"=>"dir"},
 "meta"=>{"ltr"=>"dir", "rtl"=>"dir"},
 "dt"=>{"ltr"=>"dir", "rtl"=>"dir"},
 "option"=>
  {"disabled"=>"disabled", "ltr"=>"dir", "rtl"=>"dir", "selected"=>"selected"},
 "kbd"=>{"ltr"=>"dir", "rtl"=>"dir"},
 "big"=>{"ltr"=>"dir", "rtl"=>"dir"},
 "tfoot"=>
  {"baseline"=>"valign", "bottom"=>"valign", "center"=>"align",
"char"=>"align", "justify"=>"align", "left"=>"align", "ltr"=>"dir",
"middle"=>"valign", "right"=>"align", "rtl"=>"dir", "top"=>"valign"},
 "sup"=>{"ltr"=>"dir", "rtl"=>"dir"},
 "bdo"=>{"ltr"=>"dir", "rtl"=>"dir"},
 "isindex"=>{"ltr"=>"dir", "rtl"=>"dir"},
 "dfn"=>{"ltr"=>"dir", "rtl"=>"dir"},
 "fieldset"=>{"ltr"=>"dir", "rtl"=>"dir"},
 "em"=>{"ltr"=>"dir", "rtl"=>"dir"},
 "font"=>{"ltr"=>"dir", "rtl"=>"dir"},
 "tbody"=>
  {"baseline"=>"valign", "bottom"=>"valign", "center"=>"align",
"char"=>"align", "justify"=>"align", "left"=>"align", "ltr"=>"dir",
"middle"=>"valign", "right"=>"align", "rtl"=>"dir", "top"=>"valign"},
 "noscript"=>{"ltr"=>"dir", "rtl"=>"dir"},
 "li"=>{"ltr"=>"dir", "rtl"=>"dir"},
 "col"=>
  {"baseline"=>"valign", "bottom"=>"valign", "center"=>"align",
"char"=>"align", "justify"=>"align", "left"=>"align", "ltr"=>"dir",
"middle"=>"valign", "right"=>"align", "rtl"=>"dir", "top"=>"valign"},
 "small"=>{"ltr"=>"dir", "rtl"=>"dir"},
 "dd"=>{"ltr"=>"dir", "rtl"=>"dir"},
 "i"=>{"ltr"=>"dir", "rtl"=>"dir"},
 "menu"=>{"compact"=>"compact", "ltr"=>"dir", "rtl"=>"dir"},
 "strong"=>{"ltr"=>"dir", "rtl"=>"dir"},
 "img"=>
  {"bottom"=>"align", "ismap"=>"ismap", "left"=>"align", "ltr"=>"dir",
"middle"=>"align", "right"=>"align", "rtl"=>"dir", "top"=>"align"},
 "optgroup"=>{"disabled"=>"disabled", "ltr"=>"dir", "rtl"=>"dir"},
 "map"=>{"ltr"=>"dir", "rtl"=>"dir"},
 "address"=>{"ltr"=>"dir", "rtl"=>"dir"},
 "h1"=>
  {"center"=>"align", "justify"=>"align", "left"=>"align", "ltr"=>"dir",
"right"=>"align", "rtl"=>"dir"},
 "sub"=>{"ltr"=>"dir", "rtl"=>"dir"},
 "param"=>{"data"=>"valuetype", "object"=>"valuetype", "ref"=>"valuetype"},
 "input"=>
  {"bottom"=>"align", "button"=>"type", "checkbox"=>"type",
"checked"=>"checked", "disabled"=>"disabled", "file"=>"type",
"hidden"=>"type", "image"=>"type", "ismap"=>"ismap", "left"=>"align",
"ltr"=>"dir", "middle"=>"align", "password"=>"type", "radio"=>"type",
"readonly"=>"readonly", "reset"=>"type", "right"=>"align", "rtl"=>"dir",
"submit"=>"type", "text"=>"type", "top"=>"align"},
 "h2"=>
  {"center"=>"align", "justify"=>"align", "left"=>"align", "ltr"=>"dir",
"right"=>"align", "rtl"=>"dir"},
 "abbr"=>{"ltr"=>"dir", "rtl"=>"dir"},
 "h3"=>
  {"center"=>"align", "justify"=>"align", "left"=>"align", "ltr"=>"dir",
"right"=>"align", "rtl"=>"dir"},
 "strike"=>{"ltr"=>"dir", "rtl"=>"dir"},
 "body"=>{"ltr"=>"dir", "rtl"=>"dir"},
 "ins"=>{"ltr"=>"dir", "rtl"=>"dir"},
 "button"=>
  {"button"=>"type", "disabled"=>"disabled", "ltr"=>"dir", "reset"=>"type",
"rtl"=>"dir", "submit"=>"type"},
 "h4"=>
  {"center"=>"align", "justify"=>"align", "left"=>"align", "ltr"=>"dir",
"right"=>"align", "rtl"=>"dir"},
 "select"=>
  {"disabled"=>"disabled", "ltr"=>"dir", "multiple"=>"multiple", "rtl"=>"dir"},
 "caption"=>
  {"bottom"=>"align", "left"=>"align", "ltr"=>"dir", "right"=>"align",
"rtl"=>"dir", "top"=>"align"},
 "colgroup"=>
  {"baseline"=>"valign", "bottom"=>"valign", "center"=>"align",
"char"=>"align", "justify"=>"align", "left"=>"align", "ltr"=>"dir",
"middle"=>"valign", "right"=>"align", "rtl"=>"dir", "top"=>"valign"},
 "tr"=>
  {"baseline"=>"valign", "bottom"=>"valign", "center"=>"align",
"char"=>"align", "justify"=>"align", "left"=>"align", "ltr"=>"dir",
"middle"=>"valign", "right"=>"align", "rtl"=>"dir", "top"=>"valign"},
 "del"=>{"ltr"=>"dir", "rtl"=>"dir"},
 "h5"=>
  {"center"=>"align", "justify"=>"align", "left"=>"align", "ltr"=>"dir",
"right"=>"align", "rtl"=>"dir"},
 "iframe"=>
  {"0"=>"frameborder", "1"=>"frameborder", "auto"=>"scrolling",
"bottom"=>"align", "left"=>"align", "middle"=>"align", "no"=>"scrolling",
"right"=>"align", "top"=>"align", "yes"=>"scrolling"}}
EmptyBindingObject =

:stopdoc:

Object.new

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.build_node(structure, is_xml, is_html, inherited_context = DefaultContext) ⇒ Object



198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
# File 'lib/htree/parse.rb', line 198

def HTree.build_node(structure, is_xml, is_html, inherited_context=DefaultContext)
  case structure[0]
  when :text_pcdata
    Text.parse_pcdata(structure[1])
  when :elem
    _, stag_rawstring, children, etag_rawstring = structure
    etag = etag_rawstring && ETag.parse(etag_rawstring, is_xml, is_html)
    stag = STag.parse(stag_rawstring, true, is_xml, is_html, inherited_context)
    if !children.empty? || etag ||
       stag.element_name.namespace_uri != 'http://www.w3.org/1999/xhtml' ||
       HTree::ElementContent[stag.element_name.local_name] != :EMPTY
      Elem.new!(stag,
                children.map {|c| build_node(c, is_xml, is_html, stag.context) },
                etag)
    else
      Elem.new!(stag)
    end
  when :emptytag
    Elem.new!(STag.parse(structure[1], false, is_xml, is_html, inherited_context))
  when :bogus_etag
    BogusETag.parse(structure[1], is_xml, is_html)
  when :xmldecl
    XMLDecl.parse(structure[1])
  when :doctype
    DocType.parse(structure[1], is_xml, is_html)
  when :procins
    ProcIns.parse(structure[1])
  when :comment
    Comment.parse(structure[1])
  when :text_cdata_content
    Text.parse_cdata_content(structure[1])
  when :text_cdata_section
    Text.parse_cdata_section(structure[1])
  else
    raise Exception, "[bug] unknown structure: #{structure.inspect}"
  end
end

.compile_template(template_string) ⇒ Object

call-seq:

HTree.compile_template(template_string) -> module

HTree.compile_template(template_string) compiles template_string as a template.

HTree.compile_template returns a module. The module has module functions for each templates defined in template_string. The returned module can be used for include.

M = HTree.compile_template(<<'End')
<p _template=birthday(subj,t)>
  <span _text=subj />'s birthday is <span _text="t.strftime('%B %dth %Y')"/>.
</p>
End
M.birthday('Ruby', Time.utc(1993, 2, 24)).display_xml
# <p>Ruby's birthday is February 24th 1993.</p>

The module function takes arguments specifies by a _template attribute and returns a tree represented as HTree::Node.



417
418
419
420
421
422
423
424
425
426
# File 'lib/htree/template.rb', line 417

def HTree.compile_template(template_string)
  code = HTree::TemplateCompiler.new.compile_template(template_string)
  Thread.current[:htree_compile_template_code] = code
  mod = eval(<<-'End',
      eval(Thread.current[:htree_compile_template_code])
    End
    HTree::EmptyBindingObject.empty_binding, "(eval:#{__FILE__}:#{__LINE__})")
  Thread.current[:htree_compile_template_code] = nil
  mod
end

.expand_template(*args, &block) ⇒ Object

call-seq:

HTree.expand_template(template_pathname, obj=Object.new, out=$stdout, encoding=internal_encoding) -> out
HTree.expand_template(out=$stdout, encoding=internal_encoding) { template_string } -> out

HTree.expand_template expands a template.

The arguments should be specified as follows. All argument except pathname are optional.

  • HTree.expand_template(pathname, obj, out, encoding) -> out

  • HTree.expand_template(out, encoding) <em>template_string</em> -> out

The template is specified by a file or a string. If a block is not given, the first argument represent a template pathname. Otherwise, the block is yielded and its value is interpreted as a template string. So it can be called as follows in simplest case.

  • HTree.expand_template(template_pathname)

  • HTree.expand_template<em>template_string</em>

Ruby expressions in the template file specified by template_pathname are evaluated in the context of the optional second argument obj as follows. I.e. the pseudo variable self in the expressions is bound to obj.

HTree.expand_template(template_pathname, obj)

Ruby expressions in the template_string are evaluated in the context of the caller of HTree.expand_template. (binding information is specified by the block.) I.e. they can access local variables etc. We recommend to specify template_string as a literal string without interpolation because dynamically generated string may break lexical scope.

HTree.expand_template has two more optional arguments: out, encoding.

out specifies output target. It should have << method: IO and String for example. If it is not specified, $stdout is used. If it has a method charset=, it is called to set the minimal charset of the result before << is called.

encoding specifies output character encoding. If it is not specified, internal encoding is used.

HTree.expand_template returns out or $stdout if out is not specified.



341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
# File 'lib/htree/template.rb', line 341

def HTree.expand_template(*args, &block)
  if block
    template = block.call
    binding = block.binding
  else
    pathname = args.fetch(0) { raise ArgumentError, "pathname not given" }
    args.shift
    obj = args.fetch(0) { Object.new }
    args.shift
    if pathname.respond_to? :read
      template = pathname.read.untaint
      if template.respond_to? :charset
        template = Iconv.conv(HTree::Encoder.internal_charset, template.charset, template)
      end
    else
      template = File.read(pathname).untaint
    end
    Thread.current[:htree_expand_template_obj] = obj
    binding = eval(<<-'End',
        Thread.current[:htree_expand_template_obj].class.class_eval <<-'EE'
          Thread.current[:htree_expand_template_obj].instance_eval { binding }
        EE
      End
      HTree::EmptyBindingObject.empty_binding, "(eval:#{__FILE__}:#{__LINE__})")
    Thread.current[:htree_expand_template_obj] = nil
  end

  out = args.shift || $stdout
  encoding = args.shift || HTree::Encoder.internal_charset
  if !args.empty?
    raise ArgumentError, "wrong number of arguments" 
  end
  HTree::TemplateCompiler.new.expand_template(template, out, encoding, binding)
end

.fix_element(elem, excluded_tags, included_tags, is_xml, is_html) ⇒ Object



146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/htree/parse.rb', line 146

def HTree.fix_element(elem, excluded_tags, included_tags, is_xml, is_html)
  stag_raw_string = elem[1]
  children = elem[2]
  if etag_raw_string = elem[3]
    return [:elem, stag_raw_string, fix_structure_list(children, is_xml, is_html), etag_raw_string], []
  else
    tagname = stag_raw_string[Pat::Name]
    tagname = tagname.downcase if !is_xml && is_html
    if ElementContent[tagname] == :EMPTY
      return [:elem, stag_raw_string, []], children
    else
      if ElementContent[tagname] == :CDATA
        possible_tags = []
      else
        possible_tags = ElementContent[tagname]
      end
      if possible_tags
        excluded_tags2 = ElementExclusions[tagname]
        included_tags2 = ElementInclusions[tagname]
        excluded_tags |= excluded_tags2 if excluded_tags2
        included_tags |= included_tags2 if included_tags2
        containable_tags = (possible_tags | included_tags) - excluded_tags
        uncontainable_tags = ElementContent.keys - containable_tags
      else
        # If the tagname is unknown, it is assumed that any element
        # except excluded can be contained.
        uncontainable_tags = excluded_tags
      end
      fixed_children = []
      rest = children
      until rest.empty?
        if rest[0][0] == :elem
          elem = rest.shift
          elem_tagname = elem[1][Pat::Name]
          elem_tagname = elem_tagname.downcase if !is_xml && is_html
          if uncontainable_tags.include? elem_tagname
            rest.unshift elem
            break
          else
            fixed_elem, rest2 = fix_element(elem, excluded_tags, included_tags, is_xml, is_html)
            fixed_children << fixed_elem
            rest = rest2 + rest
          end
        else
          fixed_children << rest.shift
        end
      end
      return [:elem, stag_raw_string, fixed_children], rest
    end
  end
end

.fix_structure_list(structure_list, is_xml, is_html) ⇒ Object



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/htree/parse.rb', line 130

def HTree.fix_structure_list(structure_list, is_xml, is_html)
  result = []
  rest = structure_list.dup
  until rest.empty?
    structure = rest.shift
    if structure[0] == :elem
      elem, rest2 = fix_element(structure, [], [], is_xml, is_html)
      result << elem
      rest = rest2 + rest
    else
      result << structure
    end
  end
  result
end

.frozen_string(str) ⇒ Object



18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/htree/fstr.rb', line 18

def HTree.frozen_string(str)
  if h = Thread.current[:htree_frozen_string_hash]
    if s = h[str]
      s
    else
      str = str.dup.freeze unless str.frozen?
      h[str] = str
    end
  else
    str = str.dup.freeze unless str.frozen?
    str
  end
end

.parse(input) ⇒ Object

HTree.parse parses input and return a document tree. represented by HTree::Doc.

input should be a String or an object which respond to read or open method. For example, IO, StringIO, Pathname, URI::HTTP and URI::FTP are acceptable. Note that the URIs need open-uri.

HTree.parse guesses input is HTML or not and XML or not.

If it is guessed as HTML, the default namespace in the result is set to www.w3.org/1999/xhtml regardless of input has XML namespace declaration or not nor even it is pre-XML HTML.

If it is guessed as HTML and not XML, all element and attribute names are downcaseed.

If opened file or read content has charset method, HTree.parse decode it according to $KCODE before parsing. Otherwise HTree.parse assumes the character encoding of the content is compatible to $KCODE. Note that the charset method is provided by URI::HTTP with open-uri.



34
35
36
37
38
# File 'lib/htree/parse.rb', line 34

def HTree.parse(input)
  HTree.with_frozen_string_hash {
    parse_as(input, false)
  }
end

.parse_as(input, is_xml) ⇒ Object

:stopdoc:



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/htree/parse.rb', line 56

def HTree.parse_as(input, is_xml)
  input_charset = nil
  if input.tainted? && 1 <= $SAFE
    raise SecurityError, "input tainted"
  end
  if input.respond_to? :read # IO, StringIO
    input = input.read.untaint
    input_charset = input.charset if input.respond_to? :charset
  elsif input.respond_to? :open # Pathname, URI with open-uri
    input.open {|f|
      input = f.read.untaint
      input_charset = f.charset if f.respond_to? :charset
    }
  end
  if input_charset && input_charset != Encoder.internal_charset
    input = Iconv.conv(Encoder.internal_charset, input_charset, input)
  end

  tokens = []
  is_xml, is_html = HTree.scan(input, is_xml) {|token|
    tokens << token
  }
  context = is_html ? HTMLContext : DefaultContext
  structure_list = parse_pairs(tokens, is_xml, is_html)
  structure_list = fix_structure_list(structure_list, is_xml, is_html)
  nodes = structure_list.map {|s| build_node(s, is_xml, is_html, context) }
  Doc.new(nodes)
end

.parse_pairs(tokens, is_xml, is_html) ⇒ Object



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/htree/parse.rb', line 85

def HTree.parse_pairs(tokens, is_xml, is_html)
  stack = [[nil, nil, []]]
  tokens.each {|token|
    case token[0]
    when :stag
      stag_raw_string = token[1]
      stagname = stag_raw_string[Pat::Name]
      stagname = stagname.downcase if !is_xml && is_html
      stagname = HTree.frozen_string(stagname)
      stack << [stagname, stag_raw_string, []]
    when :etag
      etag_raw_string = token[1]
      etagname = etag_raw_string[Pat::Name]
      etagname = etagname.downcase if !is_xml && is_html
      etagname = HTree.frozen_string(etagname)
      matched_elem = nil
      stack.reverse_each {|elem|
        stagname, _, _ = elem
        if stagname == etagname
          matched_elem = elem
          break
        end
      }
      if matched_elem
        until matched_elem.equal? stack.last
          stagname, stag_raw_string, children = stack.pop
          stack.last[2] << [:elem, stag_raw_string, children]
        end
        stagname, stag_raw_string, children = stack.pop
        stack.last[2] << [:elem, stag_raw_string, children, etag_raw_string]
      else
        stack.last[2] << [:bogus_etag, etag_raw_string]
      end
    else
      stack.last[2] << token
    end
  }
  elem = nil
  while 1 < stack.length
    stagname, stag_raw_string, children = stack.pop
    stack.last[2] << [:elem, stag_raw_string, children]
  end
  stack[0][2]
end

.parse_xml(input) ⇒ Object

HTree.parse_xml parses input as XML and return a document tree represented by HTree::Doc.

It behaves almost same as HTree.parse but it assumes input is XML even if no XML declaration. The assumption causes following differences.

  • doesn’t downcase element name.

  • The content of <script> and <style> element is PCDATA, not CDATA.



48
49
50
51
52
# File 'lib/htree/parse.rb', line 48

def HTree.parse_xml(input)
  HTree.with_frozen_string_hash {
    parse_as(input, true)
  }
end

.scan(input, is_xml = false) ⇒ Object



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/htree/scan.rb', line 68

def HTree.scan(input, is_xml=false)
  is_html = false
  cdata_content = nil
  cdata_content_string = nil
  pcdata = ''
  first_element = true
  index_otherstring = 1
  index_str = 2
  index_xmldecl = 3
  index_doctype = 4
  index_xmlprocins = 5
  index_quotedstarttag = 6
  index_quotedemptytag = 7
  index_starttag = 8
  index_endtag = 9
  index_emptytag = 10
  index_comment = 11
  index_cdata = 12
  index_end = 13
  pat = /\G(.*?)((#{Pat::XmlDecl})
                |(#{Pat::DocType})
                |(#{Pat::XmlProcIns})
                |(#{Pat::QuotedStartTag})
                |(#{Pat::QuotedEmptyTag})
                |(#{Pat::StartTag})
                |(#{Pat::EndTag})
                |(#{Pat::EmptyTag})
                |(#{Pat::Comment})
                |(#{Pat::CDATA})
                |(\z))
        /oxm
  input.scan(pat) {
    match = $~
    if cdata_content
      cdata_content_string << match[index_otherstring]
      str = match[index_str]
      if match[index_endtag] && str[Pat::Name] == cdata_content
        unless cdata_content_string.empty?
          yield [:text_cdata_content, HTree.frozen_string(cdata_content_string)]
        end
        yield [:etag, HTree.frozen_string(str)]
        cdata_content = nil
        cdata_content_string = nil
      elsif match[index_end]
        cdata_content_string << str
        unless cdata_content_string.empty?
          yield [:text_cdata_content, HTree.frozen_string(cdata_content_string)]
        end
        cdata_content = nil
        cdata_content_string = nil
      else
        cdata_content_string << str
      end
    else
      pcdata << match[index_otherstring]
      str = match[index_str]
      if !pcdata.empty?
        yield [:text_pcdata, HTree.frozen_string(pcdata)]
        pcdata = ''
      end
      if match[index_xmldecl]
        yield [:xmldecl, HTree.frozen_string(str)]
        is_xml = true
      elsif match[index_doctype]
        Pat::DocType_C =~ str
        root_element_name = $1
        public_identifier = $2 || $3
        system_identifier = $4 || $5
        is_html = true if /\Ahtml\z/i =~ root_element_name
        is_xml = true if public_identifier && %r{\A-//W3C//DTD XHTML } =~ public_identifier
        yield [:doctype, HTree.frozen_string(str)]
      elsif match[index_xmlprocins]
        yield [:procins, HTree.frozen_string(str)]
      elsif match[index_starttag] || match[index_quotedstarttag]
        yield stag = [:stag, HTree.frozen_string(str)]
        tagname = str[Pat::Name]
        if first_element
          if /\A(?:html|head|title|isindex|base|script|style|meta|link|object)\z/i =~ tagname
            is_html = true
          else
            is_xml = true
          end
          first_element = false
        end
        if !is_xml && ElementContent[tagname] == :CDATA
          cdata_content = tagname
          cdata_content_string = ''
        end
      elsif match[index_endtag]
        yield [:etag, HTree.frozen_string(str)]
      elsif match[index_emptytag] || match[index_quotedemptytag]
        yield [:emptytag, HTree.frozen_string(str)]
        first_element = false
        #is_xml = true
      elsif match[index_comment]
        yield [:comment, HTree.frozen_string(str)]
      elsif match[index_cdata]
        yield [:text_cdata_section, HTree.frozen_string(str)]
      elsif match[index_end]
        # pass
      else
        raise Exception, "unknown match [bug]"
      end
    end
  }
  return is_xml, is_html
end

.with_frozen_string_hashObject

:stopdoc:



5
6
7
8
9
10
11
12
13
14
15
16
# File 'lib/htree/fstr.rb', line 5

def HTree.with_frozen_string_hash
  if Thread.current[:htree_frozen_string_hash]
    yield
  else
    begin
      Thread.current[:htree_frozen_string_hash] = {}
      yield
    ensure
      Thread.current[:htree_frozen_string_hash] = nil
    end
  end
end

Instance Method Details

#==(other) ⇒ Object Also known as: eql?

compare tree structures.



10
11
12
# File 'lib/htree/equality.rb', line 10

def ==(other)
  check_equality(self, other, :usual_equal_object)
end

#check_equality(obj1, obj2, equal_object_method) ⇒ Object



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/htree/equality.rb', line 45

def check_equality(obj1, obj2, equal_object_method)
  return false unless obj1.class == obj2.class
  if obj1.class == Array
    return false unless obj1.length == obj2.length
    obj1.each_with_index {|c1, i|
      return false unless c1.class == obj2[i].class
    }
    obj1.each_with_index {|c1, i|
      return false unless check_equality(c1, obj2[i], equal_object_method)
    }
    true
  elsif obj1.respond_to? equal_object_method
    o1 = obj1.send(equal_object_method)
    o2 = obj2.send(equal_object_method)
    check_equality(o1, o2, equal_object_method)
  else
    obj1 == obj2
  end
end

#exact_equal?(other) ⇒ Boolean

Returns:

  • (Boolean)


41
42
43
# File 'lib/htree/equality.rb', line 41

def exact_equal?(other)
  check_equality(self, other, :exact_equal_object)
end

#exact_equal_objectObject



32
33
34
35
# File 'lib/htree/equality.rb', line 32

def exact_equal_object
  return @exact_equal_object if defined? @exact_equal_object
  @exact_equal_object = make_exact_equal_object
end

#hashObject

hash value for the tree structure.



16
17
18
19
# File 'lib/htree/equality.rb', line 16

def hash
  return @hash_code if defined? @hash_code
  @hash_code = usual_equal_object.hash
end

#make_exact_equal_objectObject

Raises:

  • (NotImplementedError)


37
38
39
# File 'lib/htree/equality.rb', line 37

def make_exact_equal_object
  raise NotImplementedError
end

#make_usual_equal_objectObject

Raises:

  • (NotImplementedError)


28
29
30
# File 'lib/htree/equality.rb', line 28

def make_usual_equal_object
  raise NotImplementedError
end

#usual_equal_objectObject

:stopdoc:



23
24
25
26
# File 'lib/htree/equality.rb', line 23

def usual_equal_object
  return @usual_equal_object if defined? @usual_equal_object
  @usual_equal_object = make_usual_equal_object
end