mirror of
https://github.com/sorenisanerd/gotty.git
synced 2024-11-22 04:14:25 +00:00
Merge pull request #151 from yudai/xterm_integartion
Add xterm itegration
This commit is contained in:
commit
7aafac9448
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
|||||||
gotty
|
gotty
|
||||||
bindata
|
bindata
|
||||||
builds
|
builds
|
||||||
|
js/node_modules/*
|
||||||
|
2
.gitmodules
vendored
2
.gitmodules
vendored
@ -1,3 +1,3 @@
|
|||||||
[submodule "libapps"]
|
[submodule "libapps"]
|
||||||
path = libapps
|
path = js/libapps
|
||||||
url = https://chromium.googlesource.com/apps/libapps
|
url = https://chromium.googlesource.com/apps/libapps
|
||||||
|
30
Makefile
30
Makefile
@ -7,7 +7,7 @@ gotty: server/asset.go main.go server/*.go webtty/*.go backend/*.go Makefile
|
|||||||
|
|
||||||
asset: server/asset.go
|
asset: server/asset.go
|
||||||
|
|
||||||
server/asset.go: bindata/static/js/hterm.js bindata/static/js/gotty.js bindata/static/index.html bindata/static/favicon.png
|
server/asset.go: bindata/static/js/hterm.js bindata/static/js/bundle.js bindata/static/index.html bindata/static/favicon.png bindata/static/css/index.css bindata/static/css/xterm.css bindata/static/css/xterm_customize.css
|
||||||
go-bindata -prefix bindata -pkg server -ignore=\\.gitkeep -o server/asset.go bindata/...
|
go-bindata -prefix bindata -pkg server -ignore=\\.gitkeep -o server/asset.go bindata/...
|
||||||
gofmt -w server/asset.go
|
gofmt -w server/asset.go
|
||||||
|
|
||||||
@ -26,12 +26,30 @@ bindata/static/favicon.png: bindata/static resources/favicon.png
|
|||||||
bindata/static/js: bindata/static
|
bindata/static/js: bindata/static
|
||||||
mkdir -p bindata/static/js
|
mkdir -p bindata/static/js
|
||||||
|
|
||||||
bindata/static/js/hterm.js: bindata/static/js libapps/hterm/js/*.js
|
bindata/static/js/hterm.js: bindata/static/js js/libapps/hterm/js/*.js
|
||||||
cd libapps && \
|
cd js/libapps && \
|
||||||
LIBDOT_SEARCH_PATH=`pwd` ./libdot/bin/concat.sh -i ./hterm/concat/hterm_all.concat -o ../bindata/static/js/hterm.js
|
LIBDOT_SEARCH_PATH=`pwd` ./libdot/bin/concat.sh -i ./hterm/concat/hterm_all.concat -o ../../bindata/static/js/hterm.js
|
||||||
|
|
||||||
|
bindata/static/js/bundle.js: bindata/static/js js/dist/bundle.js
|
||||||
|
cp js/dist/bundle.js bindata/static/js/bundle.js
|
||||||
|
|
||||||
|
bindata/static/css: bindata/static
|
||||||
|
mkdir -p bindata/static/css
|
||||||
|
|
||||||
|
bindata/static/css/index.css: bindata/static/css resources/index.css
|
||||||
|
cp resources/index.css bindata/static/css/index.css
|
||||||
|
|
||||||
|
bindata/static/css/xterm_customize.css: bindata/static/css resources/xterm_customize.css
|
||||||
|
cp resources/xterm_customize.css bindata/static/css/xterm_customize.css
|
||||||
|
|
||||||
|
bindata/static/css/xterm.css: bindata/static/css js/node_modules/xterm/dist/xterm.css
|
||||||
|
cp js/node_modules/xterm/dist/xterm.css bindata/static/css/xterm.css
|
||||||
|
|
||||||
|
js/dist/bundle.js:
|
||||||
|
cd js && \
|
||||||
|
webpack
|
||||||
|
|
||||||
|
|
||||||
bindata/static/js/gotty.js: bindata/static/js resources/gotty.js
|
|
||||||
cp resources/gotty.js bindata/static/js/gotty.js
|
|
||||||
|
|
||||||
tools:
|
tools:
|
||||||
go get github.com/tools/godep
|
go get github.com/tools/godep
|
||||||
|
6008
js/dist/bundle.js
vendored
Normal file
6008
js/dist/bundle.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
js/dist/bundle.js.map
vendored
Normal file
1
js/dist/bundle.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
24
js/dist/hterm.d.ts
vendored
Normal file
24
js/dist/hterm.d.ts
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import * as bare from "hterm";
|
||||||
|
export declare class TermHterm {
|
||||||
|
elem: HTMLElement;
|
||||||
|
term: bare.Terminal;
|
||||||
|
io: bare.IO;
|
||||||
|
columns: number;
|
||||||
|
rows: number;
|
||||||
|
message: string;
|
||||||
|
constructor(elem: HTMLElement);
|
||||||
|
info(): {
|
||||||
|
columns: number;
|
||||||
|
rows: number;
|
||||||
|
};
|
||||||
|
output(data: string): void;
|
||||||
|
showMessage(message: string, timeout: number): void;
|
||||||
|
removeMessage(): void;
|
||||||
|
setWindowTitle(title: string): void;
|
||||||
|
setPreferences(value: object): void;
|
||||||
|
onInput(callback: (input: string) => void): void;
|
||||||
|
onResize(callback: (colmuns: number, rows: number) => void): void;
|
||||||
|
deactivate(): void;
|
||||||
|
reset(): void;
|
||||||
|
close(): void;
|
||||||
|
}
|
77
js/dist/hterm.js
vendored
Normal file
77
js/dist/hterm.js
vendored
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
var bare = require("hterm");
|
||||||
|
var TermHterm = (function () {
|
||||||
|
function TermHterm(elem) {
|
||||||
|
var _this = this;
|
||||||
|
this.elem = elem;
|
||||||
|
this.term = new bare.Terminal();
|
||||||
|
// this.term.defaultStorage = new lib.Storage.Memory();
|
||||||
|
this.term.getPrefs().set("send-encoding", "raw");
|
||||||
|
this.term.decorate(this.elem);
|
||||||
|
this.term.onTerminalReady = function () {
|
||||||
|
_this.io = _this.term.io.push();
|
||||||
|
_this.term.installKeyboard();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
;
|
||||||
|
TermHterm.prototype.info = function () {
|
||||||
|
return { columns: this.term.screen.getWidth(), rows: this.term.screen.getHeight() };
|
||||||
|
};
|
||||||
|
;
|
||||||
|
TermHterm.prototype.output = function (data) {
|
||||||
|
if (this.term.io.writeUTF8 != null) {
|
||||||
|
this.term.io.writeUTF8(data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
;
|
||||||
|
TermHterm.prototype.showMessage = function (message, timeout) {
|
||||||
|
this.term.io.showOverlay(message, timeout);
|
||||||
|
};
|
||||||
|
;
|
||||||
|
TermHterm.prototype.removeMessage = function () {
|
||||||
|
this.term.io.showOverlay("", 0);
|
||||||
|
};
|
||||||
|
TermHterm.prototype.setWindowTitle = function (title) {
|
||||||
|
this.term.setWindowTitle(title);
|
||||||
|
};
|
||||||
|
;
|
||||||
|
TermHterm.prototype.setPreferences = function (value) {
|
||||||
|
var _this = this;
|
||||||
|
Object.keys(value).forEach(function (key) {
|
||||||
|
_this.term.getPrefs().set(key, value[key]);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
;
|
||||||
|
TermHterm.prototype.onInput = function (callback) {
|
||||||
|
this.io.onVTKeystroke = function (data) {
|
||||||
|
callback(data);
|
||||||
|
};
|
||||||
|
this.io.sendString = function (data) {
|
||||||
|
callback(data);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
;
|
||||||
|
TermHterm.prototype.onResize = function (callback) {
|
||||||
|
this.io.onTerminalResize = function (columns, rows) {
|
||||||
|
callback(columns, rows);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
;
|
||||||
|
TermHterm.prototype.deactivate = function () {
|
||||||
|
this.io.onVTKeystroke = null;
|
||||||
|
this.io.sendString = null;
|
||||||
|
this.io.onTerminalResize = null;
|
||||||
|
this.term.uninstallKeyboard();
|
||||||
|
};
|
||||||
|
TermHterm.prototype.reset = function () {
|
||||||
|
this.removeMessage();
|
||||||
|
// this.term.reset();
|
||||||
|
};
|
||||||
|
TermHterm.prototype.close = function () {
|
||||||
|
this.term.uninstallKeyboard();
|
||||||
|
};
|
||||||
|
return TermHterm;
|
||||||
|
}());
|
||||||
|
exports.TermHterm = TermHterm;
|
||||||
|
//# sourceMappingURL=hterm.js.map
|
1
js/dist/hterm.js.map
vendored
Normal file
1
js/dist/hterm.js.map
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"hterm.js","sourceRoot":"","sources":["../src/hterm.ts"],"names":[],"mappings":"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"}
|
0
js/dist/main.d.ts
vendored
Normal file
0
js/dist/main.d.ts
vendored
Normal file
23
js/dist/main.js
vendored
Normal file
23
js/dist/main.js
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
var hterm_1 = require("./hterm");
|
||||||
|
var xterm_1 = require("./xterm");
|
||||||
|
var webtty_1 = require("./webtty");
|
||||||
|
var websocket_1 = require("./websocket");
|
||||||
|
var elem = document.getElementById("terminal");
|
||||||
|
if (elem !== null) {
|
||||||
|
var term_1 = new xterm_1.TermXterm(elem);
|
||||||
|
var httpsEnabled = window.location.protocol == "https:";
|
||||||
|
var url = (httpsEnabled ? 'wss://' : 'ws://') + window.location.hostname + ":8080/ws";
|
||||||
|
var args = window.location.search;
|
||||||
|
var factory = new websocket_1.ConnectionFactory(url, webtty_1.protocols);
|
||||||
|
var wt = new webtty_1.WebTTY(term_1, factory, args, "");
|
||||||
|
var closer_1 = wt.open();
|
||||||
|
new hterm_1.TermHterm(elem);
|
||||||
|
window.addEventListener("unload", function () {
|
||||||
|
closer_1();
|
||||||
|
term_1.close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
;
|
||||||
|
//# sourceMappingURL=main.js.map
|
1
js/dist/main.js.map
vendored
Normal file
1
js/dist/main.js.map
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";;AAAA,iCAAoC;AACpC,iCAAoC;AACpC,mCAA6C;AAC7C,yCAAgD;AAGhD,IAAM,IAAI,GAAG,QAAQ,CAAC,cAAc,CAAC,UAAU,CAAC,CAAA;AAEhD,EAAE,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC;IAChB,IAAM,MAAI,GAAG,IAAI,iBAAS,CAAC,IAAI,CAAC,CAAC;IACjC,IAAM,YAAY,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,IAAI,QAAQ,CAAC;IAC1D,IAAM,GAAG,GAAG,CAAC,YAAY,GAAG,QAAQ,GAAG,OAAO,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,GAAG,UAAU,CAAC;IACxF,IAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;IACpC,IAAM,OAAO,GAAG,IAAI,6BAAiB,CAAC,GAAG,EAAE,kBAAS,CAAC,CAAC;IACtD,IAAM,EAAE,GAAG,IAAI,eAAM,CAAC,MAAI,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;IAC/C,IAAM,QAAM,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC;IAEzB,IAAI,iBAAS,CAAC,IAAI,CAAC,CAAC;IAEpB,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE;QAC9B,QAAM,EAAE,CAAC;QACT,MAAI,CAAC,KAAK,EAAE,CAAC;IACjB,CAAC,CAAC,CAAC;AACP,CAAC;AAAA,CAAC"}
|
17
js/dist/websocket.d.ts
vendored
Normal file
17
js/dist/websocket.d.ts
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
export declare class ConnectionFactory {
|
||||||
|
url: string;
|
||||||
|
protocols: string[];
|
||||||
|
constructor(url: string, protocols: string[]);
|
||||||
|
create(): Connection;
|
||||||
|
}
|
||||||
|
export declare class Connection {
|
||||||
|
bare: WebSocket;
|
||||||
|
constructor(url: string, protocols: string[]);
|
||||||
|
open(): void;
|
||||||
|
close(): void;
|
||||||
|
send(data: string): void;
|
||||||
|
isOpen(): boolean;
|
||||||
|
onOpen(callback: () => void): void;
|
||||||
|
onReceive(callback: (data: string) => void): void;
|
||||||
|
onClose(callback: () => void): void;
|
||||||
|
}
|
60
js/dist/websocket.js
vendored
Normal file
60
js/dist/websocket.js
vendored
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
var ConnectionFactory = (function () {
|
||||||
|
function ConnectionFactory(url, protocols) {
|
||||||
|
this.url = url;
|
||||||
|
this.protocols = protocols;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
ConnectionFactory.prototype.create = function () {
|
||||||
|
return new Connection(this.url, this.protocols);
|
||||||
|
};
|
||||||
|
;
|
||||||
|
return ConnectionFactory;
|
||||||
|
}());
|
||||||
|
exports.ConnectionFactory = ConnectionFactory;
|
||||||
|
var Connection = (function () {
|
||||||
|
function Connection(url, protocols) {
|
||||||
|
this.bare = new WebSocket(url, protocols);
|
||||||
|
}
|
||||||
|
Connection.prototype.open = function () {
|
||||||
|
// nothing todo for websocket
|
||||||
|
};
|
||||||
|
;
|
||||||
|
Connection.prototype.close = function () {
|
||||||
|
this.bare.close();
|
||||||
|
};
|
||||||
|
;
|
||||||
|
Connection.prototype.send = function (data) {
|
||||||
|
this.bare.send(data);
|
||||||
|
};
|
||||||
|
;
|
||||||
|
Connection.prototype.isOpen = function () {
|
||||||
|
if (this.bare.readyState == WebSocket.CONNECTING ||
|
||||||
|
this.bare.readyState == WebSocket.OPEN) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
Connection.prototype.onOpen = function (callback) {
|
||||||
|
this.bare.onopen = function (event) {
|
||||||
|
callback();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
;
|
||||||
|
Connection.prototype.onReceive = function (callback) {
|
||||||
|
this.bare.onmessage = function (event) {
|
||||||
|
callback(event.data);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
;
|
||||||
|
Connection.prototype.onClose = function (callback) {
|
||||||
|
this.bare.onclose = function (event) {
|
||||||
|
callback();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
;
|
||||||
|
return Connection;
|
||||||
|
}());
|
||||||
|
exports.Connection = Connection;
|
||||||
|
//# sourceMappingURL=websocket.js.map
|
1
js/dist/websocket.js.map
vendored
Normal file
1
js/dist/websocket.js.map
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"websocket.js","sourceRoot":"","sources":["../src/websocket.ts"],"names":[],"mappings":";;AAAA;IAII,2BAAY,GAAW,EAAE,SAAmB;QACxC,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC/B,CAAC;IAAA,CAAC;IAEF,kCAAM,GAAN;QACI,MAAM,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;IACpD,CAAC;IAAA,CAAC;IACN,wBAAC;AAAD,CAAC,AAZD,IAYC;AAZY,8CAAiB;AAc9B;IAII,oBAAY,GAAW,EAAE,SAAmB;QACxC,IAAI,CAAC,IAAI,GAAG,IAAI,SAAS,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;IAC9C,CAAC;IAED,yBAAI,GAAJ;QACI,6BAA6B;IACjC,CAAC;IAAA,CAAC;IAEF,0BAAK,GAAL;QACI,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;IACtB,CAAC;IAAA,CAAC;IAEF,yBAAI,GAAJ,UAAK,IAAY;QACb,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACzB,CAAC;IAAA,CAAC;IAEF,2BAAM,GAAN;QACI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,IAAI,SAAS,CAAC,UAAU;YAC5C,IAAI,CAAC,IAAI,CAAC,UAAU,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;YACzC,MAAM,CAAC,IAAI,CAAA;QACf,CAAC;QACD,MAAM,CAAC,KAAK,CAAA;IAChB,CAAC;IAED,2BAAM,GAAN,UAAO,QAAoB;QACvB,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,UAAC,KAAK;YACrB,QAAQ,EAAE,CAAC;QACf,CAAC,CAAA;IACL,CAAC;IAAA,CAAC;IAEF,8BAAS,GAAT,UAAU,QAAgC;QACtC,IAAI,CAAC,IAAI,CAAC,SAAS,GAAG,UAAC,KAAK;YACxB,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACzB,CAAC,CAAA;IACL,CAAC;IAAA,CAAC;IAEF,4BAAO,GAAP,UAAQ,QAAoB;QACxB,IAAI,CAAC,IAAI,CAAC,OAAO,GAAG,UAAC,KAAK;YACtB,QAAQ,EAAE,CAAC;QACf,CAAC,CAAC;IACN,CAAC;IAAA,CAAC;IACN,iBAAC;AAAD,CAAC,AA7CD,IA6CC;AA7CY,gCAAU"}
|
48
js/dist/webtty.d.ts
vendored
Normal file
48
js/dist/webtty.d.ts
vendored
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
export declare const protocols: string[];
|
||||||
|
export declare const msgInputUnknown = "0";
|
||||||
|
export declare const msgInput = "1";
|
||||||
|
export declare const msgPing = "2";
|
||||||
|
export declare const msgResizeTerminal = "3";
|
||||||
|
export declare const msgUnknownOutput = "0";
|
||||||
|
export declare const msgOutput = "1";
|
||||||
|
export declare const msgPong = "2";
|
||||||
|
export declare const msgSetWindowTitle = "3";
|
||||||
|
export declare const msgSetPreferences = "4";
|
||||||
|
export declare const msgSetReconnect = "5";
|
||||||
|
export interface Terminal {
|
||||||
|
info(): {
|
||||||
|
columns: number;
|
||||||
|
rows: number;
|
||||||
|
};
|
||||||
|
output(data: string): void;
|
||||||
|
showMessage(message: string, timeout: number): void;
|
||||||
|
removeMessage(): void;
|
||||||
|
setWindowTitle(title: string): void;
|
||||||
|
setPreferences(value: object): void;
|
||||||
|
onInput(callback: (input: string) => void): void;
|
||||||
|
onResize(callback: (colmuns: number, rows: number) => void): void;
|
||||||
|
reset(): void;
|
||||||
|
deactivate(): void;
|
||||||
|
close(): void;
|
||||||
|
}
|
||||||
|
export interface Connection {
|
||||||
|
open(): void;
|
||||||
|
close(): void;
|
||||||
|
send(data: string): void;
|
||||||
|
isOpen(): boolean;
|
||||||
|
onOpen(callback: () => void): void;
|
||||||
|
onReceive(callback: (data: string) => void): void;
|
||||||
|
onClose(callback: () => void): void;
|
||||||
|
}
|
||||||
|
export interface ConnectionFactory {
|
||||||
|
create(): Connection;
|
||||||
|
}
|
||||||
|
export declare class WebTTY {
|
||||||
|
term: Terminal;
|
||||||
|
connectionFactory: ConnectionFactory;
|
||||||
|
args: string;
|
||||||
|
authToken: string;
|
||||||
|
reconnect: number;
|
||||||
|
constructor(term: Terminal, connectionFactory: ConnectionFactory, args: string, authToken: string);
|
||||||
|
open(): () => void;
|
||||||
|
}
|
99
js/dist/webtty.js
vendored
Normal file
99
js/dist/webtty.js
vendored
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.protocols = ["webtty"];
|
||||||
|
exports.msgInputUnknown = '0';
|
||||||
|
exports.msgInput = '1';
|
||||||
|
exports.msgPing = '2';
|
||||||
|
exports.msgResizeTerminal = '3';
|
||||||
|
exports.msgUnknownOutput = '0';
|
||||||
|
exports.msgOutput = '1';
|
||||||
|
exports.msgPong = '2';
|
||||||
|
exports.msgSetWindowTitle = '3';
|
||||||
|
exports.msgSetPreferences = '4';
|
||||||
|
exports.msgSetReconnect = '5';
|
||||||
|
var WebTTY = (function () {
|
||||||
|
function WebTTY(term, connectionFactory, args, authToken) {
|
||||||
|
this.term = term;
|
||||||
|
this.connectionFactory = connectionFactory;
|
||||||
|
this.args = args;
|
||||||
|
this.authToken = authToken;
|
||||||
|
this.reconnect = -1;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
WebTTY.prototype.open = function () {
|
||||||
|
var _this = this;
|
||||||
|
var connection = this.connectionFactory.create();
|
||||||
|
var pingTimer;
|
||||||
|
var reconnectTimeout;
|
||||||
|
var setup = function () {
|
||||||
|
connection.onOpen(function () {
|
||||||
|
var termInfo = _this.term.info();
|
||||||
|
connection.send(JSON.stringify({
|
||||||
|
Arguments: _this.args,
|
||||||
|
AuthToken: _this.authToken,
|
||||||
|
}));
|
||||||
|
var resizeHandler = function (colmuns, rows) {
|
||||||
|
connection.send(exports.msgResizeTerminal + JSON.stringify({
|
||||||
|
columns: colmuns,
|
||||||
|
rows: rows
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
_this.term.onResize(resizeHandler);
|
||||||
|
resizeHandler(termInfo.columns, termInfo.rows);
|
||||||
|
_this.term.onInput(function (input) {
|
||||||
|
connection.send(exports.msgInput + input);
|
||||||
|
});
|
||||||
|
pingTimer = setInterval(function () {
|
||||||
|
connection.send(exports.msgPing);
|
||||||
|
}, 30 * 1000);
|
||||||
|
});
|
||||||
|
connection.onReceive(function (data) {
|
||||||
|
var payload = data.slice(1);
|
||||||
|
switch (data[0]) {
|
||||||
|
case exports.msgOutput:
|
||||||
|
_this.term.output(decodeURIComponent(Array.prototype.map.call(atob(payload), function (c) {
|
||||||
|
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
|
||||||
|
}).join('')));
|
||||||
|
break;
|
||||||
|
case exports.msgPong:
|
||||||
|
break;
|
||||||
|
case exports.msgSetWindowTitle:
|
||||||
|
_this.term.setWindowTitle(payload);
|
||||||
|
break;
|
||||||
|
case exports.msgSetPreferences:
|
||||||
|
var preferences = JSON.parse(payload);
|
||||||
|
_this.term.setPreferences(preferences);
|
||||||
|
break;
|
||||||
|
case exports.msgSetReconnect:
|
||||||
|
var autoReconnect = JSON.parse(payload);
|
||||||
|
console.log("Enabling reconnect: " + autoReconnect + " seconds");
|
||||||
|
_this.reconnect = autoReconnect;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
connection.onClose(function () {
|
||||||
|
clearInterval(pingTimer);
|
||||||
|
_this.term.deactivate();
|
||||||
|
_this.term.showMessage("Connection Closed", 0);
|
||||||
|
if (_this.reconnect > 0) {
|
||||||
|
reconnectTimeout = setTimeout(function () {
|
||||||
|
connection = _this.connectionFactory.create();
|
||||||
|
_this.term.reset();
|
||||||
|
setup();
|
||||||
|
}, _this.reconnect * 1000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
connection.open();
|
||||||
|
};
|
||||||
|
setup();
|
||||||
|
return function () {
|
||||||
|
clearTimeout(reconnectTimeout);
|
||||||
|
connection.close();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
;
|
||||||
|
return WebTTY;
|
||||||
|
}());
|
||||||
|
exports.WebTTY = WebTTY;
|
||||||
|
;
|
||||||
|
//# sourceMappingURL=webtty.js.map
|
1
js/dist/webtty.js.map
vendored
Normal file
1
js/dist/webtty.js.map
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"webtty.js","sourceRoot":"","sources":["../src/webtty.ts"],"names":[],"mappings":";;AAAa,QAAA,SAAS,GAAG,CAAC,QAAQ,CAAC,CAAC;AAEvB,QAAA,eAAe,GAAG,GAAG,CAAC;AACtB,QAAA,QAAQ,GAAG,GAAG,CAAC;AACf,QAAA,OAAO,GAAG,GAAG,CAAC;AACd,QAAA,iBAAiB,GAAG,GAAG,CAAC;AAExB,QAAA,gBAAgB,GAAG,GAAG,CAAC;AACvB,QAAA,SAAS,GAAG,GAAG,CAAC;AAChB,QAAA,OAAO,GAAG,GAAG,CAAC;AACd,QAAA,iBAAiB,GAAG,GAAG,CAAC;AACxB,QAAA,iBAAiB,GAAG,GAAG,CAAC;AACxB,QAAA,eAAe,GAAG,GAAG,CAAC;AAgCnC;IAOI,gBAAY,IAAc,EAAE,iBAAoC,EAAE,IAAY,EAAE,SAAiB;QAC7F,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,iBAAiB,GAAG,iBAAiB,CAAC;QAC3C,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC;IACxB,CAAC;IAAA,CAAC;IAEF,qBAAI,GAAJ;QAAA,iBA2FC;QA1FG,IAAI,UAAU,GAAG,IAAI,CAAC,iBAAiB,CAAC,MAAM,EAAE,CAAC;QACjD,IAAI,SAAiB,CAAC;QACtB,IAAI,gBAAwB,CAAC;QAE7B,IAAM,KAAK,GAAG;YACV,UAAU,CAAC,MAAM,CAAC;gBACd,IAAM,QAAQ,GAAG,KAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;gBAElC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAC1B;oBACI,SAAS,EAAE,KAAI,CAAC,IAAI;oBACpB,SAAS,EAAE,KAAI,CAAC,SAAS;iBAC5B,CACJ,CAAC,CAAC;gBAGH,IAAM,aAAa,GAAG,UAAC,OAAe,EAAE,IAAY;oBAChD,UAAU,CAAC,IAAI,CACX,yBAAiB,GAAG,IAAI,CAAC,SAAS,CAC9B;wBACI,OAAO,EAAE,OAAO;wBAChB,IAAI,EAAE,IAAI;qBACb,CACJ,CACJ,CAAC;gBACN,CAAC,CAAC;gBAEF,KAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;gBAClC,aAAa,CAAC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;gBAE/C,KAAI,CAAC,IAAI,CAAC,OAAO,CACb,UAAC,KAAa;oBACV,UAAU,CAAC,IAAI,CAAC,gBAAQ,GAAG,KAAK,CAAC,CAAC;gBACtC,CAAC,CACJ,CAAC;gBAEF,SAAS,GAAG,WAAW,CAAC;oBACpB,UAAU,CAAC,IAAI,CAAC,eAAO,CAAC,CAAA;gBAC5B,CAAC,EAAE,EAAE,GAAG,IAAI,CAAC,CAAC;YAElB,CAAC,CAAC,CAAC;YAEH,UAAU,CAAC,SAAS,CAAC,UAAC,IAAI;gBACtB,IAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;gBAC9B,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;oBACd,KAAK,iBAAS;wBACV,KAAI,CAAC,IAAI,CAAC,MAAM,CACZ,kBAAkB,CAAC,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,UAAS,CAAC;4BACjE,MAAM,CAAC,GAAG,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;wBACjE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CACf,CAAC;wBACF,KAAK,CAAC;oBACV,KAAK,eAAO;wBACR,KAAK,CAAC;oBACV,KAAK,yBAAiB;wBAClB,KAAI,CAAC,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;wBAClC,KAAK,CAAC;oBACV,KAAK,yBAAiB;wBAClB,IAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;wBACxC,KAAI,CAAC,IAAI,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC;wBACtC,KAAK,CAAC;oBACV,KAAK,uBAAe;wBAChB,IAAM,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;wBAC1C,OAAO,CAAC,GAAG,CAAC,sBAAsB,GAAG,aAAa,GAAG,UAAU,CAAC,CAAA;wBAChE,KAAI,CAAC,SAAS,GAAG,aAAa,CAAC;wBAC/B,KAAK,CAAC;gBACd,CAAC;YACL,CAAC,CAAC,CAAC;YAEH,UAAU,CAAC,OAAO,CAAC;gBACf,aAAa,CAAC,SAAS,CAAC,CAAC;gBACzB,KAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;gBACvB,KAAI,CAAC,IAAI,CAAC,WAAW,CAAC,mBAAmB,EAAE,CAAC,CAAC,CAAC;gBAC9C,EAAE,CAAC,CAAC,KAAI,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC;oBACrB,gBAAgB,GAAG,UAAU,CAAC;wBAC1B,UAAU,GAAG,KAAI,CAAC,iBAAiB,CAAC,MAAM,EAAE,CAAC;wBAC7C,KAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;wBAClB,KAAK,EAAE,CAAC;oBACZ,CAAC,EAAE,KAAI,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC;gBAC9B,CAAC;YACL,CAAC,CAAC,CAAC;YAEH,UAAU,CAAC,IAAI,EAAE,CAAC;QACtB,CAAC,CAAA;QAED,KAAK,EAAE,CAAC;QACR,MAAM,CAAC;YACH,YAAY,CAAC,gBAAgB,CAAC,CAAC;YAC/B,UAAU,CAAC,KAAK,EAAE,CAAC;QACvB,CAAC,CAAA;IACL,CAAC;IAAA,CAAC;IACN,aAAC;AAAD,CAAC,AA3GD,IA2GC;AA3GY,wBAAM;AA2GlB,CAAC"}
|
24
js/dist/xterm.d.ts
vendored
Normal file
24
js/dist/xterm.d.ts
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import * as bare from "xterm";
|
||||||
|
export declare class TermXterm {
|
||||||
|
elem: HTMLElement;
|
||||||
|
message: HTMLElement;
|
||||||
|
messageTimeout: number;
|
||||||
|
messageTimer: number;
|
||||||
|
term: bare;
|
||||||
|
resizeListener: () => void;
|
||||||
|
constructor(elem: HTMLElement);
|
||||||
|
info(): {
|
||||||
|
columns: number;
|
||||||
|
rows: number;
|
||||||
|
};
|
||||||
|
output(data: string): void;
|
||||||
|
showMessage(message: string, timeout: number): void;
|
||||||
|
removeMessage(): void;
|
||||||
|
setWindowTitle(title: string): void;
|
||||||
|
setPreferences(value: object): void;
|
||||||
|
onInput(callback: (input: string) => void): void;
|
||||||
|
onResize(callback: (colmuns: number, rows: number) => void): void;
|
||||||
|
deactivate(): void;
|
||||||
|
reset(): void;
|
||||||
|
close(): void;
|
||||||
|
}
|
88
js/dist/xterm.js
vendored
Normal file
88
js/dist/xterm.js
vendored
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
var bare = require("xterm");
|
||||||
|
bare.loadAddon("fit");
|
||||||
|
var TermXterm = (function () {
|
||||||
|
function TermXterm(elem) {
|
||||||
|
var _this = this;
|
||||||
|
this.elem = elem;
|
||||||
|
this.term = new bare();
|
||||||
|
this.message = elem.ownerDocument.createElement("div");
|
||||||
|
this.message.className = "xterm-overlay";
|
||||||
|
this.messageTimeout = 2000;
|
||||||
|
this.resizeListener = function () {
|
||||||
|
_this.term.fit();
|
||||||
|
_this.term.scrollToBottom();
|
||||||
|
_this.showMessage(String(_this.term.cols) + "x" + String(_this.term.rows), _this.messageTimeout);
|
||||||
|
};
|
||||||
|
this.term.on("open", function () {
|
||||||
|
_this.term.fit();
|
||||||
|
_this.term.scrollToBottom();
|
||||||
|
window.addEventListener("resize", function () { _this.resizeListener(); });
|
||||||
|
});
|
||||||
|
this.term.open(elem, true);
|
||||||
|
}
|
||||||
|
;
|
||||||
|
TermXterm.prototype.info = function () {
|
||||||
|
return { columns: this.term.cols, rows: this.term.rows };
|
||||||
|
};
|
||||||
|
;
|
||||||
|
TermXterm.prototype.output = function (data) {
|
||||||
|
this.term.write(data);
|
||||||
|
};
|
||||||
|
;
|
||||||
|
TermXterm.prototype.showMessage = function (message, timeout) {
|
||||||
|
var _this = this;
|
||||||
|
this.message.textContent = message;
|
||||||
|
this.elem.appendChild(this.message);
|
||||||
|
if (this.messageTimer) {
|
||||||
|
clearTimeout(this.messageTimer);
|
||||||
|
}
|
||||||
|
if (timeout > 0) {
|
||||||
|
this.messageTimer = setTimeout(function () {
|
||||||
|
_this.elem.removeChild(_this.message);
|
||||||
|
}, timeout);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
;
|
||||||
|
TermXterm.prototype.removeMessage = function () {
|
||||||
|
if (this.message.parentNode == this.elem) {
|
||||||
|
this.elem.removeChild(this.message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
TermXterm.prototype.setWindowTitle = function (title) {
|
||||||
|
document.title = title;
|
||||||
|
};
|
||||||
|
;
|
||||||
|
TermXterm.prototype.setPreferences = function (value) {
|
||||||
|
};
|
||||||
|
;
|
||||||
|
TermXterm.prototype.onInput = function (callback) {
|
||||||
|
this.term.on("data", function (data) {
|
||||||
|
callback(data);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
;
|
||||||
|
TermXterm.prototype.onResize = function (callback) {
|
||||||
|
this.term.on("resize", function (data) {
|
||||||
|
callback(data.cols, data.rows);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
;
|
||||||
|
TermXterm.prototype.deactivate = function () {
|
||||||
|
this.term.off("data");
|
||||||
|
this.term.off("resize");
|
||||||
|
this.term.blur();
|
||||||
|
};
|
||||||
|
TermXterm.prototype.reset = function () {
|
||||||
|
this.removeMessage();
|
||||||
|
this.term.clear();
|
||||||
|
};
|
||||||
|
TermXterm.prototype.close = function () {
|
||||||
|
window.removeEventListener("resize", this.resizeListener);
|
||||||
|
this.term.destroy();
|
||||||
|
};
|
||||||
|
return TermXterm;
|
||||||
|
}());
|
||||||
|
exports.TermXterm = TermXterm;
|
||||||
|
//# sourceMappingURL=xterm.js.map
|
1
js/dist/xterm.js.map
vendored
Normal file
1
js/dist/xterm.js.map
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"xterm.js","sourceRoot":"","sources":["../src/xterm.ts"],"names":[],"mappings":"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"}
|
1
js/libapps
Submodule
1
js/libapps
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit f05b714d7ff1368b3669b041ae83fcaec1742a61
|
1873
js/package-lock.json
generated
Normal file
1873
js/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
10
js/package.json
Normal file
10
js/package.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"devDependencies": {
|
||||||
|
"ts-loader": "^2.0.3",
|
||||||
|
"typescript": "^2.3.2",
|
||||||
|
"webpack": "^2.5.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"xterm": "^2.7.0"
|
||||||
|
}
|
||||||
|
}
|
93
js/src/hterm.ts
Normal file
93
js/src/hterm.ts
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
import * as bare from "hterm";
|
||||||
|
import * as bareLib from "htermLib";
|
||||||
|
|
||||||
|
export class TermHterm {
|
||||||
|
elem: HTMLElement;
|
||||||
|
|
||||||
|
term: bare.Terminal;
|
||||||
|
io: bare.IO;
|
||||||
|
|
||||||
|
columns: number;
|
||||||
|
rows: number;
|
||||||
|
|
||||||
|
// to "show" the current message when removeMessage() is called
|
||||||
|
message: string;
|
||||||
|
|
||||||
|
constructor(elem: HTMLElement) {
|
||||||
|
this.elem = elem;
|
||||||
|
hterm.defaultStorage = new bareLib.Storage.Memory();
|
||||||
|
this.term = new bare.Terminal();
|
||||||
|
this.term.getPrefs().set("send-encoding", "raw");
|
||||||
|
this.term.decorate(this.elem);
|
||||||
|
|
||||||
|
this.io = this.term.io.push();
|
||||||
|
this.term.installKeyboard();
|
||||||
|
};
|
||||||
|
|
||||||
|
info(): { columns: number, rows: number } {
|
||||||
|
return { columns: this.columns, rows: this.rows };
|
||||||
|
};
|
||||||
|
|
||||||
|
output(data: string) {
|
||||||
|
if (this.term.io != null) {
|
||||||
|
this.term.io.writeUTF16(data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
showMessage(message: string, timeout: number) {
|
||||||
|
this.message = message;
|
||||||
|
if (timeout > 0) {
|
||||||
|
this.term.io.showOverlay(message, timeout);
|
||||||
|
} else {
|
||||||
|
this.term.io.showOverlay(message, null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
removeMessage(): void {
|
||||||
|
// there is no hideOverlay(), so show the same message with 0 sec
|
||||||
|
this.term.io.showOverlay(this.message, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
setWindowTitle(title: string) {
|
||||||
|
this.term.setWindowTitle(title);
|
||||||
|
};
|
||||||
|
|
||||||
|
setPreferences(value: object) {
|
||||||
|
Object.keys(value).forEach((key) => {
|
||||||
|
this.term.getPrefs().set(key, value[key]);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
onInput(callback: (input: string) => void) {
|
||||||
|
this.io.onVTKeystroke = (data) => {
|
||||||
|
callback(data);
|
||||||
|
};
|
||||||
|
this.io.sendString = (data) => {
|
||||||
|
callback(data);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
onResize(callback: (colmuns: number, rows: number) => void) {
|
||||||
|
this.io.onTerminalResize = (columns: number, rows: number) => {
|
||||||
|
this.columns = columns;
|
||||||
|
this.rows = rows;
|
||||||
|
callback(columns, rows);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
deactivate(): void {
|
||||||
|
this.io.onVTKeystroke = null;
|
||||||
|
this.io.sendString = null
|
||||||
|
this.io.onTerminalResize = null;
|
||||||
|
this.term.uninstallKeyboard();
|
||||||
|
}
|
||||||
|
|
||||||
|
reset(): void {
|
||||||
|
this.removeMessage();
|
||||||
|
this.term.installKeyboard();
|
||||||
|
}
|
||||||
|
|
||||||
|
close(): void {
|
||||||
|
this.term.uninstallKeyboard();
|
||||||
|
}
|
||||||
|
}
|
30
js/src/main.ts
Normal file
30
js/src/main.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { TermHterm } from "./hterm";
|
||||||
|
import { TermXterm } from "./xterm";
|
||||||
|
import { Terminal, WebTTY, protocols } from "./webtty";
|
||||||
|
import { ConnectionFactory } from "./websocket";
|
||||||
|
|
||||||
|
// @TODO remove these
|
||||||
|
declare var gotty_auth_token: string;
|
||||||
|
declare var gotty_term: string;
|
||||||
|
|
||||||
|
const elem = document.getElementById("terminal")
|
||||||
|
|
||||||
|
if (elem !== null) {
|
||||||
|
var term: Terminal;
|
||||||
|
if (gotty_term == "hterm") {
|
||||||
|
term = new TermHterm(elem);
|
||||||
|
} else {
|
||||||
|
term = new TermXterm(elem);
|
||||||
|
}
|
||||||
|
const httpsEnabled = window.location.protocol == "https:";
|
||||||
|
const url = (httpsEnabled ? 'wss://' : 'ws://') + window.location.host + window.location.pathname + 'ws';
|
||||||
|
const args = window.location.search;
|
||||||
|
const factory = new ConnectionFactory(url, protocols);
|
||||||
|
const wt = new WebTTY(term, factory, args, gotty_auth_token);
|
||||||
|
const closer = wt.open();
|
||||||
|
|
||||||
|
window.addEventListener("unload", () => {
|
||||||
|
closer();
|
||||||
|
term.close();
|
||||||
|
});
|
||||||
|
};
|
60
js/src/websocket.ts
Normal file
60
js/src/websocket.ts
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
export class ConnectionFactory {
|
||||||
|
url: string;
|
||||||
|
protocols: string[];
|
||||||
|
|
||||||
|
constructor(url: string, protocols: string[]) {
|
||||||
|
this.url = url;
|
||||||
|
this.protocols = protocols;
|
||||||
|
};
|
||||||
|
|
||||||
|
create(): Connection {
|
||||||
|
return new Connection(this.url, this.protocols);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Connection {
|
||||||
|
bare: WebSocket;
|
||||||
|
|
||||||
|
|
||||||
|
constructor(url: string, protocols: string[]) {
|
||||||
|
this.bare = new WebSocket(url, protocols);
|
||||||
|
}
|
||||||
|
|
||||||
|
open() {
|
||||||
|
// nothing todo for websocket
|
||||||
|
};
|
||||||
|
|
||||||
|
close() {
|
||||||
|
this.bare.close();
|
||||||
|
};
|
||||||
|
|
||||||
|
send(data: string) {
|
||||||
|
this.bare.send(data);
|
||||||
|
};
|
||||||
|
|
||||||
|
isOpen(): boolean {
|
||||||
|
if (this.bare.readyState == WebSocket.CONNECTING ||
|
||||||
|
this.bare.readyState == WebSocket.OPEN) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
onOpen(callback: () => void) {
|
||||||
|
this.bare.onopen = (event) => {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onReceive(callback: (data: string) => void) {
|
||||||
|
this.bare.onmessage = (event) => {
|
||||||
|
callback(event.data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onClose(callback: () => void) {
|
||||||
|
this.bare.onclose = (event) => {
|
||||||
|
callback();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
152
js/src/webtty.ts
Normal file
152
js/src/webtty.ts
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
export const protocols = ["webtty"];
|
||||||
|
|
||||||
|
export const msgInputUnknown = '0';
|
||||||
|
export const msgInput = '1';
|
||||||
|
export const msgPing = '2';
|
||||||
|
export const msgResizeTerminal = '3';
|
||||||
|
|
||||||
|
export const msgUnknownOutput = '0';
|
||||||
|
export const msgOutput = '1';
|
||||||
|
export const msgPong = '2';
|
||||||
|
export const msgSetWindowTitle = '3';
|
||||||
|
export const msgSetPreferences = '4';
|
||||||
|
export const msgSetReconnect = '5';
|
||||||
|
|
||||||
|
|
||||||
|
export interface Terminal {
|
||||||
|
info(): { columns: number, rows: number };
|
||||||
|
output(data: string): void;
|
||||||
|
showMessage(message: string, timeout: number): void;
|
||||||
|
removeMessage(): void;
|
||||||
|
setWindowTitle(title: string): void;
|
||||||
|
setPreferences(value: object): void;
|
||||||
|
onInput(callback: (input: string) => void): void;
|
||||||
|
onResize(callback: (colmuns: number, rows: number) => void): void;
|
||||||
|
reset(): void;
|
||||||
|
deactivate(): void;
|
||||||
|
close(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Connection {
|
||||||
|
open(): void;
|
||||||
|
close(): void;
|
||||||
|
send(data: string): void;
|
||||||
|
isOpen(): boolean;
|
||||||
|
onOpen(callback: () => void): void;
|
||||||
|
onReceive(callback: (data: string) => void): void;
|
||||||
|
onClose(callback: () => void): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ConnectionFactory {
|
||||||
|
create(): Connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class WebTTY {
|
||||||
|
term: Terminal;
|
||||||
|
connectionFactory: ConnectionFactory;
|
||||||
|
args: string;
|
||||||
|
authToken: string;
|
||||||
|
reconnect: number;
|
||||||
|
|
||||||
|
constructor(term: Terminal, connectionFactory: ConnectionFactory, args: string, authToken: string) {
|
||||||
|
this.term = term;
|
||||||
|
this.connectionFactory = connectionFactory;
|
||||||
|
this.args = args;
|
||||||
|
this.authToken = authToken;
|
||||||
|
this.reconnect = -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
open() {
|
||||||
|
let connection = this.connectionFactory.create();
|
||||||
|
let pingTimer: number;
|
||||||
|
let reconnectTimeout: number;
|
||||||
|
|
||||||
|
const setup = () => {
|
||||||
|
connection.onOpen(() => {
|
||||||
|
const termInfo = this.term.info();
|
||||||
|
|
||||||
|
connection.send(JSON.stringify(
|
||||||
|
{
|
||||||
|
Arguments: this.args,
|
||||||
|
AuthToken: this.authToken,
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
|
||||||
|
const resizeHandler = (colmuns: number, rows: number) => {
|
||||||
|
connection.send(
|
||||||
|
msgResizeTerminal + JSON.stringify(
|
||||||
|
{
|
||||||
|
columns: colmuns,
|
||||||
|
rows: rows
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.term.onResize(resizeHandler);
|
||||||
|
resizeHandler(termInfo.columns, termInfo.rows);
|
||||||
|
|
||||||
|
this.term.onInput(
|
||||||
|
(input: string) => {
|
||||||
|
connection.send(msgInput + input);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
pingTimer = setInterval(() => {
|
||||||
|
connection.send(msgPing)
|
||||||
|
}, 30 * 1000);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
connection.onReceive((data) => {
|
||||||
|
const payload = data.slice(1);
|
||||||
|
switch (data[0]) {
|
||||||
|
case msgOutput:
|
||||||
|
this.term.output(
|
||||||
|
decodeURIComponent(Array.prototype.map.call(atob(payload), function(c) {
|
||||||
|
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
|
||||||
|
}).join(''))
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case msgPong:
|
||||||
|
break;
|
||||||
|
case msgSetWindowTitle:
|
||||||
|
this.term.setWindowTitle(payload);
|
||||||
|
break;
|
||||||
|
case msgSetPreferences:
|
||||||
|
const preferences = JSON.parse(payload);
|
||||||
|
this.term.setPreferences(preferences);
|
||||||
|
break;
|
||||||
|
case msgSetReconnect:
|
||||||
|
const autoReconnect = JSON.parse(payload);
|
||||||
|
console.log("Enabling reconnect: " + autoReconnect + " seconds")
|
||||||
|
this.reconnect = autoReconnect;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
connection.onClose(() => {
|
||||||
|
clearInterval(pingTimer);
|
||||||
|
this.term.deactivate();
|
||||||
|
this.term.showMessage("Connection Closed", 0);
|
||||||
|
if (this.reconnect > 0) {
|
||||||
|
reconnectTimeout = setTimeout(() => {
|
||||||
|
connection = this.connectionFactory.create();
|
||||||
|
this.term.reset();
|
||||||
|
setup();
|
||||||
|
}, this.reconnect * 1000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
connection.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
setup();
|
||||||
|
return () => {
|
||||||
|
clearTimeout(reconnectTimeout);
|
||||||
|
connection.close();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
101
js/src/xterm.ts
Normal file
101
js/src/xterm.ts
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
import * as bare from "xterm";
|
||||||
|
|
||||||
|
bare.loadAddon("fit");
|
||||||
|
|
||||||
|
export class TermXterm {
|
||||||
|
elem: HTMLElement;
|
||||||
|
|
||||||
|
message: HTMLElement;
|
||||||
|
messageTimeout: number;
|
||||||
|
messageTimer: number;
|
||||||
|
|
||||||
|
term: bare;
|
||||||
|
resizeListener: () => void;
|
||||||
|
|
||||||
|
constructor(elem: HTMLElement) {
|
||||||
|
this.elem = elem;
|
||||||
|
this.term = new bare();
|
||||||
|
|
||||||
|
this.message = elem.ownerDocument.createElement("div");
|
||||||
|
this.message.className = "xterm-overlay";
|
||||||
|
this.messageTimeout = 2000;
|
||||||
|
|
||||||
|
|
||||||
|
this.resizeListener = () => {
|
||||||
|
this.term.fit();
|
||||||
|
this.term.scrollToBottom();
|
||||||
|
this.showMessage(String(this.term.cols) + "x" + String(this.term.rows), this.messageTimeout);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.term.on("open", () => {
|
||||||
|
this.resizeListener();
|
||||||
|
window.addEventListener("resize", () => { this.resizeListener(); });
|
||||||
|
});
|
||||||
|
|
||||||
|
this.term.open(elem, true);
|
||||||
|
};
|
||||||
|
|
||||||
|
info(): { columns: number, rows: number } {
|
||||||
|
return { columns: this.term.cols, rows: this.term.rows };
|
||||||
|
};
|
||||||
|
|
||||||
|
output(data: string) {
|
||||||
|
this.term.write(data);
|
||||||
|
};
|
||||||
|
|
||||||
|
showMessage(message: string, timeout: number) {
|
||||||
|
this.message.textContent = message;
|
||||||
|
this.elem.appendChild(this.message);
|
||||||
|
|
||||||
|
if (this.messageTimer) {
|
||||||
|
clearTimeout(this.messageTimer);
|
||||||
|
}
|
||||||
|
if (timeout > 0) {
|
||||||
|
this.messageTimer = setTimeout(() => {
|
||||||
|
this.elem.removeChild(this.message);
|
||||||
|
}, timeout);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
removeMessage(): void {
|
||||||
|
if (this.message.parentNode == this.elem) {
|
||||||
|
this.elem.removeChild(this.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setWindowTitle(title: string) {
|
||||||
|
document.title = title;
|
||||||
|
};
|
||||||
|
|
||||||
|
setPreferences(value: object) {
|
||||||
|
};
|
||||||
|
|
||||||
|
onInput(callback: (input: string) => void) {
|
||||||
|
this.term.on("data", (data) => {
|
||||||
|
callback(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
onResize(callback: (colmuns: number, rows: number) => void) {
|
||||||
|
this.term.on("resize", (data) => {
|
||||||
|
callback(data.cols, data.rows);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
deactivate(): void {
|
||||||
|
this.term.off("data");
|
||||||
|
this.term.off("resize");
|
||||||
|
this.term.blur();
|
||||||
|
}
|
||||||
|
|
||||||
|
reset(): void {
|
||||||
|
this.removeMessage();
|
||||||
|
this.term.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
close(): void {
|
||||||
|
window.removeEventListener("resize", this.resizeListener);
|
||||||
|
this.term.destroy();
|
||||||
|
}
|
||||||
|
}
|
20
js/tsconfig.json
Normal file
20
js/tsconfig.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"strictNullChecks": true,
|
||||||
|
"noUnusedLocals" : true,
|
||||||
|
"noImplicitThis": true,
|
||||||
|
"alwaysStrict": true,
|
||||||
|
"outDir": "./dist/",
|
||||||
|
"declaration": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"target": "es5",
|
||||||
|
"module": "commonJS",
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"*": ["./typings/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"exclude": [
|
||||||
|
"node_modules"
|
||||||
|
]
|
||||||
|
}
|
47
js/typings/hterm/index.d.ts
vendored
Normal file
47
js/typings/hterm/index.d.ts
vendored
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
export interface Terminal {
|
||||||
|
io: IO;
|
||||||
|
onTerminalReady: () => void;
|
||||||
|
|
||||||
|
getPrefs(): Prefs;
|
||||||
|
decorate(HTMLElement);
|
||||||
|
installKeyboard(): void;
|
||||||
|
uninstallKeyboard(): void;
|
||||||
|
setWindowTitle(title: string): void;
|
||||||
|
reset(): void;
|
||||||
|
softReset(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TerminalConstructor {
|
||||||
|
new (): Terminal;
|
||||||
|
(): Terminal;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export interface IO {
|
||||||
|
writeUTF8: ((data: string) => void);
|
||||||
|
writeUTF16: ((data: string) => void);
|
||||||
|
onVTKeystroke: ((data: string) => void) | null;
|
||||||
|
sendString: ((data: string) => void) | null;
|
||||||
|
onTerminalResize: ((columns: number, rows: number) => void) | null;
|
||||||
|
|
||||||
|
push(): IO;
|
||||||
|
writeUTF(data: string);
|
||||||
|
showOverlay(message: string, timeout: number | null);
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Prefs {
|
||||||
|
set(key: string, value: string): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Storage {
|
||||||
|
}
|
||||||
|
|
||||||
|
export var Terminal: TerminalConstructor;
|
||||||
|
|
||||||
|
// @TODO: is there better way?
|
||||||
|
// exported variables are forced to be read-protected.
|
||||||
|
declare global {
|
||||||
|
var hterm: {
|
||||||
|
defaultStorage: Storage;
|
||||||
|
};
|
||||||
|
}
|
11
js/typings/htermLib/index.d.ts
vendored
Normal file
11
js/typings/htermLib/index.d.ts
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
export interface Storage {
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Memory {
|
||||||
|
new (): Storage;
|
||||||
|
Memory(): Storage
|
||||||
|
}
|
||||||
|
|
||||||
|
export var Storage: {
|
||||||
|
Memory: Memory
|
||||||
|
};
|
23
js/webpack.config.js
Normal file
23
js/webpack.config.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
module.exports = {
|
||||||
|
entry: "./src/main.ts",
|
||||||
|
output: {
|
||||||
|
filename: "./dist/bundle.js"
|
||||||
|
},
|
||||||
|
externals: {
|
||||||
|
"hterm": "hterm",
|
||||||
|
"htermLib": "lib"
|
||||||
|
},
|
||||||
|
devtool: "source-map",
|
||||||
|
resolve: {
|
||||||
|
extensions: [".ts", ".tsx", ".js"],
|
||||||
|
},
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.tsx?$/,
|
||||||
|
loader: "ts-loader",
|
||||||
|
exclude: [/node_modules/],
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
@ -1,95 +0,0 @@
|
|||||||
(function() {
|
|
||||||
var httpsEnabled = window.location.protocol == "https:";
|
|
||||||
var args = window.location.search;
|
|
||||||
var url = (httpsEnabled ? 'wss://' : 'ws://') + window.location.host + window.location.pathname + 'ws';
|
|
||||||
var protocols = ["webtty"];
|
|
||||||
var autoReconnect = -1;
|
|
||||||
|
|
||||||
var openWs = function() {
|
|
||||||
var ws = new WebSocket(url, protocols);
|
|
||||||
|
|
||||||
var term;
|
|
||||||
|
|
||||||
var pingTimer;
|
|
||||||
|
|
||||||
ws.onopen = function(event) {
|
|
||||||
ws.send(JSON.stringify({ Arguments: args, AuthToken: gotty_auth_token,}));
|
|
||||||
pingTimer = setInterval(sendPing, 30 * 1000, ws);
|
|
||||||
|
|
||||||
hterm.defaultStorage = new lib.Storage.Memory();
|
|
||||||
|
|
||||||
term = new hterm.Terminal();
|
|
||||||
|
|
||||||
term.getPrefs().set("send-encoding", "raw");
|
|
||||||
|
|
||||||
term.onTerminalReady = function() {
|
|
||||||
var io = term.io.push();
|
|
||||||
|
|
||||||
io.onVTKeystroke = function(str) {
|
|
||||||
ws.send("1" + str);
|
|
||||||
};
|
|
||||||
|
|
||||||
io.sendString = io.onVTKeystroke;
|
|
||||||
|
|
||||||
io.onTerminalResize = function(columns, rows) {
|
|
||||||
ws.send(
|
|
||||||
"3" + JSON.stringify(
|
|
||||||
{
|
|
||||||
columns: columns,
|
|
||||||
rows: rows,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
term.installKeyboard();
|
|
||||||
};
|
|
||||||
|
|
||||||
term.decorate(document.getElementById("terminal"));
|
|
||||||
};
|
|
||||||
|
|
||||||
ws.onmessage = function(event) {
|
|
||||||
data = event.data.slice(1);
|
|
||||||
switch(event.data[0]) {
|
|
||||||
case '1':
|
|
||||||
term.io.writeUTF8(window.atob(data));
|
|
||||||
break;
|
|
||||||
case '2':
|
|
||||||
// pong
|
|
||||||
break;
|
|
||||||
case '3':
|
|
||||||
term.setWindowTitle(data);
|
|
||||||
break;
|
|
||||||
case '4':
|
|
||||||
preferences = JSON.parse(data);
|
|
||||||
Object.keys(preferences).forEach(function(key) {
|
|
||||||
console.log("Setting " + key + ": " + preferences[key]);
|
|
||||||
term.getPrefs().set(key, preferences[key]);
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case '5':
|
|
||||||
autoReconnect = JSON.parse(data);
|
|
||||||
console.log("Enabling reconnect: " + autoReconnect + " seconds")
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
ws.onclose = function(event) {
|
|
||||||
if (term) {
|
|
||||||
term.uninstallKeyboard();
|
|
||||||
term.io.showOverlay("Connection Closed", null);
|
|
||||||
}
|
|
||||||
clearInterval(pingTimer);
|
|
||||||
if (autoReconnect > 0) {
|
|
||||||
setTimeout(openWs, autoReconnect * 1000);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var sendPing = function(ws) {
|
|
||||||
ws.send("2");
|
|
||||||
}
|
|
||||||
|
|
||||||
openWs();
|
|
||||||
})()
|
|
7
resources/index.css
Normal file
7
resources/index.css
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
html, body, #terminal {
|
||||||
|
background: black;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0%;
|
||||||
|
margin: 0%;
|
||||||
|
}
|
@ -2,13 +2,16 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>{{ .title }}</title>
|
<title>{{ .title }}</title>
|
||||||
<style>body, #terminal {position: absolute; height: 100%; width: 100%; margin: 0px;}</style>
|
|
||||||
<link rel="icon" type="image/png" href="favicon.png">
|
<link rel="icon" type="image/png" href="favicon.png">
|
||||||
|
<link rel="stylesheet" href="./css/index.css" />
|
||||||
|
<link rel="stylesheet" href="./css/xterm.css" />
|
||||||
|
<link rel="stylesheet" href="./css/xterm_customize.css" />
|
||||||
|
<script src="/js/hterm.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="terminal"></div>
|
<div id="terminal"></div>
|
||||||
<script src="./js/hterm.js"></script>
|
|
||||||
<script src="./auth_token.js"></script>
|
<script src="./auth_token.js"></script>
|
||||||
<script src="./js/gotty.js"></script>
|
<script src="./config.js"></script>
|
||||||
|
<script src="./js/bundle.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
19
resources/xterm_customize.css
Normal file
19
resources/xterm_customize.css
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
.terminal {
|
||||||
|
font-family: "DejaVu Sans Mono", "Everson Mono", FreeMono, Menlo, Terminal, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.xterm-overlay {
|
||||||
|
font-family: "DejaVu Sans Mono", "Everson Mono", FreeMono, Menlo, Terminal, monospace;
|
||||||
|
border-radius: 15px;
|
||||||
|
font-size: xx-large;
|
||||||
|
color: black;
|
||||||
|
background: white;
|
||||||
|
opacity: 0.75;
|
||||||
|
padding: 0.2em 0.5em 0.2em 0.5em;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
user-select: none;
|
||||||
|
transition: opacity 180ms ease-in;
|
||||||
|
}
|
109
server/asset.go
109
server/asset.go
File diff suppressed because one or more lines are too long
@ -218,6 +218,11 @@ func (server *Server) handleAuthToken(w http.ResponseWriter, r *http.Request) {
|
|||||||
w.Write([]byte("var gotty_auth_token = '" + server.options.Credential + "';"))
|
w.Write([]byte("var gotty_auth_token = '" + server.options.Credential + "';"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (server *Server) handleConfig(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/javascript")
|
||||||
|
w.Write([]byte("var gotty_term = '" + server.options.Term + "';"))
|
||||||
|
}
|
||||||
|
|
||||||
// titleVariables merges maps in a specified order.
|
// titleVariables merges maps in a specified order.
|
||||||
// varUnits are name-keyed maps, whose names will be iterated using order.
|
// varUnits are name-keyed maps, whose names will be iterated using order.
|
||||||
func (server *Server) titleVariables(order []string, varUnits map[string]map[string]interface{}) map[string]interface{} {
|
func (server *Server) titleVariables(order []string, varUnits map[string]map[string]interface{}) map[string]interface{} {
|
||||||
|
@ -29,6 +29,7 @@ type Options struct {
|
|||||||
Width int `hcl:"width" flagName:"width" flagDescribe:"Static width of the screen, 0(default) means dynamically resize" default:"0"`
|
Width int `hcl:"width" flagName:"width" flagDescribe:"Static width of the screen, 0(default) means dynamically resize" default:"0"`
|
||||||
Height int `hcl:"height" flagName:"height" flagDescribe:"Static height of the screen, 0(default) means dynamically resize" default:"0"`
|
Height int `hcl:"height" flagName:"height" flagDescribe:"Static height of the screen, 0(default) means dynamically resize" default:"0"`
|
||||||
WSOrigin string `hcl:"ws_origin" flagName:"ws-origin" flagDescribe:"A regular expression that matches origin URLs to be accepted by WebSocket. No cross origin requests are acceptable by default" default:""`
|
WSOrigin string `hcl:"ws_origin" flagName:"ws-origin" flagDescribe:"A regular expression that matches origin URLs to be accepted by WebSocket. No cross origin requests are acceptable by default" default:""`
|
||||||
|
Term string `hcl:"term" flagName:"term" flagDescribe:"Terminal name to use on the browser, one of xterm or hterm." default:"xterm"`
|
||||||
|
|
||||||
TitleVariables map[string]interface{}
|
TitleVariables map[string]interface{}
|
||||||
}
|
}
|
||||||
|
@ -186,7 +186,9 @@ func (server *Server) setupHandlers(ctx context.Context, cancel context.CancelFu
|
|||||||
siteMux.HandleFunc(url.Path, server.handleIndex)
|
siteMux.HandleFunc(url.Path, server.handleIndex)
|
||||||
siteMux.Handle(url.Path+"js/", http.StripPrefix(url.Path, staticFileHandler))
|
siteMux.Handle(url.Path+"js/", http.StripPrefix(url.Path, staticFileHandler))
|
||||||
siteMux.Handle(url.Path+"favicon.png", http.StripPrefix(url.Path, staticFileHandler))
|
siteMux.Handle(url.Path+"favicon.png", http.StripPrefix(url.Path, staticFileHandler))
|
||||||
|
siteMux.Handle(url.Path+"css/", http.StripPrefix(url.Path, staticFileHandler))
|
||||||
siteMux.HandleFunc(url.Path+"auth_token.js", server.handleAuthToken)
|
siteMux.HandleFunc(url.Path+"auth_token.js", server.handleAuthToken)
|
||||||
|
siteMux.HandleFunc(url.Path+"config.js", server.handleConfig)
|
||||||
|
|
||||||
siteHandler := http.Handler(siteMux)
|
siteHandler := http.Handler(siteMux)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user