// 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 (
	
	
	
	
	
)

type writer interface {
	io.Writer
	io.ByteWriter
	WriteString(string) (int, error)
}

// Render renders the parse tree n to the given writer.
//
// Rendering is done on a 'best effort' basis: calling Parse on the output of
// Render will always result in something similar to the original tree, but it
// is not necessarily an exact clone unless the original tree was 'well-formed'.
// 'Well-formed' is not easily specified; the HTML5 specification is
// complicated.
//
// Calling Parse on arbitrary input typically results in a 'well-formed' parse
// tree. However, it is possible for Parse to yield a 'badly-formed' parse tree.
// For example, in a 'well-formed' parse tree, no <a> element is a child of
// another <a> element: parsing "<a><a>" results in two sibling elements.
// Similarly, in a 'well-formed' parse tree, no <a> element is a child of a
// <table> element: parsing "<p><table><a>" results in a <p> with two sibling
// children; the <a> is reparented to the <table>'s parent. However, calling
// Parse on "<a><table><a>" does not return an error, but the result has an <a>
// element with an <a> child, and is therefore not 'well-formed'.
//
// Programmatically constructed trees are typically also 'well-formed', but it
// is possible to construct a tree that looks innocuous but, 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>".
func ( io.Writer,  *Node) error {
	if ,  := .(writer);  {
		return render(, )
	}
	 := bufio.NewWriter()
	if  := render(, );  != nil {
		return 
	}
	return .Flush()
}

// plaintextAbort is returned from render1 when a <plaintext> element
// has been rendered. No more end tags should be rendered after that.
var plaintextAbort = errors.New("html: internal error (plaintext abort)")

func render( writer,  *Node) error {
	 := render1(, )
	if  == plaintextAbort {
		 = nil
	}
	return 
}

func render1( writer,  *Node) error {
	// Render non-element nodes; these are the easy cases.
	switch .Type {
	case ErrorNode:
		return errors.New("html: cannot render an ErrorNode node")
	case TextNode:
		return escape(, .Data)
	case DocumentNode:
		for  := .FirstChild;  != nil;  = .NextSibling {
			if  := (, );  != nil {
				return 
			}
		}
		return nil
	case ElementNode:
		// No-op.
	case CommentNode:
		if ,  := .WriteString("<!--");  != nil {
			return 
		}
		if  := escapeComment(, .Data);  != nil {
			return 
		}
		if ,  := .WriteString("-->");  != nil {
			return 
		}
		return nil
	case DoctypeNode:
		if ,  := .WriteString("<!DOCTYPE ");  != nil {
			return 
		}
		if  := escape(, .Data);  != nil {
			return 
		}
		if .Attr != nil {
			var ,  string
			for ,  := range .Attr {
				switch .Key {
				case "public":
					 = .Val
				case "system":
					 = .Val
				}
			}
			if  != "" {
				if ,  := .WriteString(" PUBLIC ");  != nil {
					return 
				}
				if  := writeQuoted(, );  != nil {
					return 
				}
				if  != "" {
					if  := .WriteByte(' ');  != nil {
						return 
					}
					if  := writeQuoted(, );  != nil {
						return 
					}
				}
			} else if  != "" {
				if ,  := .WriteString(" SYSTEM ");  != nil {
					return 
				}
				if  := writeQuoted(, );  != nil {
					return 
				}
			}
		}
		return .WriteByte('>')
	case RawNode:
		,  := .WriteString(.Data)
		return 
	default:
		return errors.New("html: unknown node type")
	}

	// Render the <xxx> opening tag.
	if  := .WriteByte('<');  != nil {
		return 
	}
	if ,  := .WriteString(.Data);  != nil {
		return 
	}
	for ,  := range .Attr {
		if  := .WriteByte(' ');  != nil {
			return 
		}
		if .Namespace != "" {
			if ,  := .WriteString(.Namespace);  != nil {
				return 
			}
			if  := .WriteByte(':');  != nil {
				return 
			}
		}
		if ,  := .WriteString(.Key);  != nil {
			return 
		}
		if ,  := .WriteString(`="`);  != nil {
			return 
		}
		if  := escape(, .Val);  != nil {
			return 
		}
		if  := .WriteByte('"');  != nil {
			return 
		}
	}
	if voidElements[.Data] {
		if .FirstChild != nil {
			return fmt.Errorf("html: void element <%s> has child nodes", .Data)
		}
		,  := .WriteString("/>")
		return 
	}
	if  := .WriteByte('>');  != nil {
		return 
	}

	// Add initial newline where there is danger of a newline beging ignored.
	if  := .FirstChild;  != nil && .Type == TextNode && strings.HasPrefix(.Data, "\n") {
		switch .Data {
		case "pre", "listing", "textarea":
			if  := .WriteByte('\n');  != nil {
				return 
			}
		}
	}

	// Render any child nodes
	if childTextNodesAreLiteral() {
		for  := .FirstChild;  != nil;  = .NextSibling {
			if .Type == TextNode {
				if ,  := .WriteString(.Data);  != nil {
					return 
				}
			} else {
				if  := (, );  != nil {
					return 
				}
			}
		}
		if .Data == "plaintext" {
			// Don't render anything else. <plaintext> must be the
			// last element in the file, with no closing tag.
			return plaintextAbort
		}
	} else {
		for  := .FirstChild;  != nil;  = .NextSibling {
			if  := (, );  != nil {
				return 
			}
		}
	}

	// Render the </xxx> closing tag.
	if ,  := .WriteString("</");  != nil {
		return 
	}
	if ,  := .WriteString(.Data);  != nil {
		return 
	}
	return .WriteByte('>')
}

func childTextNodesAreLiteral( *Node) bool {
	// Per WHATWG HTML 13.3, if the parent of the current node is a style,
	// script, xmp, iframe, noembed, noframes, or plaintext element, and the
	// current node is a text node, append the value of the node's data
	// literally. The specification is not explicit about it, but we only
	// enforce this if we are in the HTML namespace (i.e. when the namespace is
	// "").
	// NOTE: we also always include noscript elements, although the
	// specification states that they should only be rendered as such if
	// scripting is enabled for the node (which is not something we track).
	if .Namespace != "" {
		return false
	}
	switch .Data {
	case "iframe", "noembed", "noframes", "noscript", "plaintext", "script", "style", "xmp":
		return true
	default:
		return false
	}
}

// writeQuoted writes s to w surrounded by quotes. Normally it will use double
// quotes, but if s contains a double quote, it will use single quotes.
// It is used for writing the identifiers in a doctype declaration.
// In valid HTML, they can't contain both types of quotes.
func writeQuoted( writer,  string) error {
	var  byte = '"'
	if strings.Contains(, `"`) {
		 = '\''
	}
	if  := .WriteByte();  != nil {
		return 
	}
	if ,  := .WriteString();  != nil {
		return 
	}
	if  := .WriteByte();  != nil {
		return 
	}
	return nil
}

// Section 12.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,
	"embed":  true,
	"hr":     true,
	"img":    true,
	"input":  true,
	"keygen": true, // "keygen" has been removed from the spec, but are kept here for backwards compatibility.
	"link":   true,
	"meta":   true,
	"param":  true,
	"source": true,
	"track":  true,
	"wbr":    true,
}