func (w *ViewWriter) processNodes2(htmlWriter, srcWriter, patternWriter *formatting.IndentingWriter) { // initialise opening quote for htmlArray htmlWriter.Print("`") // initialise starting indent for src srcWriter.IncrIndent() // initialise starting indent and openning quote patternWriter.IncrIndent() htmlIndex := 0 patternIndex := 0 for n := w.rootNode.children.Front(); n != nil; n = n.Next() { htmlIndex, _ = w.writeNode(n, htmlWriter, srcWriter, patternWriter, htmlIndex, patternIndex) } // close quote for html Array htmlWriter.Print("`,") // Ensure final html is written srcWriter.Printf("fmt.Fprint(w, %sHtml[%d])\n", w.viewName, htmlIndex) // if our last op was writing code, we need to close pattern string if w.writingCodeOutput { patternWriter.Println("`,") } }
func (w *ViewWriter) writeCodeExecution(nd *Node, haml *formatting.IndentingWriter, src *formatting.IndentingWriter, currentHtmlIndex int) int { // end most recent haml output haml.Println("`,") // start next haml output (which will follow this code output haml.Println("`") // All call to write html from array src.Printf("fmt.Fprint(w, %sHtml[%d])\n", w.destinationName, currentHtmlIndex) currentHtmlIndex++ // attempt to keep formatting across user code. // Here we're checking to see if this is the end of a block statement // if so, we need to decrease indent first := getFirstChar(nd.text) if first == '}' { src.DecrIndent() } // add user's code src.Printf("%s\n", nd.text) // If user code ends in {, incr indent as they started a block statement last := getLastChar(nd.text) if last == '{' { src.IncrIndent() } for n := nd.children.Front(); n != nil; n = n.Next() { currentHtmlIndex = w.writeNode(n.Value.(*Node), haml, src, currentHtmlIndex) } return currentHtmlIndex }
// Recursive function to write parsed HAML Nodes func (w *ViewWriter) writeNode(nd *Node, haml *formatting.IndentingWriter, src *formatting.IndentingWriter, currentHtmlIndex int) int { if nd.name == "code_output" { return w.writeCodeOutput(nd, haml, src, currentHtmlIndex) } else if nd.name == "code_execution" { return w.writeCodeExecution(nd, haml, src, currentHtmlIndex) } haml.Printf("<%s", nd.name) if nd.id != nil { w.writeAttribute(nd.id, haml) } if nd.class != nil { w.writeAttribute(nd.class, haml) } for attrEl := nd.attributes.Front(); attrEl != nil; attrEl = attrEl.Next() { attr := attrEl.Value.(*nameValueStr) w.writeAttribute(attr, haml) } if nd.selfClosing { haml.Println(" />`)") return currentHtmlIndex } else { haml.Print(">") } // Outputting text. // If tag only contains short text, add it on same line if w.canChildContentFitOnOneLine(nd) { haml.Printf("%s</%s>\n", nd.text, nd.name) return currentHtmlIndex } // We either have long text, child tags or both // so we add it as indented child content haml.Println("") haml.IncrIndent() if len(nd.text) > 0 { w.writeLongText(nd.text, haml) } for n := nd.children.Front(); n != nil; n = n.Next() { currentHtmlIndex = w.writeNode(n.Value.(*Node), haml, src, currentHtmlIndex) } haml.DecrIndent() haml.Printf("</%s>\n", nd.name) return currentHtmlIndex }
func (vw *ViewWriter) writeLongText(text string, w *formatting.IndentingWriter) { // create index of spaces in string spaces := vw.getWhitespaceIndicies(text) // split string on space less than MAX_STRING_LENGTH start := 0 for _, idx := range spaces { distance := idx - start if distance > TEXT_BREAK_LENGTH { w.Println(text[start:idx]) start = idx + 1 } } w.Println(text[start:]) }
func (w *ViewWriter) writeCodeOutput(nd *Node, haml *formatting.IndentingWriter, src *formatting.IndentingWriter, currentHtmlIndex int) int { // end most recent haml output haml.Println("`,") // start next haml output (which will follow this code output haml.Println("`") // All call to write html from array src.Printf("fmt.Fprint(w, %sHtml[%d])\n", w.destinationName, currentHtmlIndex) currentHtmlIndex++ // add call to print output src.Printf("fmt.Fprint(w, %s)\n", nd.text) for n := nd.children.Front(); n != nil; n = n.Next() { currentHtmlIndex = w.writeNode(n.Value.(*Node), haml, src, currentHtmlIndex) } return currentHtmlIndex }
func (w *ViewWriter) writeCodeOutput(el *list.Element, haml, src, pattern *formatting.IndentingWriter, currentHtmlIndex, currentPatternIndex int, nodeType CodeOutputType) (htmlIndex, patternIndex int) { nd := el.Value.(*Node) htmlIndex = currentHtmlIndex patternIndex = currentPatternIndex if !w.writingCodeOutput { // First code output node - close off haml node output: // end most recent haml output haml.Println("`,") // start next haml output (which will follow this code output haml.Println("`") // Add call to write html from array src.Printf("fmt.Fprint(w, %sHtml[%d])\n", w.viewName, currentHtmlIndex) if nodeType == Literal || nodeType == EscapedValue { // Add calls to execute template. However, we need to know which // object to inject into template. Usually this will be 'data', but // can be something else inside a loop, for example, so we take the // object from the first dynamic element objectToInject := "data" if nodeType == EscapedValue { objectToInject = getObjectName(nd.text) } else { LookaheadLoop: for n := el; n != nil; n = n.Next() { node := n.Value.(*Node) switch node.name { case "code_output_dynamic": objectToInject = getObjectName(node.text) break LookaheadLoop } } } src.Printf("err = wr.templates[%d].Execute(w, %s)\n", currentPatternIndex, objectToInject) src.Printf("handle%sError(err)\n", w.properViewName) // start a new pattern string pattern.Print("`") w.writingCodeOutput = true patternIndex++ } htmlIndex++ } // These stop writing patterns, so we need to close off pattern strings if w.writingCodeOutput && (nodeType == RawValue || nodeType == Execution) { pattern.Println("`,") w.writingCodeOutput = false } // add call to print output switch nodeType { case Literal: pattern.Print(nd.text) case EscapedValue: // print the path to the desired object for the template pattern patternStr := "." index := strings.Index(nd.text, ".") if index > 0 { patternStr = nd.text[index:] } pattern.Printf("{{%s}}", patternStr) case RawValue: src.Printf("fmt.Fprint(w, %s)\n", nd.text) case Execution: // attempt to keep formatting across user code. // Here we're checking to see if this is the end of a block statement // if so, we need to decrease indent first := getFirstChar(nd.text) if first == '}' { src.DecrIndent() } // add user's code src.Printf("%s\n", nd.text) // If user code ends in {, incr indent as they started a block statement last := getLastChar(nd.text) if last == '{' { src.IncrIndent() } } for n := nd.children.Front(); n != nil; n = n.Next() { htmlIndex, patternIndex = w.writeNode(n, haml, src, pattern, htmlIndex, patternIndex) } return }
// Recursive function to write parsed HAML Nodes // We have to return a bool indicating if we have escaped any HTML (XSS protection) // so that we know if we need to include the templating library for that function func (w *ViewWriter) writeNode(el *list.Element, haml, src, pattern *formatting.IndentingWriter, currentHtmlIndex, currentPatternIndex int) (htmlIndex, patternIndex int) { nd := el.Value.(*Node) htmlIndex = currentHtmlIndex patternIndex = currentPatternIndex switch nd.name { case "code_output_literal": return w.writeCodeOutput(el, haml, src, pattern, htmlIndex, patternIndex, Literal) case "code_output_value": return w.writeCodeOutput(el, haml, src, pattern, htmlIndex, patternIndex, EscapedValue) case "code_output_raw": return w.writeCodeOutput(el, haml, src, pattern, htmlIndex, patternIndex, RawValue) case "code_execution": return w.writeCodeOutput(el, haml, src, pattern, htmlIndex, patternIndex, Execution) } if w.writingCodeOutput { // we've finished writing code output and we're back to haml // so close off our pattern string pattern.Println("`,") } w.writingCodeOutput = false if nd.name != "" { haml.Printf("<%s", nd.name) if nd.id != nil { w.writeAttribute(nd.id, haml) } if nd.class != nil { w.writeAttribute(nd.class, haml) } for attrEl := nd.attributes.Front(); attrEl != nil; attrEl = attrEl.Next() { attr := attrEl.Value.(*nameValueStr) w.writeAttribute(attr, haml) } if nd.selfClosing { haml.Print(" />") if nd.text != "" { haml.Printf(" %s", nd.text) } haml.Println("") return } else { haml.Print(">") } } // Outputting text. // If tag only contains short text, add it on same line if w.canChildContentFitOnOneLine(nd) { if nd.name == "" { haml.Printf("%s\n", nd.text) } else { haml.Printf("%s</%s>\n", nd.text, nd.name) } return } // We either have long text, child tags or both // so we add it as indented child content haml.Println("") haml.IncrIndent() if len(nd.text) > 0 { w.writeLongText(nd.text, haml) } for n := nd.children.Front(); n != nil; n = n.Next() { htmlIndex, patternIndex = w.writeNode(n, haml, src, pattern, htmlIndex, patternIndex) } haml.DecrIndent() if nd.name != "" { haml.Printf("</%s>\n", nd.name) } return }