blob: e060e203c4a1443e90bca79ee85f3eb81b64969c [file] [log] [blame]
// Copyright 2012 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.
// opts is an object with these keys
// codeEl - code editor element
// outputEl - program output element
// runEl - run button element
// shareEl - share button element (optional)
// shareURLEl - share URL text input element (optional)
// shareRedirect - base URL to redirect to on share (optional)
// preCompile - callback to mutate request data before compiling
// postCompile - callback to read response data after compiling
// simple - use plain textarea instead of CodeMirror.
function playground(opts) {
var simple = opts['simple'];
var code = $(opts['codeEl']);
var editor;
// autoindent helpers for simple mode.
function insertTabs(n) {
// find the selection start and end
var start = code[0].selectionStart;
var end = code[0].selectionEnd;
// split the textarea content into two, and insert n tabs
var v = code[0].value;
var u = v.substr(0, start);
for (var i=0; i<n; i++) {
u += "\t";
}
u += v.substr(end);
// set revised content
code[0].value = u;
// reset caret position after inserted tabs
code[0].selectionStart = start+n;
code[0].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);
}, 1);
}
function keyHandler(e) {
if (simple && e.keyCode == 9) { // tab
insertTabs(1);
e.preventDefault();
return false;
}
if (e.keyCode == 13) { // enter
if (e.shiftKey) { // +shift
run();
e.preventDefault();
return false;
} else if (simple) {
autoindent(e.target);
}
}
return true;
}
if (simple) {
code.unbind('keydown').bind('keydown', keyHandler);
} else {
editor = CodeMirror.fromTextArea(
code[0],
{
lineNumbers: true,
indentUnit: 8,
indentWithTabs: true,
onKeyEvent: function(editor, e) { keyHandler(e); }
}
);
}
var output = $(opts['outputEl']);
function clearErrors() {
if (!editor) {
return;
}
var lines = editor.lineCount();
for (var i = 0; i < lines; i++) {
editor.setLineClass(i, null);
}
}
function highlightErrors(text) {
if (!editor) {
return;
}
var errorRe = /[a-z]+\.go:([0-9]+): /g;
var result;
while ((result = errorRe.exec(text)) != null) {
var line = result[1]*1-1;
editor.setLineClass(line, "errLine")
}
}
function body() {
if (editor) {
return editor.getValue();
}
return $(opts['codeEl']).val();
}
var seq = 0;
function run() {
clearErrors();
output.removeClass("error").html(
'<div class="loading">Waiting for remote server...</div>'
);
seq++;
var cur = seq;
var data = {"body": body()};
if (opts['preCompile']) {
opts['preCompile'](data);
}
$.ajax("/compile", {
data: data,
type: "POST",
dataType: "json",
success: function(data) {
if (seq != cur) {
return;
}
pre = $("<pre/>");
output.empty().append(pre);
if (opts['postCompile']) {
opts['postCompile'](data);
}
if (!data) {
return;
}
if (data.compile_errors != "") {
pre.text(data.compile_errors);
output.addClass("error");
highlightErrors(data.compile_errors);
return;
}
var out = ""+data.output;
if (out.indexOf("IMAGE:") == 0) {
var img = $("<img/>");
var url = "data:image/png;base64,";
url += out.substr(6)
img.attr("src", url);
output.empty().append(img);
return;
}
pre.text(out);
},
error: function() {
output.addClass("error").text(
"Error communicating with remote server."
);
}
});
}
$(opts['runEl']).click(run);
if (opts['shareEl'] == null || (opts['shareURLEl'] == null && opts['shareRedirect'] == null)) {
return editor;
}
function origin(href) {
return (""+href).split("/").slice(0, 3).join("/");
}
var shareURL;
if (opts['shareURLEl']) {
shareURL = $(opts['shareURLEl']).hide();
}
var sharing = false;
$(opts['shareEl']).click(function() {
if (sharing) return;
sharing = true;
$.ajax("/share", {
processData: false,
data: body(),
type: "POST",
complete: function(xhr) {
sharing = false;
if (xhr.status != 200) {
alert("Server error; try again.");
return;
}
if (opts['shareRedirect']) {
window.location = opts['shareRedirect'] + xhr.responseText;
}
if (shareURL) {
var url = origin(window.location) + "/p/" +
xhr.responseText;
shareURL.show().val(url).focus().select();
}
}
});
});
return editor;
}