[x/tour] initial commit
X-Tour-Commit: ab572597e342dc4e3a03dfd56449037d879e4e71
This commit is contained in:
Родитель
001739136e
Коммит
5d3839ca4d
|
@ -0,0 +1,206 @@
|
|||
// 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 main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"http"
|
||||
"template"
|
||||
)
|
||||
|
||||
func init() {
|
||||
http.HandleFunc("/compile", Compile)
|
||||
}
|
||||
|
||||
// Compile is an HTTP handler that reads Go source code from the request,
|
||||
// compiles and links the code (returning any errors), runs the program,
|
||||
// and sends the program's output as the HTTP response.
|
||||
func Compile(w http.ResponseWriter, req *http.Request) {
|
||||
out, err := compile(req)
|
||||
if err != nil {
|
||||
w.WriteHeader(404)
|
||||
var s string
|
||||
if out != nil {
|
||||
s = string(out)
|
||||
} else {
|
||||
s = err.String()
|
||||
}
|
||||
output.Execute(w, s)
|
||||
return
|
||||
}
|
||||
|
||||
// write the output of x as the http response
|
||||
if *htmlOutput {
|
||||
w.Write(out)
|
||||
} else if url, ok := isImage(out); ok {
|
||||
fmt.Fprintf(w, `<img src="%s">`, url)
|
||||
} else {
|
||||
output.Execute(w, out)
|
||||
}
|
||||
}
|
||||
|
||||
func isImage(out []byte) (string, bool) {
|
||||
out = bytes.TrimSpace(out)
|
||||
if !bytes.HasPrefix(out, []byte("IMAGE:")) {
|
||||
return "", false
|
||||
}
|
||||
out = out[6:]
|
||||
for _, c := range out {
|
||||
switch {
|
||||
case 'A' <= c && c <= 'Z', 'a' <= c && c <= 'z', '0' <= c && c <= '9',
|
||||
c == '+', c == '-', c == '/', c == '_', c == '=':
|
||||
default:
|
||||
println("bad", c)
|
||||
return "", false
|
||||
}
|
||||
}
|
||||
return "data:image/png;base64," + string(out), true
|
||||
}
|
||||
|
||||
var frontPage, output *template.Template // HTML templates
|
||||
|
||||
func init() {
|
||||
frontPage = template.New(nil)
|
||||
frontPage.SetDelims("«", "»")
|
||||
if err := frontPage.Parse(frontPageText); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
output = template.MustParse(outputText, nil)
|
||||
}
|
||||
|
||||
var outputText = `<pre>{@|html}</pre>`
|
||||
|
||||
var frontPageText = `<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
pre, textarea {
|
||||
font-family: monospace; /* use the user's browser setting */
|
||||
font-size: 100%;
|
||||
}
|
||||
.hints {
|
||||
font-size: 0.8em;
|
||||
text-align: right;
|
||||
}
|
||||
#edit, #output, #errors { width: 100%; text-align: left; }
|
||||
#edit { height: 500px; }
|
||||
#output { color: #00c; }
|
||||
#errors { color: #c00; }
|
||||
</style>
|
||||
<script>
|
||||
|
||||
function insertTabs(n) {
|
||||
// find the selection start and end
|
||||
var cont = document.getElementById("edit");
|
||||
var start = cont.selectionStart;
|
||||
var end = cont.selectionEnd;
|
||||
// split the textarea content into two, and insert n tabs
|
||||
var v = cont.value;
|
||||
var u = v.substr(0, start);
|
||||
for (var i=0; i<n; i++) {
|
||||
u += "\t";
|
||||
}
|
||||
u += v.substr(end);
|
||||
// set revised content
|
||||
cont.value = u;
|
||||
// reset caret position after inserted tabs
|
||||
cont.selectionStart = start+n;
|
||||
cont.selectionEnd = start+n;
|
||||
}
|
||||
|
||||
function autoindent(el) {
|
||||
var curpos = el.selectionStart;
|
||||
var tabs = 0;
|
||||
while (curpos > 0) {
|
||||
curpos--;
|
||||
if (el.value[curpos] == "\t") {
|
||||
tabs++;
|
||||
} else if (tabs > 0 || el.value[curpos] == "\n") {
|
||||
break;
|
||||
}
|
||||
}
|
||||
setTimeout(function() {
|
||||
insertTabs(tabs);
|
||||
}, 1);
|
||||
}
|
||||
|
||||
function keyHandler(event) {
|
||||
var e = window.event || event;
|
||||
if (e.keyCode == 9) { // tab
|
||||
insertTabs(1);
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
if (e.keyCode == 13) { // enter
|
||||
if (e.shiftKey) { // +shift
|
||||
compile(e.target);
|
||||
e.preventDefault();
|
||||
return false;
|
||||
} else {
|
||||
autoindent(e.target);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
var xmlreq;
|
||||
|
||||
function autocompile() {
|
||||
if(!document.getElementById("autocompile").checked) {
|
||||
return;
|
||||
}
|
||||
compile();
|
||||
}
|
||||
|
||||
function compile() {
|
||||
var prog = document.getElementById("edit").value;
|
||||
var req = new XMLHttpRequest();
|
||||
xmlreq = req;
|
||||
req.onreadystatechange = function() { compileUpdate(req); }
|
||||
req.open("POST", "/compile", true);
|
||||
req.setRequestHeader("Content-Type", "text/plain; charset=utf-8");
|
||||
req.send(prog);
|
||||
document.getElementById("output").innerHTML = "running...";
|
||||
}
|
||||
|
||||
function compileUpdate(req) {
|
||||
if(req != xmlreq || !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>
|
||||
<table width="100%"><tr><td width="60%" valign="top">
|
||||
<textarea autofocus="true" id="edit" spellcheck="false" onkeydown="keyHandler(event);" onkeyup="autocompile();">«@|html»</textarea>
|
||||
<div class="hints">
|
||||
(Shift-Enter to compile and run.)
|
||||
<input type="checkbox" id="autocompile" value="checked" /> Compile and run after each keystroke
|
||||
</div>
|
||||
<td width="3%">
|
||||
<td width="27%" align="right" valign="top">
|
||||
<div id="output"></div>
|
||||
</table>
|
||||
<div id="errors"></div>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
|
||||
var helloWorld = []byte(`package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
fmt.Println("hello, world")
|
||||
}
|
||||
`)
|
|
@ -0,0 +1,161 @@
|
|||
// 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 main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"exec"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"http"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
// Imports so that goinstall automatically installs them.
|
||||
_ "go-tour.googlecode.com/hg/pic"
|
||||
_ "go-tour.googlecode.com/hg/wc"
|
||||
)
|
||||
|
||||
const basePkg = "go-tour.googlecode.com/hg"
|
||||
|
||||
var (
|
||||
httpListen = flag.String("http", "127.0.0.1:3999", "host:port to listen on")
|
||||
htmlOutput = flag.Bool("html", false, "render program output as HTML")
|
||||
)
|
||||
|
||||
var (
|
||||
// a source of numbers, for naming temporary files
|
||||
uniq = make(chan int)
|
||||
// the architecture-identifying character of the tool chain, 5, 6, or 8
|
||||
archChar string
|
||||
// where gc and ld should find the go-tour packages
|
||||
pkgDir string
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
// source of unique numbers
|
||||
go func() {
|
||||
for i := 0; ; i++ {
|
||||
uniq <- i
|
||||
}
|
||||
}()
|
||||
|
||||
// set archChar
|
||||
var err os.Error
|
||||
archChar, err = build.ArchChar(runtime.GOARCH)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// find and serve the go tour files
|
||||
t, _, err := build.FindTree(basePkg)
|
||||
if err != nil {
|
||||
log.Fatalf("Couldn't find tour files: %v", err)
|
||||
}
|
||||
root := filepath.Join(t.SrcDir(), basePkg, "static")
|
||||
log.Println("Serving content from", root)
|
||||
http.Handle("/", http.FileServer(http.Dir(root)))
|
||||
|
||||
pkgDir = t.PkgDir()
|
||||
|
||||
log.Printf("Serving at http://%s/", *httpListen)
|
||||
log.Fatal(http.ListenAndServe(*httpListen, nil))
|
||||
}
|
||||
|
||||
var running struct {
|
||||
sync.Mutex
|
||||
cmd *exec.Cmd
|
||||
}
|
||||
|
||||
func stopRun() {
|
||||
running.Lock()
|
||||
if running.cmd != nil {
|
||||
running.cmd.Process.Kill()
|
||||
running.cmd = nil
|
||||
}
|
||||
running.Unlock()
|
||||
}
|
||||
|
||||
func compile(req *http.Request) (out []byte, err os.Error) {
|
||||
stopRun()
|
||||
|
||||
// x is the base name for .go, .6, executable files
|
||||
x := os.TempDir() + "/compile" + strconv.Itoa(<-uniq)
|
||||
src := x + ".go"
|
||||
obj := x + "." + archChar
|
||||
bin := x
|
||||
if runtime.GOOS == "windows" {
|
||||
bin += ".exe"
|
||||
}
|
||||
|
||||
// rewrite filename in error output
|
||||
defer func() {
|
||||
out = bytes.Replace(out, []byte(src+":"), []byte("main.go:"), -1)
|
||||
}()
|
||||
|
||||
// write body to x.go
|
||||
body := new(bytes.Buffer)
|
||||
if _, err = body.ReadFrom(req.Body); err != nil {
|
||||
return
|
||||
}
|
||||
if err = ioutil.WriteFile(src, body.Bytes(), 0666); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// build x.go, creating x.6
|
||||
out, err = run(archChar+"g", "-I", pkgDir, "-o", obj, src)
|
||||
defer os.Remove(obj)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// link x.6, creating x (the program binary)
|
||||
out, err = run(archChar+"l", "-L", pkgDir, "-o", bin, obj)
|
||||
defer os.Remove(bin)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// run x
|
||||
return run(bin)
|
||||
}
|
||||
|
||||
// run executes the specified command and returns its output and an error.
|
||||
func run(args ...string) ([]byte, os.Error) {
|
||||
var buf bytes.Buffer
|
||||
cmd := exec.Command(args[0], args[1:]...)
|
||||
cmd.Stdout = &buf
|
||||
cmd.Stderr = cmd.Stdout
|
||||
|
||||
// Start command and leave in 'running'.
|
||||
running.Lock()
|
||||
if running.cmd != nil {
|
||||
defer running.Unlock()
|
||||
return nil, fmt.Errorf("already running %s", running.cmd.Path)
|
||||
}
|
||||
if err := cmd.Start(); err != nil {
|
||||
running.Unlock()
|
||||
return nil, err
|
||||
}
|
||||
running.cmd = cmd
|
||||
running.Unlock()
|
||||
|
||||
// Wait for the command. Clean up,
|
||||
err := cmd.Wait()
|
||||
running.Lock()
|
||||
if running.cmd == cmd {
|
||||
running.cmd = nil
|
||||
}
|
||||
running.Unlock()
|
||||
return buf.Bytes(), err
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
// 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 pic
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"image"
|
||||
"image/png"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func Show(f func(int, int)[][]uint8) {
|
||||
const (
|
||||
dx = 256
|
||||
dy = 256
|
||||
)
|
||||
data := f(dx, dy)
|
||||
m := image.NewNRGBA(dx, dy)
|
||||
for y := 0; y < dy; y++ {
|
||||
for x := 0; x < dx; x++ {
|
||||
v := data[y][x]
|
||||
m.Pix[y*dy+x] = image.NRGBAColor{v, v, 255, 255}
|
||||
}
|
||||
}
|
||||
ShowImage(m)
|
||||
}
|
||||
|
||||
func ShowImage(m image.Image) {
|
||||
var buf bytes.Buffer
|
||||
png.Encode(&buf, m)
|
||||
enc := make([]byte, base64.StdEncoding.EncodedLen(buf.Len()))
|
||||
base64.StdEncoding.Encode(enc, buf.Bytes())
|
||||
fmt.Println("IMAGE:" + string(enc))
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,119 @@
|
|||
body {
|
||||
font-family: 'Droid Serif', serif;
|
||||
margin-left: 0px;
|
||||
margin-right: 0px;
|
||||
margin-top: 0px;
|
||||
font-size: 120%;
|
||||
}
|
||||
.slides {
|
||||
margin-left: 1em;
|
||||
margin-right: 1em;
|
||||
margin-top: 0px;
|
||||
}
|
||||
.slidenum {
|
||||
float: right;
|
||||
color: white;
|
||||
padding: 5px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
pre {
|
||||
margin-top: 0px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
pre span {
|
||||
font-family: 'Droid Serif', serif;
|
||||
}
|
||||
|
||||
h1, h2, h3 {
|
||||
font-family: 'Droid Sans', sans-serif;
|
||||
margin-top: 20px;
|
||||
}
|
||||
h1 {
|
||||
color: #fff;
|
||||
background-color: #444;
|
||||
margin: 0px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
code, pre, textarea {
|
||||
font-family: 'Droid Sans Mono', monospace;
|
||||
font-size: 100%;
|
||||
}
|
||||
|
||||
div.code {
|
||||
border: 1px solid black;
|
||||
float: left;
|
||||
width: 45%;
|
||||
height: 80%;
|
||||
margin: 0px 1em 0px 0px;
|
||||
}
|
||||
|
||||
textarea {
|
||||
border: 0px;
|
||||
display: block;
|
||||
padding: 5px;
|
||||
|
||||
width: 100%;
|
||||
height: 95%;
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
textarea:focus {
|
||||
border: 0px;
|
||||
outline: 0px;
|
||||
}
|
||||
|
||||
.programoutput {
|
||||
color: #0000cc;
|
||||
}
|
||||
|
||||
.compileerrors {
|
||||
color: #cc0000;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
button {
|
||||
display: inline;
|
||||
font-size: 60%;
|
||||
font-family: 'Droid Sans', sans-serif;
|
||||
color: #000;
|
||||
background-color: #eee;
|
||||
border-color: #000;
|
||||
border-width: 1px;
|
||||
padding: 3px;
|
||||
margin: 3px;
|
||||
}
|
||||
|
||||
button.next {
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
div.nav {
|
||||
display: block;
|
||||
text-align: right;
|
||||
float: right;
|
||||
}
|
||||
|
||||
div.clear {
|
||||
clear: right;
|
||||
}
|
||||
|
||||
h2.nocode {
|
||||
text-align: center;
|
||||
font-size: 300%;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
div.toc {
|
||||
display: none;
|
||||
visibility: hidden;
|
||||
}
|
|
@ -0,0 +1,283 @@
|
|||
window.onload = init;
|
||||
|
||||
var slides;
|
||||
var slide = null;
|
||||
var slidenum = 0;
|
||||
var codebox = null;
|
||||
var output = null;
|
||||
var errors = null;
|
||||
|
||||
function findclass(el, name) {
|
||||
var x = el.getElementsByClassName(name);
|
||||
if (x.length == 0)
|
||||
return null;
|
||||
return x[0];
|
||||
}
|
||||
|
||||
function initSlides() {
|
||||
var $slides = $("div.slide");
|
||||
$slides.each(function(i, slide) {
|
||||
var $s = $(slide).hide();
|
||||
|
||||
var $code = null;
|
||||
var $sdiv = $s.find("div");
|
||||
if (!$s.hasClass("nocode") && $sdiv.length > 0) {
|
||||
$code = $sdiv.last();
|
||||
$code.remove();
|
||||
}
|
||||
|
||||
var $h2 = $s.find("h2").first();
|
||||
if ($h2.length > 0) {
|
||||
$("<div/>").addClass("clear").insertAfter($h2);
|
||||
var $nav = $("<div/>").addClass("nav")
|
||||
if (i > 0) {
|
||||
$nav.append($("<button>").click(function() {
|
||||
show(i-1);
|
||||
}).text("PREV").addClass("prev"));
|
||||
}
|
||||
if (i+1 < $slides.length) {
|
||||
$nav.append($("<button>").click(function() {
|
||||
show(i+1);
|
||||
}).text("NEXT").addClass("next"));
|
||||
}
|
||||
$nav.insertBefore($h2);
|
||||
}
|
||||
if ($s.hasClass("nocode"))
|
||||
$h2.addClass("nocode");
|
||||
|
||||
if ($code == null)
|
||||
return;
|
||||
|
||||
var $codebox = $("<textarea/>").html($code.html().trim());
|
||||
var $codenav = $("<div/>").addClass("nav");
|
||||
$codenav.append($("<button>").click(function() {
|
||||
compile($codebox[0]);
|
||||
}).text("COMPILE").addClass("compile"));
|
||||
$code.empty().addClass("code");
|
||||
$code.append($codenav).append($codebox);
|
||||
$s.prepend($code);
|
||||
|
||||
$s.append("<hr/>");
|
||||
$s.append('<div class="compileerrors"/>')
|
||||
$s.append('<div class="programoutput"/>')
|
||||
});
|
||||
return $slides;
|
||||
}
|
||||
|
||||
function show(i) {
|
||||
console.log("show", i);
|
||||
if(i < 0 || i >= slides.length)
|
||||
return;
|
||||
if(slide != null) {
|
||||
$(slide).hide();
|
||||
}
|
||||
document.onkeydown = null;
|
||||
if(codebox != null) {
|
||||
codebox.onkeydown = null;
|
||||
codebox.onkeyup = null;
|
||||
}
|
||||
slidenum = i;
|
||||
|
||||
$("#num").text(i+1);
|
||||
|
||||
var url = location.href;
|
||||
var j = url.indexOf("#");
|
||||
if(j >= 0)
|
||||
url = url.substr(0, j);
|
||||
url += "#" + (slidenum+1).toString();
|
||||
location.href = url;
|
||||
|
||||
slide = slides[i];
|
||||
$(slide).show();
|
||||
if ($(slide).hasClass("nocode")) {
|
||||
setTimeout(function() {
|
||||
document.onkeydown = pageUpDown;
|
||||
}, 1);
|
||||
return;
|
||||
}
|
||||
var $code = $("div.code", slide);
|
||||
if ($code.length == 0)
|
||||
return;
|
||||
codebox = $code.find("textarea")[0];
|
||||
if (codebox != null) {
|
||||
codebox.spellcheck = false;
|
||||
codebox.onkeydown = keyDown;
|
||||
codebox.onkeyup = keyUp;
|
||||
codebox.focus();
|
||||
document.onclick = null;
|
||||
}
|
||||
output = $("div.programoutput", slide)[0];
|
||||
errors = $("div.compileerrors", slide)[0];
|
||||
document.onclick = function() { codebox.focus(); }
|
||||
}
|
||||
|
||||
function urlSlideNumber(url) {
|
||||
var i = url.indexOf("#");
|
||||
if(i < 0)
|
||||
return 0;
|
||||
var frag = unescape(url.substr(i+1));
|
||||
if(/^\d+$/.test(frag)) {
|
||||
i = parseInt(frag);
|
||||
if(i-1 < 0 || i-1 >= slides.length)
|
||||
return 0;
|
||||
return i-1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function insertTabs(cont, n) {
|
||||
// find the selection start and end
|
||||
var start = cont.selectionStart;
|
||||
var end = cont.selectionEnd;
|
||||
// split the textarea content into two, and insert n tabs
|
||||
var v = cont.value;
|
||||
var u = v.substr(0, start);
|
||||
for (var i=0; i<n; i++) {
|
||||
u += "\t";
|
||||
}
|
||||
u += v.substr(end);
|
||||
// set revised content
|
||||
cont.value = u;
|
||||
// reset caret position after inserted tabs
|
||||
cont.selectionStart = start+n;
|
||||
cont.selectionEnd = start+n;
|
||||
}
|
||||
|
||||
function autoindent(el) {
|
||||
var curpos = el.selectionStart;
|
||||
var tabs = 0;
|
||||
while (curpos > 0) {
|
||||
curpos--;
|
||||
if (el.value[curpos] == "\t") {
|
||||
tabs++;
|
||||
} else if (tabs > 0 || el.value[curpos] == "\n") {
|
||||
break;
|
||||
}
|
||||
}
|
||||
setTimeout(function() {
|
||||
insertTabs(el, tabs);
|
||||
}, 1);
|
||||
}
|
||||
|
||||
var keySeq = 0;
|
||||
var keyWaiting = false;
|
||||
|
||||
function keyDown(event) {
|
||||
var e = window.event || event;
|
||||
if (e.keyCode == 9) { // tab
|
||||
insertTabs(e.target, 1);
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
if (e.keyCode == 13) { // enter
|
||||
if (e.shiftKey) {
|
||||
compile(e.target);
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
autoindent(e.target);
|
||||
}
|
||||
if (e.keyCode == 33) { // page up
|
||||
e.preventDefault();
|
||||
show(slidenum-1);
|
||||
return false;
|
||||
}
|
||||
if (e.keyCode == 34) { // page down
|
||||
e.preventDefault();
|
||||
show(slidenum+1);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function pageUpDown(event) {
|
||||
var e = window.event || event;
|
||||
if (e.keyCode == 33) { // page up
|
||||
e.preventDefault();
|
||||
show(slidenum-1);
|
||||
return false;
|
||||
}
|
||||
if (e.keyCode == 34) { // page down
|
||||
e.preventDefault();
|
||||
show(slidenum+1);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
var autocompile = false;
|
||||
|
||||
function keyUp(event) {
|
||||
var e = window.event || event;
|
||||
keySeq++;
|
||||
if(!autocompile || codebox == null)
|
||||
return;
|
||||
if (!keyWaiting) {
|
||||
var seq = keySeq;
|
||||
keyWaiting = true;
|
||||
setTimeout(function() { keyTimeout(seq, 50); }, 50)
|
||||
}
|
||||
}
|
||||
|
||||
var waitTime = 200; // wait 200 ms before compiling
|
||||
|
||||
function keyTimeout(seq, n) {
|
||||
ks1 = seq;
|
||||
ks2 = n;
|
||||
if (keySeq != seq) {
|
||||
seq = keySeq;
|
||||
setTimeout(function() { keyTimeout(seq, 50); }, 50)
|
||||
return;
|
||||
}
|
||||
if (n < waitTime) {
|
||||
setTimeout(function() { keyTimeout(seq, n+50); }, 50)
|
||||
return;
|
||||
}
|
||||
keyWaiting = false;
|
||||
if (codebox != null)
|
||||
compile(codebox);
|
||||
}
|
||||
|
||||
var compileSeq = 0;
|
||||
|
||||
function compile(el) {
|
||||
var prog = $(el).val();
|
||||
var req = new XMLHttpRequest();
|
||||
var seq = compileSeq++;
|
||||
req.onreadystatechange = function() { compileUpdate(req, seq); }
|
||||
req.open("POST", "/compile", true);
|
||||
req.setRequestHeader("Content-Type", "text/plain; charset=utf-8");
|
||||
req.send(prog);
|
||||
if (output) {
|
||||
var seq = compileSeq;
|
||||
if (errors)
|
||||
errors.innerHTML = "";
|
||||
output.innerHTML = "";
|
||||
setTimeout(function() {
|
||||
if (seq == compileSeq) {
|
||||
output.innerHTML = "running...";
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
function compileUpdate(req, seq) {
|
||||
if(!req || req.readyState != 4 || compileSeq != seq)
|
||||
return;
|
||||
var out = req.responseText;
|
||||
var err = "";
|
||||
if(req.status != 200) {
|
||||
err = out;
|
||||
out = "";
|
||||
}
|
||||
if (output)
|
||||
output.innerHTML = out;
|
||||
if (errors)
|
||||
errors.innerHTML = err;
|
||||
compileSeq++;
|
||||
}
|
||||
|
||||
function init() {
|
||||
slides = initSlides();
|
||||
show(urlSlideNumber(location.href));
|
||||
}
|
|
@ -0,0 +1,161 @@
|
|||
// 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>
|
||||
]
|
||||
`
|
||||
|
Загрузка…
Ссылка в новой задаче