alanyeung 4 lat temu
rodzic
commit
58f2fecb21

+ 196 - 0
common.go

@@ -0,0 +1,196 @@
+package main
+
+import (
+	"bufio"
+	"encoding/base64"
+	"errors"
+	"io/ioutil"
+	"log"
+	"net/http"
+	"os"
+	"strconv"
+	"time"
+)
+
+/*
+	SYSTEM COMMON FUNCTIONS
+
+	This is a system function that put those we usually use function but not belongs to
+	any module / system.
+
+	Author: TOBY CHUI
+*/
+
+/*
+	Basic Response Functions
+
+	Send response with ease
+*/
+//Send text response with given w and message as string
+func sendTextResponse(w http.ResponseWriter, msg string) {
+	w.Write([]byte(msg))
+}
+
+//Send JSON response, with an extra json header
+func sendJSONResponse(w http.ResponseWriter, json string) {
+	w.Header().Set("Content-Type", "application/json")
+	w.Write([]byte(json))
+}
+
+func sendErrorResponse(w http.ResponseWriter, errMsg string) {
+	w.Header().Set("Content-Type", "application/json")
+	w.Write([]byte("{\"error\":\"" + errMsg + "\"}"))
+}
+
+func sendOK(w http.ResponseWriter) {
+	w.Header().Set("Content-Type", "application/json")
+	w.Write([]byte("\"OK\""))
+}
+
+/*
+	The paramter move function (mv)
+
+	You can find similar things in the PHP version of ArOZ Online Beta. You need to pass in
+	r (HTTP Request Object)
+	getParamter (string, aka $_GET['This string])
+
+	Will return
+	Paramter string (if any)
+	Error (if error)
+
+*/
+func mv(r *http.Request, getParamter string, postMode bool) (string, error) {
+	if postMode == false {
+		//Access the paramter via GET
+		keys, ok := r.URL.Query()[getParamter]
+
+		if !ok || len(keys[0]) < 1 {
+			//log.Println("Url Param " + getParamter +" is missing")
+			return "", errors.New("GET paramter " + getParamter + " not found or it is empty")
+		}
+
+		// Query()["key"] will return an array of items,
+		// we only want the single item.
+		key := keys[0]
+		return string(key), nil
+	} else {
+		//Access the parameter via POST
+		r.ParseForm()
+		x := r.Form.Get(getParamter)
+		if len(x) == 0 || x == "" {
+			return "", errors.New("POST paramter " + getParamter + " not found or it is empty")
+		}
+		return string(x), nil
+	}
+
+}
+
+func stringInSlice(a string, list []string) bool {
+	for _, b := range list {
+		if b == a {
+			return true
+		}
+	}
+	return false
+}
+
+func fileExists(filename string) bool {
+	_, err := os.Stat(filename)
+	if os.IsNotExist(err) {
+		return false
+	}
+	return true
+}
+
+func isDir(path string) bool {
+	if fileExists(path) == false {
+		return false
+	}
+	fi, err := os.Stat(path)
+	if err != nil {
+		log.Fatal(err)
+		return false
+	}
+	switch mode := fi.Mode(); {
+	case mode.IsDir():
+		return true
+	case mode.IsRegular():
+		return false
+	}
+	return false
+}
+
+func inArray(arr []string, str string) bool {
+	for _, a := range arr {
+		if a == str {
+			return true
+		}
+	}
+	return false
+}
+
+func timeToString(targetTime time.Time) string {
+	return targetTime.Format("2006-01-02 15:04:05")
+}
+
+func intToString(number int) string {
+	return strconv.Itoa(number)
+}
+
+func stringToInt(number string) (int, error) {
+	return strconv.Atoi(number)
+}
+
+func stringToInt64(number string) (int64, error) {
+	i, err := strconv.ParseInt(number, 10, 64)
+	if err != nil {
+		return -1, err
+	}
+	return i, nil
+}
+
+func int64ToString(number int64) string {
+	convedNumber := strconv.FormatInt(number, 10)
+	return convedNumber
+}
+
+func getUnixTime() int64 {
+	return time.Now().Unix()
+}
+
+func loadImageAsBase64(filepath string) (string, error) {
+	if !fileExists(filepath) {
+		return "", errors.New("File not exists")
+	}
+	f, _ := os.Open(filepath)
+	reader := bufio.NewReader(f)
+	content, _ := ioutil.ReadAll(reader)
+	encoded := base64.StdEncoding.EncodeToString(content)
+	return string(encoded), nil
+}
+
+func pushToSliceIfNotExist(slice []string, newItem string) []string {
+	itemExists := false
+	for _, item := range slice {
+		if item == newItem {
+			itemExists = true
+		}
+	}
+
+	if !itemExists {
+		slice = append(slice, newItem)
+	}
+
+	return slice
+}
+
+func removeFromSliceIfExists(slice []string, target string) []string {
+	newSlice := []string{}
+	for _, item := range slice {
+		if item != target {
+			newSlice = append(newSlice, item)
+		}
+	}
+
+	return newSlice
+}

+ 8 - 0
go.mod

@@ -0,0 +1,8 @@
+module aytechnology.us/gomatlab
+
+go 1.14
+
+require (
+	github.com/google/uuid v1.1.2
+	github.com/jmoiron/jsonq v0.0.0-20150511023944-e874b168d07e
+)

+ 4 - 0
go.sum

@@ -0,0 +1,4 @@
+github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
+github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/jmoiron/jsonq v0.0.0-20150511023944-e874b168d07e h1:ZZCvgaRDZg1gC9/1xrsgaJzQUCQgniKtw0xjWywWAOE=
+github.com/jmoiron/jsonq v0.0.0-20150511023944-e874b168d07e/go.mod h1:+rHyWac2R9oAZwFe1wGY2HBzFJJy++RHBg1cU23NkD8=

BIN
gomatlab


+ 69 - 0
main.go

@@ -0,0 +1,69 @@
+package main
+
+import (
+	"log"
+	"os"
+	"os/signal"
+	"syscall"
+
+	"aytechnology.us/gomatlab/mod/aroz"
+	"aytechnology.us/gomatlab/mod/server"
+)
+
+//MCServer should not be exported
+var MCServer *server.Handler
+
+var (
+	handler *aroz.ArozHandler
+)
+
+//Kill signal handler. Do something before the system the core terminate.
+func SetupCloseHandler() {
+	c := make(chan os.Signal, 2)
+	signal.Notify(c, os.Interrupt, syscall.SIGTERM)
+	go func() {
+		<-c
+		log.Println("\r- Shutting down demo module.")
+		//Do other things like close database or opened files
+
+		os.Exit(0)
+	}()
+}
+
+func main() {
+	//AROZOS
+	//If you have other flags, please add them here
+
+	//Start the aoModule pipeline (which will parse the flags as well). Pass in the module launch information
+	handler = aroz.HandleFlagParse(aroz.ServiceInfo{
+		Name:     "Octave",
+		Desc:     "GO Octave",
+		Group:    "Development",
+		IconPath: "gooctave/icon.png",
+		Version:  "0.0.1",
+		//You can define any path before the actualy html file. This directory (in this case demo/ ) will be the reverse proxy endpoint for this module
+		StartDir:     "gooctave/index.html",
+		SupportFW:    true,
+		LaunchFWDir:  "gooctave/index.html",
+		SupportEmb:   true,
+		LaunchEmb:    "gooctave/404.html",
+		InitFWSize:   []int{720, 480},
+		InitEmbSize:  []int{720, 480},
+		SupportedExt: []string{},
+	})
+
+	MCServer = server.NewHandler()
+	MCServer.StartService("./tmp/")
+
+	//init the image Handler
+	MCServer.SendCommand("figure(\"visible\", \"off\")")
+	MCServer.SendCommand("plot(0,0)")
+	//MCServer.SendCommand("figure(\"visible\", \"on\")")
+
+	SetupCloseHandler()
+	//Any log println will be shown in the core system via STDOUT redirection. But not STDIN.
+	log.Println("gooctave module started. Listening on " + handler.Port)
+	webServer("./webroot/", "./tmp/", handler.Port)
+	MCServer.Wait()
+
+}

+ 74 - 0
mc_misc.go

@@ -0,0 +1,74 @@
+package main
+
+import (
+	"crypto/md5"
+	"encoding/hex"
+	"fmt"
+	"io"
+	"log"
+	"net/http"
+	"os"
+	"time"
+
+	guuid "github.com/google/uuid"
+)
+
+var prevFileMD5 = ""
+
+func SendCommand(w http.ResponseWriter, r *http.Request) {
+	command, _ := mv(r, "command", false)
+	MCServer.SendCommand(command)
+
+	//Handle image output
+	//generate UUID if for image
+
+	id := guuid.New()
+	filename := id.String() + ".png"
+	MCServer.SendCommand("print -dpng " + filename)
+	for {
+		<-time.After(time.Second)
+		fmt.Println("Waiting files...")
+		if fileExists("./tmp/" + filename) {
+			break
+		}
+	}
+	if fileExists("./tmp/" + filename) {
+		newFileMD5, _ := hashFileMD5("./tmp/" + filename)
+		fmt.Println(newFileMD5)
+		if newFileMD5 == prevFileMD5 && prevFileMD5 != "" {
+			e := os.Remove("./tmp/" + filename)
+			if e != nil {
+				log.Fatal(e)
+			}
+		} else {
+			if prevFileMD5 != "" {
+				MCServer.AddLogEntry("<img style=\"width:400px;height:auto\" src=\"./output/" + filename + "\"></img>")
+			} else {
+				e := os.Remove("./tmp/" + filename)
+				if e != nil {
+					log.Fatal(e)
+				}
+			}
+		}
+		prevFileMD5 = newFileMD5
+	}
+
+	//end image output
+	sendJSONResponse(w, "OK")
+}
+
+func hashFileMD5(filePath string) (string, error) {
+	var returnMD5String string
+	file, err := os.Open(filePath)
+	if err != nil {
+		return returnMD5String, err
+	}
+	defer file.Close()
+	hash := md5.New()
+	if _, err := io.Copy(hash, file); err != nil {
+		return returnMD5String, err
+	}
+	hashInBytes := hash.Sum(nil)[:16]
+	returnMD5String = hex.EncodeToString(hashInBytes)
+	return returnMD5String, nil
+}

+ 32 - 0
mc_read.go

@@ -0,0 +1,32 @@
+package main
+
+import (
+	"encoding/json"
+	"net/http"
+	"strconv"
+)
+
+func ReadLog(w http.ResponseWriter, r *http.Request) {
+	log := MCServer.ReadAllLog()
+	jsonData, _ := json.Marshal(log)
+	sendJSONResponse(w, string(jsonData))
+}
+
+func ReadLogFrom(w http.ResponseWriter, r *http.Request) {
+	start, _ := mv(r, "start", false)
+	startInt, _ := strconv.Atoi(start)
+	end := MCServer.LenLog()
+
+	//show only first 3000 record if start ~ end is >= 3000
+	if end-startInt > 3000 {
+		startInt = end - 3000
+	}
+
+	if startInt > MCServer.LenLog()-1 {
+		sendJSONResponse(w, "[]")
+	} else {
+		log := MCServer.ReadRangeLog(startInt, end)
+		jsonData, _ := json.Marshal(log)
+		sendJSONResponse(w, string(jsonData))
+	}
+}

BIN
mod/._aroz


BIN
mod/._server


+ 70 - 0
mod/aroz/aroz.go

@@ -0,0 +1,70 @@
+package aroz
+
+import (
+	"flag"
+	"fmt"
+	"net/http"
+	"net/url"
+	"encoding/json"
+	"os"
+)
+
+type ArozHandler struct{
+	Port string
+	restfulEndpoint string
+}
+
+//Information required for registering this subservice to arozos
+type ServiceInfo struct{
+	Name string				//Name of this module. e.g. "Audio"
+	Desc string				//Description for this module
+	Group string			//Group of the module, e.g. "system" / "media" etc
+	IconPath string			//Module icon image path e.g. "Audio/img/function_icon.png"
+	Version string			//Version of the module. Format: [0-9]*.[0-9][0-9].[0-9]
+	StartDir string 		//Default starting dir, e.g. "Audio/index.html"
+	SupportFW bool 			//Support floatWindow. If yes, floatWindow dir will be loaded
+	LaunchFWDir string 		//This link will be launched instead of 'StartDir' if fw mode
+	SupportEmb bool			//Support embedded mode
+	LaunchEmb string 		//This link will be launched instead of StartDir / Fw if a file is opened with this module
+	InitFWSize []int 		//Floatwindow init size. [0] => Width, [1] => Height
+	InitEmbSize []int		//Embedded mode init size. [0] => Width, [1] => Height
+	SupportedExt []string 	//Supported File Extensions. e.g. ".mp3", ".flac", ".wav"
+}
+
+//This function will request the required flag from the startup paramters and parse it to the need of the arozos.
+func HandleFlagParse(info ServiceInfo) *ArozHandler{
+	var infoRequestMode = flag.Bool("info", false, "Show information about this subservice")
+	var port = flag.String("port", ":80", "The default listening endpoint for this subservice")
+	var restful = flag.String("rpt", "http://localhost:8080/api/ajgi/interface", "The RESTFUL Endpoint of the parent")
+	//Parse the flags
+	flag.Parse();
+	if (*infoRequestMode == true){
+		//Information request mode
+		jsonString, _ := json.Marshal(info);
+		fmt.Println(string(jsonString))
+		os.Exit(0);
+	}
+	return &ArozHandler{
+		Port: *port,
+		restfulEndpoint: *restful,
+	};
+}
+
+//Get the username and resources access token from the request, return username, token
+func (a *ArozHandler)GetUserInfoFromRequest(w http.ResponseWriter, r *http.Request)(string, string){
+	username := r.Header.Get("aouser")
+	token := r.Header.Get("aotoken")
+
+	return username, token
+}
+
+func (a *ArozHandler)RequestGatewayInterface(token string, script string)(*http.Response, error){
+	resp, err := http.PostForm(a.restfulEndpoint,
+		url.Values{"token":{token}, "script":{script}})
+    if err != nil {
+		// handle error
+		return nil, err
+	}
+	
+	return resp, nil
+}

+ 38 - 0
mod/aroz/doc.txt

@@ -0,0 +1,38 @@
+
+package aroz // import "imuslab.com/arozos/demo/aroz"
+
+
+TYPES
+
+type ArozHandler struct {
+	Port string
+	// Has unexported fields.
+}
+
+func HandleFlagParse(info ServiceInfo) *ArozHandler
+    This function will request the required flag from the startup paramters and
+    parse it to the need of the arozos.
+
+func (a *ArozHandler) GetUserInfoFromRequest(w http.ResponseWriter, r *http.Request) (string, string)
+    Get the username and resources access token from the request, return
+    username, token
+
+func (a *ArozHandler) RequestGatewayInterface(token string, script string) (*http.Response, error)
+
+type ServiceInfo struct {
+	Name         string   //Name of this module. e.g. "Audio"
+	Desc         string   //Description for this module
+	Group        string   //Group of the module, e.g. "system" / "media" etc
+	IconPath     string   //Module icon image path e.g. "Audio/img/function_icon.png"
+	Version      string   //Version of the module. Format: [0-9]*.[0-9][0-9].[0-9]
+	StartDir     string   //Default starting dir, e.g. "Audio/index.html"
+	SupportFW    bool     //Support floatWindow. If yes, floatWindow dir will be loaded
+	LaunchFWDir  string   //This link will be launched instead of 'StartDir' if fw mode
+	SupportEmb   bool     //Support embedded mode
+	LaunchEmb    string   //This link will be launched instead of StartDir / Fw if a file is opened with this module
+	InitFWSize   []int    //Floatwindow init size. [0] => Width, [1] => Height
+	InitEmbSize  []int    //Embedded mode init size. [0] => Width, [1] => Height
+	SupportedExt []string //Supported File Extensions. e.g. ".mp3", ".flac", ".wav"
+}
+    Information required for registering this subservice to arozos
+

+ 33 - 0
mod/server/autoclean.go

@@ -0,0 +1,33 @@
+package server
+
+import (
+	"time"
+)
+
+func (server *Handler) startLogAutoCleaning() {
+	//create the routine for auto clean trash
+	go func() {
+		for {
+			<-time.After(60 * time.Second) //no rush, clean every five minute
+			//if there is more than 1000 log, trim it.
+			if len(server.log) >= 1000 {
+				server.log = server.log[1000:]
+			}
+			//those are old old old logs, no longer need to store it.
+			for i, data := range server.log {
+				if time.Now().Sub(data.Timestamp).Minutes() >= 180 {
+					server.removeLog(i)
+				}
+			}
+		}
+	}()
+}
+
+func (server *Handler) removeLog(i int) {
+	if len(server.log)-1 != i {
+		server.log = append(server.log[:i], server.log[i+1:]...)
+	} else {
+		// if it is the last item, just remove it
+		server.log = server.log[:i]
+	}
+}

+ 25 - 0
mod/server/core.go

@@ -0,0 +1,25 @@
+package server
+
+import (
+	"io"
+	"os/exec"
+)
+
+//Handler is handler
+type Handler struct {
+	stdout    io.ReadCloser
+	stdin     io.WriteCloser
+	cmd       *exec.Cmd
+	isRunning bool
+	log       []Log
+}
+
+//NewHandler means everythign starts here :)
+func NewHandler() *Handler {
+	NewlyCreatedHandler := Handler{
+		isRunning: false,
+		log:       []Log{},
+	}
+
+	return &NewlyCreatedHandler
+}

+ 48 - 0
mod/server/io.go

@@ -0,0 +1,48 @@
+package server
+
+import (
+	"io"
+	"time"
+)
+
+//IsRunning is exported
+func (server *Handler) IsRunning() bool {
+	return server.isRunning
+}
+
+//ReadAllLog is exported
+func (server *Handler) ReadAllLog() []Log {
+	return server.log
+}
+
+//ReadRangeLog is exported
+func (server *Handler) ReadRangeLog(start int, end int) []Log {
+	return server.log[start:end]
+}
+
+//LenLog is exported
+func (server *Handler) LenLog() int {
+	return len(server.log)
+}
+
+//SendCommand is exported
+func (server *Handler) SendCommand(command string) bool {
+	if server.isRunning {
+		io.WriteString(server.stdin, command+"\n")
+		return true
+	} else {
+		return false
+	}
+}
+
+//AddLogEntry is exported function
+func (server *Handler) AddLogEntry(line string) {
+	if string(line) != "" {
+		newLog := Log{
+			ID:        len(server.log),
+			Timestamp: time.Now(),
+			Log:       string(line),
+		}
+		server.log = append(server.log, newLog)
+	}
+}

+ 16 - 0
mod/server/manage.go

@@ -0,0 +1,16 @@
+package server
+
+//KillServer is exported
+func (server *Handler) KillServer() bool {
+	if server.isRunning {
+		server.cmd.Process.Kill()
+		return true
+	} else {
+		return false
+	}
+}
+
+//Wait is exported function
+func (server *Handler) Wait() {
+	server.cmd.Wait()
+}

+ 63 - 0
mod/server/server.go

@@ -0,0 +1,63 @@
+package server
+
+import (
+	"bufio"
+	"fmt"
+	"log"
+	"os/exec"
+	"strings"
+	"time"
+)
+
+//StartService is exported
+func (server *Handler) StartService(dir string) {
+	if server.isRunning == false {
+		//server.log = []Log{} //Clean up the log
+		cmdName := "octave -i"
+		fmt.Println(cmdName)
+		cmdArgs := strings.Fields(cmdName)
+
+		server.cmd = exec.Command(cmdArgs[0], cmdArgs[1:len(cmdArgs)]...)
+		server.cmd.Dir = dir
+		server.stdout, _ = server.cmd.StdoutPipe()
+		server.stdin, _ = server.cmd.StdinPipe()
+		server.cmd.Start()
+		server.StartStdout()
+		server.isRunning = true
+		server.startCheckStatus()
+		server.startLogAutoCleaning()
+	} else {
+		log.Println("Server is already running.")
+	}
+	//cmd.Wait()
+	//init the server status checker
+
+}
+
+func (server *Handler) startCheckStatus() {
+	go func() {
+		server.cmd.Wait()
+		log.Println("Stopped.")
+		server.isRunning = false
+	}()
+}
+
+//StartStdout is exported
+func (server *Handler) StartStdout() {
+	i := len(server.log)
+	buf := bufio.NewReader(server.stdout)
+	go func() {
+		for {
+			line, _, _ := buf.ReadLine()
+			if string(line) != "" {
+				newLog := Log{
+					ID:        i,
+					Timestamp: time.Now(),
+					Log:       string(line),
+				}
+				server.log = append(server.log, newLog)
+				i++
+			}
+		}
+	}()
+}

+ 10 - 0
mod/server/struct.go

@@ -0,0 +1,10 @@
+package server
+
+import "time"
+
+//Log is exported
+type Log struct {
+	ID        int
+	Timestamp time.Time
+	Log       string
+}

BIN
tmp/49eb3d1d-0673-4ec0-9226-8b019b37cd5c.png


BIN
tmp/99470ca1-b525-4667-9f65-f4c75089ea6a.png


BIN
tmp/a6dd2a94-9eb9-4556-81cd-4a83c9495ca4.png


+ 23 - 0
web.go

@@ -0,0 +1,23 @@
+package main
+
+import (
+	"log"
+	"net/http"
+)
+
+func webServer(Dir string, tmpDir string, Port string) {
+	fs := http.FileServer(http.Dir(Dir))
+	http.Handle("/", fs)
+	http.Handle("/output/", http.StripPrefix("/output/", http.FileServer(http.Dir(tmpDir))))
+
+	//SYSTEM FUNCTION
+	http.HandleFunc("/sendcommand", SendCommand)
+
+	http.HandleFunc("/log", ReadLog)
+	http.HandleFunc("/log/from", ReadLogFrom)
+
+	err := http.ListenAndServe(Port, nil)
+	if err != nil {
+		log.Fatal(err)
+	}
+}

BIN
webroot/.DS_Store


BIN
webroot/._.DS_Store


BIN
webroot/._img


BIN
webroot/img/minecraft.png


+ 81 - 0
webroot/index.html

@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+    <!-- Standard Meta -->
+    <meta charset="utf-8" />
+    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
+
+    <!-- Site Properties -->
+    <title>Octave</title>
+
+    <script src="https://code.jquery.com/jquery-3.1.1.min.js" crossorigin="anonymous"></script>
+    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/semantic-ui@2.4.2/dist/semantic.min.css">
+    <script src="https://cdn.jsdelivr.net/npm/semantic-ui@2.4.2/dist/semantic.min.js"></script>
+</head>
+
+<body>
+    <div class="ui form">
+        <div class="field">
+            <div id="log" contenteditable="true" style="white-space: pre;overflow-x: scroll;font-family: monospace;"></div>
+        </div>
+    </div>
+    <br>
+    <div class="ui fluid action input">
+        <input type="text" id="command" placeholder="Command here (e.g. give @a minecraft:wooden_axe)">
+        <div class="ui button" onclick="sendCmd()">Send</div>
+    </div>
+</body>
+<script>
+    var currentLogID = 0;
+    var loading = false;
+
+    //to fix the fucking log textbox size.
+    $("#log").height(window.innerHeight - $("#command").height() - 50)
+    $(window).on('resize', function() {
+        $("#log").height(window.innerHeight - $("#command").height() - 50)
+    });
+
+    refreshLog();
+    setInterval(function() {
+        refreshLog()
+    }, 1000);
+
+    function refreshLog() {
+        if (!loading) {
+            loading = true;
+            $.get("log/from?start=" + currentLogID, function(data) {
+                previd = currentLogID;
+                $.each(data, function(index, value) {
+                    $("#log").append(value.Log + "<br>");
+                    currentLogID = value.ID + 1;
+                });
+                var data = $("#log").val().split("\n");
+                if (data.length > 3001) {
+                    $("#log").val("---Log timmeed---\n");
+                    data = data.splice(data.length - 2000, data.length - 1);
+                    $("#log").val(data.join("\n"));
+                }
+                loading = false;
+            });
+        }
+    }
+
+    function sendCmd() {
+        var cmdVal = $("#command").val();
+        $.get("sendcommand?command=" + cmdVal);
+        $("#log").append("Command: [" + cmdVal + "] sent\n");
+        $("#command").val("");
+    }
+
+    $('#command').on("keypress", function(e) {
+        if (e.keyCode == 13) {
+            sendCmd();
+            //$("#command").val("");
+            return false; // prevent the button click from happening
+        }
+    });
+</script>
+
+</html>