diff options
| author | Nigel Tao <nigeltao@golang.org> | 2011-10-10 14:44:37 +1100 |
|---|---|---|
| committer | Nigel Tao <nigeltao@golang.org> | 2011-10-10 14:44:37 +1100 |
| commit | be8b4d943ff5fc9c169b91410ce11a377c8aa6b9 (patch) | |
| tree | 35b1849252e7468239a7a2398fb614e7e4807e99 /src/pkg/html/render.go | |
| parent | e63fcd613fd1770dc73fedb11dca616ce59e961c (diff) | |
| download | go-be8b4d943ff5fc9c169b91410ce11a377c8aa6b9.tar.xz | |
html: add a Render function.
R=mikesamuel, andybalholm
CC=golang-dev
https://golang.org/cl/5218041
Diffstat (limited to 'src/pkg/html/render.go')
| -rw-r--r-- | src/pkg/html/render.go | 159 |
1 files changed, 159 insertions, 0 deletions
diff --git a/src/pkg/html/render.go b/src/pkg/html/render.go new file mode 100644 index 0000000000..bf7b5995a1 --- /dev/null +++ b/src/pkg/html/render.go @@ -0,0 +1,159 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package html + +import ( + "bufio" + "fmt" + "io" + "os" +) + +type writer interface { + io.Writer + WriteByte(byte) os.Error + WriteString(string) (int, os.Error) +} + +// Render renders the parse tree n to the given writer. +// +// For 'well-formed' parse trees, calling Parse on the output of Render will +// result in a clone of the original tree. +// +// 'Well-formed' is not formally specified, but calling Parse on arbitrary +// input results in a 'well-formed' parse tree if Parse does not return an +// error. Programmatically constructed trees are typically also 'well-formed', +// but it is possible to construct a tree that, when rendered and re-parsed, +// results in a different tree. A simple example is that a solitary text node +// would become a tree containing <html>, <head> and <body> elements. Another +// example is that the programmatic equivalent of "a<head>b</head>c" becomes +// "<html><head><head/><body>abc</body></html>". +// +// Comment nodes are elided from the output, analogous to Parse skipping over +// any <!--comment--> input. +func Render(w io.Writer, n *Node) os.Error { + if x, ok := w.(writer); ok { + return render(x, n) + } + buf := bufio.NewWriter(w) + if err := render(buf, n); err != nil { + return err + } + return buf.Flush() +} + +func render(w writer, n *Node) os.Error { + // Render non-element nodes; these are the easy cases. + switch n.Type { + case ErrorNode: + return os.NewError("html: cannot render an ErrorNode node") + case TextNode: + return escape(w, n.Data) + case DocumentNode: + for _, c := range n.Child { + if err := render(w, c); err != nil { + return err + } + } + return nil + case ElementNode: + // No-op. + case CommentNode: + return nil + case DoctypeNode: + if _, err := w.WriteString("<!DOCTYPE "); err != nil { + return err + } + if _, err := w.WriteString(n.Data); err != nil { + return err + } + return w.WriteByte('>') + default: + return os.NewError("html: unknown node type") + } + + // TODO: figure out what to do with <script>, <style>, <noembed>, + // <noframes> and <noscript> elements. A tentative plan: + // 1. render the <xxx> opening tag as normal. + // 2. maybe error out if any child is not a text node. + // 3. render the text nodes (without escaping??). + // 4. maybe error out if `</xxx` is a case-insensitive substring of the + // concatenation of the children's data. + // 5. maybe error out if the concatenation of the children's data contains an + // unbalanced escaping text span start ("<!--") not followed by an end ("-->"). + // 6. render the closing tag as normal. + + // Render the <xxx> opening tag. + if err := w.WriteByte('<'); err != nil { + return err + } + if _, err := w.WriteString(n.Data); err != nil { + return err + } + for _, a := range n.Attr { + if err := w.WriteByte(' '); err != nil { + return err + } + if _, err := w.WriteString(a.Key); err != nil { + return err + } + if _, err := w.WriteString(`="`); err != nil { + return err + } + if err := escape(w, a.Val); err != nil { + return err + } + if err := w.WriteByte('"'); err != nil { + return err + } + } + if voidElements[n.Data] { + if len(n.Child) != 0 { + return fmt.Errorf("html: void element <%s> has child nodes", n.Data) + } + _, err := w.WriteString("/>") + return err + } + if err := w.WriteByte('>'); err != nil { + return err + } + + // Render any child nodes. + for _, c := range n.Child { + if err := render(w, c); err != nil { + return err + } + } + + // Render the </xxx> closing tag. + if _, err := w.WriteString("</"); err != nil { + return err + } + if _, err := w.WriteString(n.Data); err != nil { + return err + } + return w.WriteByte('>') +} + +// Section 13.1.2, "Elements", gives this list of void elements. Void elements +// are those that can't have any contents. +var voidElements = map[string]bool{ + "area": true, + "base": true, + "br": true, + "col": true, + "command": true, + "embed": true, + "hr": true, + "img": true, + "input": true, + "keygen": true, + "link": true, + "meta": true, + "param": true, + "source": true, + "track": true, + "wbr": true, +} |
