// // 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 : ProxyOutStream { ////////////////////////////////////////////////////////////////////////// // 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("var 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) { w("window.addEventListener('load', function() { fan.std.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 XmlUtil.writeXml(this, obj.toStr, XmlUtil.xmlEscQuotes) } ////////////////////////////////////////////////////////////////////////// // Fields ////////////////////////////////////////////////////////////////////////// private Uri[]? cssUris // what CSS uris have been added private Uri[]? jsUris // what JavaScript uris have been added }