blob: c7755c10ff118c9c99d42996a2d84f11d86f5ad3 [file] [log] [blame]
// Copyright 2010 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 wc
import (
"bytes"
"fmt"
"http"
"io"
"log"
"sort"
"template"
"runtime/debug"
)
func frontPageHandler(c http.ResponseWriter, req *http.Request) {
frontPage.Execute(c, "a man a plan a canal panama")
}
var wcFunc func(string)map[string]int
func runwc(s string) (m map[string]int, err string) {
defer func() {
if v := recover(); v != nil {
err = fmt.Sprintln("panic: ", v)
err += "\nCall stack:\n" + string(debug.Stack())
}
}()
return wcFunc(s), ""
}
type wordCount struct {
Word string
Count int
}
type wordCounts []wordCount
func (wc wordCounts) Less(i, j int) bool {
return wc[i].Count > wc[j].Count ||
wc[i].Count == wc[j].Count && wc[i].Word < wc[j].Word
}
func (wc wordCounts) Swap(i, j int) {
wc[i], wc[j] = wc[j], wc[i]
}
func (wc wordCounts) Len() int {
return len(wc)
}
func wcHandler(c http.ResponseWriter, req *http.Request) {
var buf bytes.Buffer
io.Copy(&buf, req.Body)
m, err := runwc(buf.String())
if err != "" {
c.WriteHeader(404)
c.Write([]byte("<pre>"))
template.HTMLEscape(c, []byte(err))
c.Write([]byte("</pre>"))
return
}
w := make([]wordCount, len(m))
n := 0
for word, count := range m {
w[n] = wordCount{word, count}
n++
}
sort.Sort(wordCounts(w))
table.Execute(c, w)
}
// Serve runs a web server on port 4000 counting words using f.
func Serve(f func(string)map[string]int) {
wcFunc = f
http.HandleFunc("/", frontPageHandler)
http.HandleFunc("/wc", wcHandler)
err := http.ListenAndServe("127.0.0.1:4000", nil)
log.Fatal(err)
}
var frontPage, table *template.Template
func init() {
frontPage = template.New(nil)
frontPage.SetDelims("«", "»")
err := frontPage.Parse(frontPageText)
if err != nil {
panic(err)
}
table = template.New(nil)
table.SetDelims("«", "»")
err = table.Parse(tableText)
if err != nil {
panic(err)
}
}
var frontPageText = `<!doctype html>
<html>
<head>
<style>
h1 { font-family: monospace; }
</style>
<script>
var xmlreq;
function runwc() {
var prog = document.getElementById("edit").value;
var req = new XMLHttpRequest();
xmlreq = req;
req.onreadystatechange = wcUpdate;
req.open("POST", "/wc", true);
req.setRequestHeader("Content-Type", "text/plain; charset=utf-8");
req.send(prog);
}
function wcUpdate() {
var req = xmlreq;
if(!req || req.readyState != 4) {
return;
}
if(req.status == 200) {
document.getElementById("output").innerHTML = req.responseText;
document.getElementById("errors").innerHTML = "";
} else {
document.getElementById("errors").innerHTML = req.responseText;
document.getElementById("output").innerHTML = "";
}
}
</script>
</head>
<body>
<h1>Interactive Word Count</h1>
<table width="100%"><tr><td width="60%" valign="top">
<textarea autofocus="true" id="edit" style="width: 100%; height: 200px; font-size: 100%;" spellcheck="false" contenteditable="true" onkeyup="runwc();">«@|html»
</textarea>
<br/>
<td width="3%">
<td width="27%" align="right" valign="top">
<div id="output" align="left" style="width: 100%; font-size: 100%;">
</div>
</table>
<div id="errors" align="left" style="width: 100%; font-family: monaco; font-size: 100%; color: #800;">
</div>
</body>
</html>
`
var tableText = `map [
<table>
«.repeated section @»
<tr><td width=20><td>«Word»<td>«Count»</tr>
«.end»
</table>
]
`