# frozen_string_literal: true
# Base module for HTML-generation mixins.
# Provides methods for code generation for tags following
# the various DTD element types.
module TagMaker # :nodoc:
# Generate code for an element with required start and end tags.
def nn_element(element, attributes = {})
s = nOE_element(element, attributes)
s << "</#{element.upcase}>"
def nn_element_def(attributes = {}, &block)
nn_element(__callee__, attributes, &block)
# Generate code for an empty element.
def nOE_element(element, attributes = {})
attributes={attributes=>nil} if attributes.kind_of?(String)
s = "<#{element.upcase}".dup
attributes.each do|name, value|
s << CGI.escapeHTML(name.to_s)
s << CGI.escapeHTML(value.to_s)
def nOE_element_def(attributes = {}, &block)
nOE_element(__callee__, attributes, &block)
# Generate code for an element for which the end (and possibly the
# start) tag is optional.
def nO_element(element, attributes = {})
s = nOE_element(element, attributes)
s << "</#{element.upcase}>"
def nO_element_def(attributes = {}, &block)
nO_element(__callee__, attributes, &block)
# Mixin module providing HTML generation methods.
# cgi.a("http://www.example.com") { "Example" }
# # => "<A HREF=\"http://www.example.com\">Example</A>"
# Modules Html3, Html4, etc., contain more basic HTML-generation methods
# (+#title+, +#h1+, etc.).
# See class CGI for a detailed example.
# Generate an Anchor element as a string.
# +href+ can either be a string, giving the URL
# for the HREF attribute, or it can be a hash of
# the element's attributes.
# The body of the element is the string returned by the no-argument
# a("http://www.example.com") { "Example" }
# # => "<A HREF=\"http://www.example.com\">Example</A>"
# a("HREF" => "http://www.example.com", "TARGET" => "_top") { "Example" }
# # => "<A HREF=\"http://www.example.com\" TARGET=\"_top\">Example</A>"
def a(href = "") # :yield:
attributes = if href.kind_of?(String)
# Generate a Document Base URI element as a String.
# +href+ can either by a string, giving the base URL for the HREF
# attribute, or it can be a has of the element's attributes.
# The passed-in no-argument block is ignored.
# base("http://www.example.com/cgi")
# # => "<BASE HREF=\"http://www.example.com/cgi\">"
def base(href = "") # :yield:
attributes = if href.kind_of?(String)
# Generate a BlockQuote element as a string.
# +cite+ can either be a string, give the URI for the source of
# the quoted text, or a hash, giving all attributes of the element,
# or it can be omitted, in which case the element has no attributes.
# The body is provided by the passed-in no-argument block
# blockquote("http://www.example.com/quotes/foo.html") { "Foo!" }
# #=> "<BLOCKQUOTE CITE=\"http://www.example.com/quotes/foo.html\">Foo!</BLOCKQUOTE>
def blockquote(cite = {}) # :yield:
attributes = if cite.kind_of?(String)
# Generate a Table Caption element as a string.
# +align+ can be a string, giving the alignment of the caption
# (one of top, bottom, left, or right). It can be a hash of
# all the attributes of the element. Or it can be omitted.
# The body of the element is provided by the passed-in no-argument block.
# caption("left") { "Capital Cities" }
# # => <CAPTION ALIGN=\"left\">Capital Cities</CAPTION>
def caption(align = {}) # :yield:
attributes = if align.kind_of?(String)
# Generate a Checkbox Input element as a string.
# The attributes of the element can be specified as three arguments,
# +name+, +value+, and +checked+. +checked+ is a boolean value;
# if true, the CHECKED attribute will be included in the element.
# Alternatively, the attributes can be specified as a hash.
# # = checkbox("NAME" => "name")
# checkbox("name", "value")
# # = checkbox("NAME" => "name", "VALUE" => "value")
# checkbox("name", "value", true)
# # = checkbox("NAME" => "name", "VALUE" => "value", "CHECKED" => true)
def checkbox(name = "", value = nil, checked = nil)
attributes = if name.kind_of?(String)
{ "TYPE" => "checkbox", "NAME" => name,
"VALUE" => value, "CHECKED" => checked }
name["TYPE"] = "checkbox"
# Generate a sequence of checkbox elements, as a String.
# The checkboxes will all have the same +name+ attribute.
# Each checkbox is followed by a label.
# There will be one checkbox for each value. Each value
# can be specified as a String, which will be used both
# as the value of the VALUE attribute and as the label
# for that checkbox. A single-element array has the
# Each value can also be specified as a three-element array.
# The first element is the VALUE attribute; the second is the
# label; and the third is a boolean specifying whether this
# Each value can also be specified as a two-element
# array, by omitting either the value element (defaults
# to the same as the label), or the boolean checked element
# checkbox_group("name", "foo", "bar", "baz")
# # <INPUT TYPE="checkbox" NAME="name" VALUE="foo">foo
# # <INPUT TYPE="checkbox" NAME="name" VALUE="bar">bar
# # <INPUT TYPE="checkbox" NAME="name" VALUE="baz">baz
# checkbox_group("name", ["foo"], ["bar", true], "baz")
# # <INPUT TYPE="checkbox" NAME="name" VALUE="foo">foo
# # <INPUT TYPE="checkbox" CHECKED NAME="name" VALUE="bar">bar
# # <INPUT TYPE="checkbox" NAME="name" VALUE="baz">baz
# checkbox_group("name", ["1", "Foo"], ["2", "Bar", true], "Baz")
# # <INPUT TYPE="checkbox" NAME="name" VALUE="1">Foo
# # <INPUT TYPE="checkbox" CHECKED NAME="name" VALUE="2">Bar
# # <INPUT TYPE="checkbox" NAME="name" VALUE="Baz">Baz
# checkbox_group("NAME" => "name",
# "VALUES" => ["foo", "bar", "baz"])
# checkbox_group("NAME" => "name",
# "VALUES" => [["foo"], ["bar", true], "baz"])
# checkbox_group("NAME" => "name",
# "VALUES" => [["1", "Foo"], ["2", "Bar", true], "Baz"])
def checkbox_group(name = "", *values)
if value.kind_of?(String)
checkbox(name, value) + value
if value[-1] == true || value[-1] == false
checkbox(name, value[0], value[-1]) +
checkbox(name, value[0]) +
# Generate an File Upload Input element as a string.
# The attributes of the element can be specified as three arguments,
# +name+, +size+, and +maxlength+. +maxlength+ is the maximum length
# of the file's _name_, not of the file's _contents_.
# Alternatively, the attributes can be specified as a hash.
# See #multipart_form() for forms that include file uploads.
# # <INPUT TYPE="file" NAME="name" SIZE="20">
# # <INPUT TYPE="file" NAME="name" SIZE="40">
# file_field("name", 40, 100)
# # <INPUT TYPE="file" NAME="name" SIZE="40" MAXLENGTH="100">
# file_field("NAME" => "name", "SIZE" => 40)
# # <INPUT TYPE="file" NAME="name" SIZE="40">
def file_field(name = "", size = 20, maxlength = nil)
attributes = if name.kind_of?(String)
{ "TYPE" => "file", "NAME" => name,
attributes["MAXLENGTH"] = maxlength.to_s if maxlength
# Generate a Form element as a string.
# +method+ should be either "get" or "post", and defaults to the latter.
# +action+ defaults to the current CGI script name. +enctype+
# defaults to "application/x-www-form-urlencoded".
# Alternatively, the attributes can be specified as a hash.
# See also #multipart_form() for forms that include file uploads.
# # <FORM METHOD="post" ENCTYPE="application/x-www-form-urlencoded">string</FORM>
# form("get") { "string" }
# # <FORM METHOD="get" ENCTYPE="application/x-www-form-urlencoded">string</FORM>
# form("get", "url") { "string" }
# # <FORM METHOD="get" ACTION="url" ENCTYPE="application/x-www-form-urlencoded">string</FORM>
# form("METHOD" => "post", "ENCTYPE" => "enctype") { "string" }
# # <FORM METHOD="post" ENCTYPE="enctype">string</FORM>
def form(method = "post", action = script_name, enctype = "application/x-www-form-urlencoded")
attributes = if method.kind_of?(String)
{ "METHOD" => method, "ACTION" => action,
unless method.has_key?("METHOD")
method["METHOD"] = "post"
unless method.has_key?("ENCTYPE")
method["ENCTYPE"] = enctype
body << @output_hidden.collect{|k,v|
"<INPUT TYPE=\"HIDDEN\" NAME=\"#{k}\" VALUE=\"#{v}\">"
# Generate a Hidden Input element as a string.
# The attributes of the element can be specified as two arguments,
# Alternatively, the attributes can be specified as a hash.
# # <INPUT TYPE="hidden" NAME="name">
# hidden("name", "value")
# # <INPUT TYPE="hidden" NAME="name" VALUE="value">
# hidden("NAME" => "name", "VALUE" => "reset", "ID" => "foo")
# # <INPUT TYPE="hidden" NAME="name" VALUE="value" ID="foo">
def hidden(name = "", value = nil)
attributes = if name.kind_of?(String)
{ "TYPE" => "hidden", "NAME" => name, "VALUE" => value }
# Generate a top-level HTML element as a string.
# The attributes of the element are specified as a hash. The
# pseudo-attribute "PRETTY" can be used to specify that the generated
# HTML string should be indented. "PRETTY" can also be specified as
# a string as the sole argument to this method. The pseudo-attribute
# "DOCTYPE", if given, is used as the leading DOCTYPE SGML tag; it
# should include the entire text of this tag, including angle brackets.
# The body of the html element is supplied as a block.
# # <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><HTML>string</HTML>
# html("LANG" => "ja") { "string" }
# # <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><HTML LANG="ja">string</HTML>
# html("DOCTYPE" => false) { "string" }
# html("DOCTYPE" => '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">') { "string" }
# # <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"><HTML>string</HTML>
# html("PRETTY" => " ") { "<BODY></BODY>" }
# # <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
# html("PRETTY" => "\t") { "<BODY></BODY>" }
# # <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
# html("PRETTY") { "<BODY></BODY>" }
# # = html("PRETTY" => " ") { "<BODY></BODY>" }
# html(if $VERBOSE then "PRETTY" end) { "HTML string" }
def html(attributes = {}) # :yield:
elsif "PRETTY" == attributes
attributes = { "PRETTY" => true }
pretty = attributes.delete("PRETTY")
pretty = " " if true == pretty
if attributes.has_key?("DOCTYPE")
buf << attributes.delete("DOCTYPE")
attributes.delete("DOCTYPE")
# Generate an Image Button Input element as a string.
# +src+ is the URL of the image to use for the button. +name+
# is the input name. +alt+ is the alternative text for the image.
# Alternatively, the attributes can be specified as a hash.
# # <INPUT TYPE="image" SRC="url">
# image_button("url", "name", "string")
# # <INPUT TYPE="image" SRC="url" NAME="name" ALT="string">
# image_button("SRC" => "url", "ALT" => "string")
# # <INPUT TYPE="image" SRC="url" ALT="string">
def image_button(src = "", name = nil, alt = nil)
attributes = if src.kind_of?(String)
{ "TYPE" => "image", "SRC" => src, "NAME" => name,
# Generate an Image element as a string.
# +src+ is the URL of the image. +alt+ is the alternative text for
# the image. +width+ is the width of the image, and +height+ is
# Alternatively, the attributes can be specified as a hash.
# img("src", "alt", 100, 50)
# # <IMG SRC="src" ALT="alt" WIDTH="100" HEIGHT="50">
# img("SRC" => "src", "ALT" => "alt", "WIDTH" => 100, "HEIGHT" => 50)
# # <IMG SRC="src" ALT="alt" WIDTH="100" HEIGHT="50">
def img(src = "", alt = "", width = nil, height = nil)
attributes = if src.kind_of?(String)
{ "SRC" => src, "ALT" => alt }
attributes["WIDTH"] = width.to_s if width
attributes["HEIGHT"] = height.to_s if height
# Generate a Form element with multipart encoding as a String.
# Multipart encoding is used for forms that include file uploads.
# +action+ is the action to perform. +enctype+ is the encoding
# type, which defaults to "multipart/form-data".
# Alternatively, the attributes can be specified as a hash.
# multipart_form{ "string" }
# # <FORM METHOD="post" ENCTYPE="multipart/form-data">string</FORM>
# multipart_form("url") { "string" }
# # <FORM METHOD="post" ACTION="url" ENCTYPE="multipart/form-data">string</FORM>
def multipart_form(action = nil, enctype = "multipart/form-data")