| // 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") |
| } |
| `) |