//
// Copyright (c) 2006, Brian Frank and Andy Frank
// Licensed under the Academic Free License version 3.0
//
// History:
// 28 May 06 Andy Frank Creation
//
**
** WebOutStream provides methods for generating XML and XHTML content.
**
@Js
class WebOutStream : OutStream
{
//////////////////////////////////////////////////////////////////////////
// Factory
//////////////////////////////////////////////////////////////////////////
**
** Construct a WebOutStream that wraps the given OutStream.
**
new make(OutStream out)
: super(out)
{
}
//////////////////////////////////////////////////////////////////////////
// General Methods
//////////////////////////////////////////////////////////////////////////
**
** Convenience for writeChars(obj.toStr).
**
This w(Obj? obj)
{
writeChars(obj == null ? "null" : obj.toStr)
return this
}
**
** Convenience for writeChars(Str.spaces(numSpaces)).
**
This tab(Int numSpaces := 2)
{
writeChars(Str.spaces(numSpaces))
return this
}
**
** Convenience for writeChar('\n').
**
This nl()
{
writeChar('\n')
return this
}
//////////////////////////////////////////////////////////////////////////
// Xml Methods
//////////////////////////////////////////////////////////////////////////
**
** Write out a prolog statement using the streams
** current charset encoding.
**
This prolog()
{
writeChars("<?xml version='1.0' encoding='$charset'?>\n")
return this
}
**
** Write a start tag. Use attrs to fully specify the attributes
** manually. Use empty to optionally close this element without
** using an end tag.
**
This tag(Str elemName, Str? attrs := null, Bool empty := false)
{
writeChar('<')
writeChars(elemName)
if (attrs != null) writeChar(' ').writeChars(attrs)
if (empty) writeChars(" /")
writeChar('>')
return this
}
**
** Write an end tag.
**
This tagEnd(Str elemName)
{
writeChars("</").writeChars(elemName).writeChar('>')
return this
}
//////////////////////////////////////////////////////////////////////////
// DOCTYPE
//////////////////////////////////////////////////////////////////////////
**
** Write the XHTML Strict DOCTYPE.
**
This docType()
{
writeChars("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n")
writeChars(" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n")
return this
}
**
** Write the HTML5 DOCTYPE.
**
This docType5()
{
writeChars("<!DOCTYPE html>\n")
return this
}
//////////////////////////////////////////////////////////////////////////
// html
//////////////////////////////////////////////////////////////////////////
**
** Start a <html> tag.
**
This html()
{
return tag("html", "xmlns='http://www.w3.org/1999/xhtml'").nl
}
**
** End a <html> tag.
**
This htmlEnd()
{
return tagEnd("html").nl
}
//////////////////////////////////////////////////////////////////////////
// head
//////////////////////////////////////////////////////////////////////////
**
** Start a <head> tag.
**
This head() { return tag("head").nl }
**
** End a <head> tag.
**
This headEnd() { return tagEnd("head").nl }
**
** Write a complete <title> tag.
**
This title(Str? attrs := null) { return tag("title", attrs) }
**
** End a <title> tag.
**
This titleEnd() { return tagEnd("title").nl }
**
** Write a complete <link> tag for an external CSS stylesheet.
** If this URI has already been included in this WebOutStream
** instance, then this method does nothing.
**
This includeCss(Uri href)
{
if (cssUris == null) cssUris = Uri[,]
if (!cssUris.contains(href))
{
attrs := "rel='stylesheet' type='text/css' href='$href.encode.toXml'"
tag("link", attrs, true).nl
cssUris.add(href)
}
return this
}
**
** Write a complete <script> tag for an external JavaScript file.
** If this URI has already been included in this WebOutStream
** instance, then this method does nothing.
**
This includeJs(Uri? href := null)
{
if (jsUris == null) jsUris = Uri[,]
if (!jsUris.contains(href))
{
tag("script", "type='text/javascript' src='$href.encode.toXml'")
tagEnd("script").nl
jsUris.add(href)
}
return this
}
**
** Customize how the JavaScript runtime environment is initialized.
** This method *must* be called inside the '<head>' tag, and also
** before 'sys.js' is loaded in order to take effect.
**
** Note this method is not necessary if no customization is needed.
** The JS runtime will automatically initialize using default values.
**
** The following variables are supported:
**
** - 'timezone': set the default TimeZone for JsVM
**
** - 'locale': set the default Locale for the JsVM. Note you
** must manually provide the locale config.props files. See
** `FilePack.toLocaleJsFile`.
**
** - 'main': an optional method to invoke after the page has
** been loaded. The 'main' argument can be either a type or
** method. If no method is specified, 'main' is used. If
** the method is not static, a new instance of type is
** created:
**
** "foo::Instance" => Instance().main()
** "foo::Instance.bar" => Instance().bar()
** "foo::Static" => Static.main()
** "foo::Static.bar" => Static.bar()
**
This initJs(Str:Str env)
{
w("<script type='text/javascript'>").nl
// init Env.vars to pickup in Env.$ctor
w("globalThis.fan\$env = {").nl
env.keys.each |n,i|
{
v := env[n]
w(" ${n.toCode}:${v.toCode}")
if (i < env.keys.size-1) w(",")
nl
}
w("}").nl
// if main method is specified add an onLoad callback to invoke
main := env["main"]
if (main != null)
{
if (WebJsMode.cur.isEs)
{
w("window.addEventListener('load', function() {
fan.sys.Env.__invokeMain('${main}');
}, false);").nl
}
else
{
w("window.addEventListener('load', function() {
fan.sys.Env.\$invokeMain('${main}');
}, false);").nl
}
}
w("</script>").nl
return this
}
**
** Write a complete <link> tag for an Atom feed resource.
**
This atom(Uri href, Str? attrs := null)
{
return tag("link rel='alternate' type='application/atom+xml' href='$href.encode.toXml'", attrs, true).nl
}
**
** Write a complete <link> tag for a RSS feed resource.
**
This rss(Uri href, Str? attrs := null)
{
return tag("link rel='alternate' type='application/rss+xml' href='$href.encode.toXml'", attrs, true).nl
}
**
** Write a complete <link> tag for a favicon. You must specifiy
** the MIME type for your icon in the 'attrs' argument:
**
** out.favIcon(`/fav.png`, "type='image/png'")
**
This favIcon(Uri href, Str? attrs := null)
{
return tag("link rel='icon' href='$href.encode.toXml'", attrs, true).nl
}
//////////////////////////////////////////////////////////////////////////
// style
//////////////////////////////////////////////////////////////////////////
**
** Start a <style> tag.
**
This style(Str? attrs := "type='text/css'") { return tag("style", attrs).nl }
**
** End a <style> tag.
**
This styleEnd() { return tagEnd("style").nl }
//////////////////////////////////////////////////////////////////////////
// script
//////////////////////////////////////////////////////////////////////////
**
** Start a <script> tag.
**
This script(Str? attrs := "type='text/javascript'") { return tag("script", attrs).nl }
**
** End a <script> tag.
**
This scriptEnd() { return tagEnd("script").nl }
//////////////////////////////////////////////////////////////////////////
// body
//////////////////////////////////////////////////////////////////////////
**
** Start a <body> tag.
**
This body(Str? attrs := null) { return tag("body", attrs).nl }
**
** End a <body> tag.
**
This bodyEnd() { return tagEnd("body").nl }
//////////////////////////////////////////////////////////////////////////
// h1, h2, h3, h4, h5, h6
//////////////////////////////////////////////////////////////////////////
**
** Start a <h1> tag.
**
This h1(Str? attrs := null) { return tag("h1", attrs) }
**
** End a <h1> tag.
**
This h1End() { return tagEnd("h1").nl }
**
** Start a <h2> tag.
**
This h2(Str? attrs := null) { return tag("h2", attrs) }
**
** End a <h2> tag.
**
This h2End() { return tagEnd("h2").nl }
**
** Start a <h3> tag.
**
This h3(Str? attrs := null) { return tag("h3", attrs) }
**
** End a <h3> tag.
**
This h3End() { return tagEnd("h3").nl }
**
** Start a <h4> tag.
**
This h4(Str? attrs := null) { return tag("h4", attrs) }
**
** End a <h4> tag.
**
This h4End() { return tagEnd("h4").nl }
**
** Start a <h5> tag.
**
This h5(Str? attrs := null) { return tag("h5", attrs) }
**
** End a <h5> tag.
**
This h5End() { return tagEnd("h5").nl }
**
** Start a <h6> tag.
**
This h6(Str? attrs := null) { return tag("h6", attrs) }
**
** End a <h6> tag.
**
This h6End() { return tagEnd("h6").nl }
//////////////////////////////////////////////////////////////////////////
// div
//////////////////////////////////////////////////////////////////////////
**
** Start a <div> tag.
**
This div(Str? attrs := null) { return tag("div", attrs).nl }
**
** End a <div> tag.
**
This divEnd() { return tagEnd("div").nl }
//////////////////////////////////////////////////////////////////////////
// span
//////////////////////////////////////////////////////////////////////////
**
** Start a <span> tag.
**
This span(Str? attrs := null) { return tag("span", attrs) }
**
** End a <span> tag.
**
This spanEnd() { return tagEnd("span") }
//////////////////////////////////////////////////////////////////////////
// p
//////////////////////////////////////////////////////////////////////////
**
** Start a <p> tag.
**
This p(Str? attrs := null) { return tag("p", attrs).nl }
**
** End a <p> tag.
**
This pEnd() { return tagEnd("p").nl }
//////////////////////////////////////////////////////////////////////////
// b
//////////////////////////////////////////////////////////////////////////
**
** Start a <b> tag.
**
This b(Str? attrs := null) { return tag("b", attrs) }
**
** End a <b> tag.
**
This bEnd() { return tagEnd("b") }
//////////////////////////////////////////////////////////////////////////
// i
//////////////////////////////////////////////////////////////////////////
**
** Start a <i> tag.
**
This i(Str? attrs := null) { return tag("i", attrs) }
**
** End a <i> tag.
**
This iEnd() { return tagEnd("i") }
//////////////////////////////////////////////////////////////////////////
// em
//////////////////////////////////////////////////////////////////////////
**
** Start a <em> tag.
**
This em(Str? attrs := null) { return tag("em", attrs) }
**
** End a <em> tag.
**
This emEnd() { return tagEnd("em") }
//////////////////////////////////////////////////////////////////////////
// pre
//////////////////////////////////////////////////////////////////////////
**
** Start a <pre> tag.
**
This pre(Str? attrs := null) { return tag("pre", attrs) }
**
** End a <pre> tag.
**
This preEnd() { return tagEnd("pre").nl }
//////////////////////////////////////////////////////////////////////////
// code
//////////////////////////////////////////////////////////////////////////
**
** Start a <code> tag.
**
This code(Str? attrs := null) { return tag("code", attrs) }
**
** End a <code> tag.
**
This codeEnd() { return tagEnd("code") }
//////////////////////////////////////////////////////////////////////////
// hr
//////////////////////////////////////////////////////////////////////////
**
** Write out a complete <hr/> tag.
**
This hr(Str? attrs := null) { return tag("hr", attrs, true).nl }
//////////////////////////////////////////////////////////////////////////
// br
//////////////////////////////////////////////////////////////////////////
**
** Write out a complete <br/> tag.
**
This br() { return tag("br", null, true) }
//////////////////////////////////////////////////////////////////////////
// a
//////////////////////////////////////////////////////////////////////////
**
** Start a <a> tag.
**
This a(Uri href, Str? attrs := null)
{
return tag("a href='$href.encode.toXml'", attrs)
}
**
** End a <a> tag.
**
This aEnd() { return tagEnd("a") }
//////////////////////////////////////////////////////////////////////////
// img
//////////////////////////////////////////////////////////////////////////
**
** Write a complete <img> tag.
**
This img(Uri src, Str? attrs := null)
{
return tag("img src='$src.encode.toXml'", attrs, true)
}
//////////////////////////////////////////////////////////////////////////
// table
//////////////////////////////////////////////////////////////////////////
** Start a <table> tag.
This table(Str? attrs := null) { return tag("table", attrs).nl }
** End a <table> tag.
This tableEnd() { return tagEnd("table").nl }
** Start a <thead> tag.
This thead(Str? attrs := null) { return tag("thead", attrs).nl }
** End a <thead> tag.
This theadEnd() { return tagEnd("thead").nl }
** Start a <tbody> tag.
This tbody(Str? attrs := null) { return tag("tbody", attrs).nl }
** End a <tbody> tag.
This tbodyEnd() { return tagEnd("tbody").nl }
** Start a <tfoot> tag.
This tfoot(Str? attrs := null) { return tag("tfoot", attrs).nl }
** End a <tfoot> tag.
This tfootEnd() { return tagEnd("tfoot").nl }
** Start a <tr> tag.
This tr(Str? attrs := null) { return tag("tr", attrs).nl }
** End a <tr> tag.
This trEnd() { return tagEnd("tr").nl }
** Start a <th> tag.
This th(Str? attrs := null) { return tag("th", attrs) }
** End a <th> tag.
This thEnd() { return tagEnd("th").nl }
** Start a <td> tag.
This td(Str? attrs := null) { return tag("td", attrs) }
** End a <td> tag.
This tdEnd() { return tagEnd("td").nl }
//////////////////////////////////////////////////////////////////////////
// ul/ol/li
//////////////////////////////////////////////////////////////////////////
**
** Start a <ul> tag.
**
This ul(Str? attrs := null) { return tag("ul", attrs).nl }
**
** End a <ul> tag.
**
This ulEnd() { return tagEnd("ul").nl }
**
** Start a <ol> tag.
**
This ol(Str? attrs := null) { return tag("ol", attrs).nl }
**
** End a <ol> tag.
**
This olEnd() { return tagEnd("ol").nl }
**
** Start a <li> tag.
**
This li(Str? attrs := null) { return tag("li", attrs) }
**
** End a <li> tag.
**
This liEnd() { return tagEnd("li") }
//////////////////////////////////////////////////////////////////////////
// dl/dd/dt
//////////////////////////////////////////////////////////////////////////
**
** Start a <dl> tag.
**
This dl(Str? attrs := null) { return tag("dl", attrs).nl }
**
** End a <dl> tag.
**
This dlEnd() { return tagEnd("dl").nl }
**
** Start a <dt> tag.
**
This dt(Str? attrs := null) { return tag("dt", attrs).nl }
**
** End a <dt> tag.
**
This dtEnd() { return tagEnd("dt").nl }
**
** Start a <dd> tag.
**
This dd(Str? attrs := null) { return tag("dd", attrs).nl }
**
** End a <dd> tag.
**
This ddEnd() { return tagEnd("dd").nl }
//////////////////////////////////////////////////////////////////////////
// form
//////////////////////////////////////////////////////////////////////////
**
** Start a <form> tag.
**
This form(Str? attrs := null) { return tag("form", attrs).nl }
**
** End a <form> tag.
**
This formEnd() { return tagEnd("form").nl }
//////////////////////////////////////////////////////////////////////////
// label
//////////////////////////////////////////////////////////////////////////
**
** Start a <label> tag.
**
This label(Str? attrs := null) { return tag("label", attrs).nl }
**
** End a <label> tag.
**
This labelEnd() { return tagEnd("label").nl }
//////////////////////////////////////////////////////////////////////////
// input
//////////////////////////////////////////////////////////////////////////
**
** Write a complete <input> tag.
**
This input(Str? attrs := null)
{
return tag("input", attrs, true)
}
**
** Convenience for input("type='text'" + attrs).
**
This textField(Str? attrs := null)
{
return tag("input type='text'", attrs, true)
}
**
** Convenience for input("type='password'" + attrs).
**
This password(Str? attrs := null)
{
return tag("input type='password'", attrs, true)
}
**
** Convenience for input("type='hidden'" + attrs).
**
This hidden(Str? attrs := null)
{
return tag("input type='hidden'", attrs, true)
}
**
** Convenience for input("type='button'" + attrs).
**
This button(Str? attrs := null)
{
return tag("input type='button'", attrs, true)
}
**
** Convenience for input("type='checkbox'" + attrs)
**
This checkbox(Str? attrs := null)
{
return tag("input type='checkbox'", attrs, true)
}
**
** Convenience for input("type='radio'" + attrs)
**
This radio(Str? attrs := null)
{
return tag("input type='radio'", attrs, true)
}
**
** Convenience for input("type='submit'" + attrs).
**
This submit(Str? attrs := null)
{
return tag("input type='submit'", attrs, true)
}
//////////////////////////////////////////////////////////////////////////
// select
//////////////////////////////////////////////////////////////////////////
**
** Start a <select> tag.
**
This select(Str? attrs := null) { return tag("select", attrs).nl }
**
** End a <select> tag.
**
This selectEnd() { return tagEnd("select").nl }
**
** Start a <option> tag.
**
This option(Str? attrs := null) { return tag("option", attrs) }
**
** End a <option> tag.
**
This optionEnd() { return tagEnd("option").nl }
//////////////////////////////////////////////////////////////////////////
// textarea
//////////////////////////////////////////////////////////////////////////
**
** Start a <textarea> tag.
**
This textArea(Str? attrs := null) { return tag("textarea", attrs).nl }
**
** End a <textarea> tag.
**
This textAreaEnd() { return tagEnd("textarea").nl }
//////////////////////////////////////////////////////////////////////////
// HTML5
//////////////////////////////////////////////////////////////////////////
** Start a <header> tag.
This header(Str? attrs := null) { return tag("header", attrs).nl }
** End a <header> tag.
This headerEnd() { return tagEnd("header").nl }
** Start a <footer> tag.
This footer(Str? attrs := null) { return tag("footer", attrs).nl }
** End a <footer> tag.
This footerEnd() { return tagEnd("footer").nl }
** Start a <main> tag.
This main(Str? attrs := null) { return tag("main", attrs).nl }
** End a <main> tag.
This mainEnd() { return tagEnd("main").nl }
** Start a <nav> tag.
This nav(Str? attrs := null) { return tag("nav", attrs).nl }
** End a <nav> tag.
This navEnd() { return tagEnd("nav").nl }
** Start a <section> tag.
This section(Str? attrs := null) { return tag("section", attrs).nl }
** End a <section> tag.
This sectionEnd() { return tagEnd("section").nl }
** Start a <article> tag.
This article(Str? attrs := null) { return tag("article", attrs).nl }
** End a <article> tag.
This articleEnd() { return tagEnd("article").nl }
** Start a <aside> tag.
This aside(Str? attrs := null) { return tag("aside", attrs).nl }
** End a <aside> tag.
This asideEnd() { return tagEnd("aside").nl }
//////////////////////////////////////////////////////////////////////////
// Utils
//////////////////////////////////////////////////////////////////////////
**
** Write 'obj.toStr' to the stream as valid XML text. The
** special control characters amp, lt, apos and quot are
** always escaped. The gt char is escaped only if it is
** the first char or if preceeded by the ']' char. Also
** see `sys::Str.toXml`. If obj is null, then "null" is
** written.
**
This esc(Obj? obj)
{
if (obj == null) return w("null")
return writeXml(obj.toStr, xmlEscQuotes)
}
//////////////////////////////////////////////////////////////////////////
// Fields
//////////////////////////////////////////////////////////////////////////
private Uri[]? cssUris // what CSS uris have been added
private Uri[]? jsUris // what JavaScript uris have been added
}