Replace go-bindata with go 1.16 embed (#452)

This commit is contained in:
Marcos Nils
2021-02-21 13:58:29 -03:00
committed by GitHub
parent cd6815aed5
commit 3a762ba15c
48 changed files with 21 additions and 576 deletions

22
handlers/www/503.html Normal file
View File

@@ -0,0 +1,22 @@
<!doctype html>
<html>
<head>
<title>Docker Playground</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700,400italic|Material+Icons" />
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/angular_material/1.1.0/angular-material.min.css">
<link rel="stylesheet" href="/assets/style.css" />
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-89019737-1', 'auto');
ga('send', 'pageview');
</script>
</head>
<body>
<div layout="column" style="height:100%;">
An error has occurred. If you have some time, please report it. Thanks!
</div>
</body>
</html>

928
handlers/www/assets/app.js Normal file
View File

@@ -0,0 +1,928 @@
(function() {
'use strict';
var app = angular.module('DockerPlay', ['ngMaterial', 'ngFileUpload', 'ngclipboard']);
// Automatically redirects user to a new session when bypassing captcha.
// Controller keeps code/logic separate from the HTML
app.controller("BypassController", ['$scope', '$log', '$http', '$location', '$timeout', function($scope, $log, $http, $location, $timeout) {
setTimeout(function() {
document.getElementById("welcomeFormBypass").submit();
}, 500);
}]);
function SessionBuilderModalController($mdDialog, $scope) {
$scope.createBuilderTerminal();
$scope.closeSessionBuilder = function() {
$mdDialog.cancel();
}
}
app.controller('PlayController', ['$scope', '$rootScope', '$log', '$http', '$location', '$timeout', '$mdDialog', '$window', 'TerminalService', 'KeyboardShortcutService', 'InstanceService', 'SessionService', 'Upload', function($scope, $rootScope, $log, $http, $location, $timeout, $mdDialog, $window, TerminalService, KeyboardShortcutService, InstanceService, SessionService, Upload) {
$scope.sessionId = SessionService.getCurrentSessionId();
$rootScope.instances = [];
$scope.idx = {};
$scope.host = window.location.host;
$scope.idxByHostname = {};
$rootScope.selectedInstance = null;
$scope.isAlive = true;
$scope.ttl = '--:--:--';
$scope.connected = false;
$scope.type = {windows: false};
$scope.isInstanceBeingCreated = false;
$scope.newInstanceBtnText = '+ Add new instance';
$scope.deleteInstanceBtnText = 'Delete';
$scope.isInstanceBeingDeleted = false;
$scope.uploadProgress = 0;
$scope.uploadFiles = function (files, invalidFiles) {
let total = files.length;
let uploadFile = function() {
let file = files.shift();
if (!file){
$scope.uploadMessage = "";
$scope.uploadProgress = 0;
return
}
$scope.uploadMessage = "Uploading file(s) " + (total - files.length) + "/"+ total + " : " + file.name;
let upload = Upload.upload({url: '/sessions/' + $scope.sessionId + '/instances/' + $rootScope.selectedInstance.name + '/uploads', data: {file: file}, method: 'POST'})
.then(function(){}, function(){}, function(evt) {
$scope.uploadProgress = parseInt(100.0 * evt.loaded / evt.total);
});
// process next file
upload.finally(uploadFile);
}
uploadFile();
}
var selectedKeyboardShortcuts = KeyboardShortcutService.getCurrentShortcuts();
$scope.resizeHandler = null;
angular.element($window).bind('resize', function() {
if ($rootScope.selectedInstance) {
if (!$scope.resizeHandler) {
$scope.resizeHandler = setTimeout(function() {
$scope.resizeHandler = null
$scope.resize($scope.selectedInstance.term.proposeGeometry());
}, 1000);
}
}
});
$scope.$on("settings:shortcutsSelected", function(e, preset) {
selectedKeyboardShortcuts = preset;
});
$scope.showAlert = function(title, content, parent, cb) {
$mdDialog.show(
$mdDialog.alert()
.parent(angular.element(document.querySelector(parent || '#popupContainer')))
.clickOutsideToClose(true)
.title(title)
.textContent(content)
.ok('Got it!')
).finally(function() {
if (cb) {
cb();
}
});
}
$scope.resize = function(geometry) {
$scope.socket.emit('instance viewport resize', geometry.cols, geometry.rows);
}
KeyboardShortcutService.setResizeFunc($scope.resize);
$scope.closeSession = function() {
// Remove alert before closing browser tab
window.onbeforeunload = null;
$scope.socket.emit('session close');
}
$scope.upsertInstance = function(info) {
var i = info;
if (!$scope.idx[i.name]) {
$rootScope.instances.push(i);
i.buffer = '';
$scope.idx[i.name] = i;
$scope.idxByHostname[i.hostname] = i;
} else {
$scope.idx[i.name] = Object.assign($scope.idx[i.name], info);
}
return $scope.idx[i.name];
}
$scope.newInstance = function() {
updateNewInstanceBtnState(true);
var instanceType = $scope.type.windows ? 'windows': 'linux';
$http({
method: 'POST',
url: '/sessions/' + $scope.sessionId + '/instances',
data : { ImageName : InstanceService.getDesiredImage(), type: instanceType }
}).then(function(response) {
$scope.upsertInstance(response.data);
}, function(response) {
if (response.status == 409) {
$scope.showAlert('Max instances reached', 'Maximum number of instances reached')
} else if (response.status == 503 && response.data.error == 'out_of_capacity') {
$scope.showAlert('Out Of Capacity', 'We are really sorry. But we are currently out of capacity and cannot create new instances. Please try again later.')
}
}).finally(function() {
updateNewInstanceBtnState(false);
});
}
$scope.setSessionState = function(state) {
$scope.ready = state;
if (!state) {
$mdDialog.show({
onComplete: function(){SessionBuilderModalController($mdDialog, $scope)},
contentElement: '#builderDialog',
parent: angular.element(document.body),
clickOutsideToClose: false,
scope: $scope,
preserveScope: true
});
}
}
$scope.loadPlaygroundConf = function() {
$http({
method: 'GET',
url: '/my/playground',
}).then(function(response) {
$scope.playground = response.data;
});
}
$scope.getSession = function(sessionId) {
$http({
method: 'GET',
url: '/sessions/' + $scope.sessionId,
}).then(function(response) {
$scope.setSessionState(response.data.ready);
if (response.data.created_at) {
$scope.expiresAt = moment(response.data.expires_at);
setInterval(function() {
$scope.ttl = moment.utc($scope.expiresAt.diff(moment())).format('HH:mm:ss');
$scope.$apply();
}, 1000);
}
var i = response.data;
for (var k in i.instances) {
var instance = i.instances[k];
$rootScope.instances.push(instance);
$scope.idx[instance.name] = instance;
$scope.idxByHostname[instance.hostname] = instance;
}
var base = '';
if (window.location.protocol == 'http:') {
base = 'ws://';
} else {
base = 'wss://';
}
base += window.location.host;
if (window.location.port) {
base += ':' + window.location.port;
}
var socket = new ReconnectingWebSocket(base + '/sessions/' + sessionId + '/ws/', null, {reconnectInterval: 1000});
socket.listeners = {};
socket.on = function(name, cb) {
if (!socket.listeners[name]) {
socket.listeners[name] = [];
}
socket.listeners[name].push(cb);
}
socket.emit = function() {
var name = arguments[0]
var args = [];
for (var i = 1; i < arguments.length; i++) {
args.push(arguments[i]);
}
socket.send(JSON.stringify({name: name, args: args}));
}
socket.addEventListener('open', function (event) {
$scope.connected = true;
for (var i in $rootScope.instances) {
var instance = $rootScope.instances[i];
if (instance.term) {
instance.term.setOption('disableStdin', false);
}
}
});
socket.addEventListener('close', function (event) {
$scope.connected = false;
for (var i in $rootScope.instances) {
var instance = $rootScope.instances[i];
if (instance.term) {
instance.term.setOption('disableStdin', true);
}
}
});
socket.addEventListener('message', function (event) {
var m = JSON.parse(event.data);
var ls = socket.listeners[m.name];
if (ls) {
for (var i=0; i<ls.length; i++) {
var l = ls[i];
l.apply(l, m.args);
}
}
});
socket.on('instance terminal status', function(name, status) {
var instance = $scope.idx[name];
if (instance) {
instance.status = status;
}
});
socket.on('session ready', function(ready) {
$scope.setSessionState(ready);
});
socket.on('session builder out', function(data) {
$scope.builderTerminal.write(data);
});
socket.on('instance terminal out', function(name, data) {
var instance = $scope.idx[name];
if (!instance) {
return;
}
if (!instance) {
// instance is new and was created from another client, we should add it
$scope.upsertInstance({ name: name });
instance = $scope.idx[name];
}
if (!instance.term) {
instance.buffer += data;
} else {
instance.term.write(data);
}
});
socket.on('session end', function() {
$scope.showAlert('Session timed out!', 'Your session has expired and all of your instances have been deleted.', '#sessionEnd', function() {
window.location.href = '/';
});
$scope.isAlive = false;
socket.close();
});
socket.on('instance new', function(name, ip, hostname, proxyHost) {
var instance = $scope.upsertInstance({ name: name, ip: ip, hostname: hostname, proxy_host: proxyHost, session_id: $scope.sessionId});
$scope.$apply(function() {
$scope.showInstance(instance);
});
});
socket.on('instance delete', function(name) {
$scope.removeInstance(name);
$scope.$apply();
});
socket.on('instance viewport resize', function(cols, rows) {
if (cols == 0 || rows == 0) {
return
}
// viewport has changed, we need to resize all terminals
$rootScope.instances.forEach(function(instance) {
if (instance.term) {
instance.term.resize(cols, rows);
if (instance.buffer) {
instance.term.write(instance.buffer);
instance.buffer = '';
}
}
});
});
socket.on('instance stats', function(stats) {
if (! $scope.idx[stats.instance]) {
return
}
$scope.idx[stats.instance].mem = stats.mem;
$scope.idx[stats.instance].cpu = stats.cpu;
$scope.$apply();
});
socket.on('instance docker swarm status', function(status) {
if (!$scope.idx[status.instance]) {
return
}
if (status.is_manager) {
$scope.idx[status.instance].isManager = true
} else if (status.is_worker) {
$scope.idx[status.instance].isManager = false
} else {
$scope.idx[status.instance].isManager = null
}
$scope.$apply();
});
socket.on('instance k8s status', function(status) {
if (!$scope.idx[status.instance]) {
return
}
if (status.is_manager) {
$scope.idx[status.instance].isK8sManager = true
} else if (status.is_worker) {
$scope.idx[status.instance].isK8sManager = false
} else {
$scope.idx[status.instance].isK8sManager = null
}
$scope.$apply();
});
socket.on('instance docker ports', function(status) {
if (!$scope.idx[status.instance]) {
return
}
$scope.idx[status.instance].ports = status.ports;
$scope.$apply();
});
socket.on('instance docker swarm ports', function(status) {
for(var i in status.instances) {
var instance = status.instances[i];
if ($scope.idxByHostname[instance]) {
$scope.idxByHostname[instance].swarmPorts = status.ports;
}
}
$scope.$apply();
});
$scope.socket = socket;
// If instance is passed in URL, select it
let inst = $scope.idx[$location.hash()];
if (inst) {
$scope.showInstance(inst);
} else if($rootScope.instances.length > 0) {
// if no instance has been passed, select the first.
$scope.showInstance($rootScope.instances[0]);
}
}, function(response) {
if (response.status == 404) {
document.write('session not found');
return
}
});
}
$scope.openPort = function(instance) {
var port = prompt('What port would you like to open?');
if (!port) return;
var url = $scope.getProxyUrl(instance, port);
window.open(url, '_blank');
}
$scope.getProxyUrl = function(instance, port) {
var url = 'http://' + instance.proxy_host + '-' + port + '.direct.' + $scope.host;
return url;
}
$scope.showInstance = function(instance) {
$rootScope.selectedInstance = instance;
$location.hash(instance.name);
if (!instance.term) {
$timeout(function() {
createTerminal(instance);
TerminalService.setFontSize(TerminalService.getFontSize());
instance.term.focus();
$timeout(function() {
}, 0, false);
}, 0, false);
return
}
instance.term.focus();
}
$scope.removeInstance = function(name) {
if ($scope.idx[name]) {
var handler = $scope.idx[name].terminalBufferInterval;
clearInterval(handler);
}
if ($scope.idx[name]) {
delete $scope.idx[name];
$rootScope.instances = $rootScope.instances.filter(function(i) {
return i.name != name;
});
if ($rootScope.instances.length) {
$scope.showInstance($rootScope.instances[0]);
}
}
}
$scope.deleteInstance = function(instance) {
updateDeleteInstanceBtnState(true);
$http({
method: 'DELETE',
url: '/sessions/' + $scope.sessionId + '/instances/' + instance.name,
}).then(function(response) {
$scope.removeInstance(instance.name);
}, function(response) {
console.log('error', response);
}).finally(function() {
updateDeleteInstanceBtnState(false);
});
};
$scope.openEditor = function(instance) {
var w = window.screen.availWidth * 45 / 100;
var h = window.screen.availHeight * 45 / 100;
$window.open('/sessions/' + instance.session_id + '/instances/'+instance.name+'/editor', 'editor',
'width='+w+',height='+h+',resizable,scrollbars=yes,status=1');
};
$scope.loadPlaygroundConf();
$scope.getSession($scope.sessionId);
$scope.createBuilderTerminal = function() {
var builderTerminalContainer = document.getElementById('builder-terminal');
let term = new Terminal({
cursorBlink: false
});
term.open(builderTerminalContainer);
$scope.builderTerminal = term;
}
function createTerminal(instance, cb) {
if (instance.term) {
return instance.term;
}
var terminalContainer = document.getElementById('terminal-' + instance.name);
var term = new Terminal({
cursorBlink: false,
screenReaderMode: true
});
term.open(terminalContainer);
const handleCopy = (e) => {
// Ctrl + Alt + C
if (e.ctrlKey && e.altKey && (e.keyCode == 67)) {
document.execCommand('copy');
return false;
}
};
term.attachCustomKeyEventHandler(function(e) {
// handleCopy(e);
if (selectedKeyboardShortcuts == null) return;
var presets = selectedKeyboardShortcuts.presets
.filter(function(preset) { return preset.keyCode == e.keyCode })
.filter(function(preset) { return (preset.metaKey == undefined && !e.metaKey) || preset.metaKey == e.metaKey })
.filter(function(preset) { return (preset.ctrlKey == undefined && !e.ctrlKey) || preset.ctrlKey == e.ctrlKey })
.filter(function(preset) { return (preset.altKey == undefined && !e.altKey) || preset.altKey == e.altKey })
.forEach(function(preset) { preset.action({ terminal : term, e })});
});
// Set geometry during the next tick, to avoid race conditions.
setTimeout(function() {
$scope.resize(term.proposeGeometry());
}, 0);
instance.terminalBuffer = '';
instance.terminalBufferInterval = setInterval(function() {
if (instance.terminalBuffer.length > 0) {
$scope.socket.emit('instance terminal in', instance.name, instance.terminalBuffer);
instance.terminalBuffer = '';
}
}, 70);
term.on('data', function(d) {
instance.terminalBuffer += d;
});
instance.term = term;
if (cb) {
cb();
}
}
function updateNewInstanceBtnState(isInstanceBeingCreated) {
if (isInstanceBeingCreated === true) {
$scope.newInstanceBtnText = '+ Creating...';
$scope.isInstanceBeingCreated = true;
} else {
$scope.newInstanceBtnText = '+ Add new instance';
$scope.isInstanceBeingCreated = false;
}
}
function updateDeleteInstanceBtnState(isInstanceBeingDeleted) {
if (isInstanceBeingDeleted === true) {
$scope.deleteInstanceBtnText = 'Deleting...';
$scope.isInstanceBeingDeleted = true;
} else {
$scope.deleteInstanceBtnText = 'Delete';
$scope.isInstanceBeingDeleted = false;
}
}
}])
.config(['$mdIconProvider', '$locationProvider', '$mdThemingProvider', function($mdIconProvider, $locationProvider, $mdThemingProvider) {
$locationProvider.html5Mode({enabled: true, requireBase: false});
$mdIconProvider.defaultIconSet('../assets/social-icons.svg', 24);
$mdThemingProvider.theme('kube')
.primaryPalette('grey')
.accentPalette('grey');
}])
.component('settingsIcon', {
template : "<md-button class='md-mini' ng-click='$ctrl.onClick()'><md-icon class='material-icons'>settings</md-icon></md-button>",
controller : function($mdDialog) {
var $ctrl = this;
$ctrl.onClick = function() {
$mdDialog.show({
controller : function() {},
template : "<settings-dialog></settings-dialog>",
parent: angular.element(document.body),
clickOutsideToClose : true
})
}
}
})
.component('templatesIcon', {
template : "<md-button class='md-mini' ng-click='$ctrl.onClick()'><md-icon class='material-icons'>build</md-icon></md-button>",
controller : function($mdDialog) {
var $ctrl = this;
$ctrl.onClick = function() {
$mdDialog.show({
controller : function() {},
template : "<templates-dialog></templates-dialog>",
parent: angular.element(document.body),
clickOutsideToClose : true
})
}
}
})
.component("templatesDialog", {
templateUrl : "templates-modal.html",
controller : function($mdDialog, $scope, SessionService) {
var $ctrl = this;
$scope.building = false;
$scope.templates = SessionService.getAvailableTemplates();
$ctrl.close = function() {
$mdDialog.cancel();
}
$ctrl.setupSession = function(setup) {
$scope.building = true;
SessionService.setup(setup, function(err) {
$scope.building = false;
if (err) {
$scope.errorMessage = err;
return;
}
$ctrl.close();
});
}
}
})
.component("settingsDialog", {
templateUrl : "settings-modal.html",
controller : function($mdDialog, KeyboardShortcutService, $rootScope, InstanceService, TerminalService) {
var $ctrl = this;
$ctrl.$onInit = function() {
$ctrl.keyboardShortcutPresets = KeyboardShortcutService.getAvailablePresets();
$ctrl.selectedShortcutPreset = KeyboardShortcutService.getCurrentShortcuts();
$ctrl.instanceImages = InstanceService.getAvailableImages();
$ctrl.selectedInstanceImage = InstanceService.getDesiredImage();
$ctrl.terminalFontSizes = TerminalService.getFontSizes();
};
$ctrl.currentShortcutConfig = function(value) {
if (value !== undefined) {
value = JSON.parse(value);
KeyboardShortcutService.setCurrentShortcuts(value);
$ctrl.selectedShortcutPreset = angular.copy(KeyboardShortcutService.getCurrentShortcuts());
$rootScope.$broadcast('settings:shortcutsSelected', $ctrl.selectedShortcutPreset);
}
return JSON.stringify(KeyboardShortcutService.getCurrentShortcuts());
};
$ctrl.currentDesiredInstanceImage = function(value) {
if (value !== undefined) {
InstanceService.setDesiredImage(value);
}
return InstanceService.getDesiredImage(value);
};
$ctrl.currentTerminalFontSize = function(value) {
if (value !== undefined) {
// set font size
TerminalService.setFontSize(value);
return;
}
return TerminalService.getFontSize();
}
$ctrl.close = function() {
$mdDialog.cancel();
}
}
})
.service("SessionService", function($http) {
var templates = [
{
title: '3 Managers and 2 Workers',
icon: '/assets/swarm.png',
setup: {
instances: [
{hostname: 'manager1', is_swarm_manager: true},
{hostname: 'manager2', is_swarm_manager: true},
{hostname: 'manager3', is_swarm_manager: true},
{hostname: 'worker1', is_swarm_worker: true},
{hostname: 'worker2', is_swarm_worker: true}
]
}
},
{
title: '5 Managers and no workers',
icon: '/assets/swarm.png',
setup: {
instances: [
{hostname: 'manager1', is_swarm_manager: true},
{hostname: 'manager2', is_swarm_manager: true},
{hostname: 'manager3', is_swarm_manager: true},
{hostname: 'manager4', is_swarm_manager: true},
{hostname: 'manager5', is_swarm_manager: true}
]
}
},
{
title: '1 Manager and 1 Worker',
icon: '/assets/swarm.png',
setup: {
instances: [
{hostname: 'manager1', is_swarm_manager: true},
{hostname: 'worker1', is_swarm_worker: true}
]
}
}
];
return {
getAvailableTemplates: getAvailableTemplates,
getCurrentSessionId: getCurrentSessionId,
setup: setup,
};
function getCurrentSessionId() {
return window.location.pathname.replace('/p/', '');
}
function getAvailableTemplates() {
return templates;
}
function setup(plan, cb) {
return $http
.post("/sessions/" + getCurrentSessionId() + "/setup", plan)
.then(function(response) {
if (cb) cb();
}, function(response) {
if (cb) cb(response.data);
});
}
})
.service("InstanceService", function($http) {
var instanceImages = [];
_prepopulateAvailableImages();
return {
getAvailableImages : getAvailableImages,
setDesiredImage : setDesiredImage,
getDesiredImage : getDesiredImage,
};
function getAvailableImages() {
return instanceImages;
}
function getDesiredImage() {
var image = localStorage.getItem("settings.desiredImage");
if (image == null)
return instanceImages[0];
return image;
}
function setDesiredImage(image) {
if (image === null)
localStorage.removeItem("settings.desiredImage");
else
localStorage.setItem("settings.desiredImage", image);
}
function _prepopulateAvailableImages() {
return $http
.get("/instances/images")
.then(function(response) {
instanceImages = response.data;
});
}
})
.run(function(InstanceService) { /* forcing pre-populating for now */ })
.service("KeyboardShortcutService", ['TerminalService', function(TerminalService) {
var resizeFunc;
return {
getAvailablePresets : getAvailablePresets,
getCurrentShortcuts : getCurrentShortcuts,
setCurrentShortcuts : setCurrentShortcuts,
setResizeFunc : setResizeFunc
};
function setResizeFunc(f) {
resizeFunc = f;
}
function getAvailablePresets() {
return [
{
name : "None",
presets : [
{
description : "Toggle terminal fullscreen", command : "Alt+enter", altKey : true, keyCode : 13, action : function(context) { TerminalService.toggleFullScreen(context.terminal, resizeFunc); }
},
{
description: "Increase Font Size",
command: "Ctrl++",
ctrlKey : true,
keyCode: 187,
action: function(context) {
TerminalService.increaseFontSize();
context.e.preventDefault()
}
},
{
description: "Decrease Font Size",
command: "Ctrl+-",
ctrlKey: true,
keyCode: 189,
action: function(context) {
context.e.preventDefault()
TerminalService.decreaseFontSize();
}
}
]
},
{
name : "Mac OSX",
presets : [
{ description : "Clear terminal", command : "Cmd+K", metaKey : true, keyCode : 75, action : function(context) { context.terminal.clear(); }},
{ description : "Toggle terminal fullscreen", command : "Alt+enter", altKey : true, keyCode : 13, action : function(context) { TerminalService.toggleFullScreen(context.terminal, resizeFunc); }},
{
description: "Increase Font Size",
command: "Cmd++",
metaKey : true,
keyCode: 187,
action: function(context) {
TerminalService.increaseFontSize();
context.e.preventDefault()
}
},
{
description: "Decrease Font Size",
command: "Cmd+-",
metaKey: true,
keyCode: 189,
action: function(context) {
context.e.preventDefault()
TerminalService.decreaseFontSize();
}
}
]
}
]
}
function getCurrentShortcuts() {
var shortcuts = localStorage.getItem("shortcut-preset-name");
if (shortcuts == null) {
shortcuts = getDefaultShortcutPrefixName();
if (shortcuts == null)
return null;
}
var preset = getAvailablePresets()
.filter(function(preset) { return preset.name == shortcuts; });
if (preset.length == 0)
console.error("Unable to find preset with name '" + shortcuts + "'");
return preset[0];
return (shortcuts == null) ? null : JSON.parse(shortcuts);
}
function setCurrentShortcuts(config) {
localStorage.setItem("shortcut-preset-name", config.name);
}
function getDefaultShortcutPrefixName() {
if (window.navigator.platform.toUpperCase().indexOf('MAC') >= 0)
return "Mac OSX";
return "None";
}
}])
.service('TerminalService', ['$window', '$rootScope', function($window, $rootScope) {
var fullscreen;
var fontSize = getFontSize();
return {
getFontSizes : getFontSizes,
setFontSize : setFontSize,
getFontSize : getFontSize,
increaseFontSize : increaseFontSize,
decreaseFontSize : decreaseFontSize,
toggleFullScreen : toggleFullScreen
};
function getFontSizes() {
var terminalFontSizes = [];
for (var i=3; i<40; i++) {
terminalFontSizes.push(i+'px');
}
return terminalFontSizes;
};
function getFontSize() {
if($rootScope.selectedInstance){
return $rootScope.selectedInstance.term.getOption("fontSize") + "px"
}else{
return $(".terminal").css("font-size")
}
}
function setFontSize(value) {
const { term }= $rootScope.selectedInstance;
fontSize = value;
var size = parseInt(value);
term.setOption("fontSize", size)
term.resize(1,1)
term.fit()
}
function increaseFontSize() {
var sizes = getFontSizes();
var size = getFontSize();
var i = sizes.indexOf(size);
if (i == -1) {
return;
}
if (i+1 > sizes.length) {
return;
}
setFontSize(sizes[i+1]);
}
function decreaseFontSize() {
var sizes = getFontSizes();
var size = getFontSize();
var i = sizes.indexOf(size);
if (i == -1) {
return;
}
if (i-1 < 0) {
return;
}
setFontSize(sizes[i-1]);
}
function toggleFullScreen(terminal, resize) {
if(fullscreen) {
terminal.toggleFullScreen();
terminal.containerElement.append(terminal.element)
setTimeout(()=>{
terminal.resize(1,1);
terminal.fit();
terminal.focus();
},100)
fullscreen = null;
} else {
// save the current parent
terminal.containerElement = $(terminal.element).parent()
$("body").append(terminal.element)
fullscreen = terminal.proposeGeometry();
terminal.toggleFullScreen();
terminal.fit();
terminal.focus();
}
}
}]);
})();

View File

@@ -0,0 +1,134 @@
/*
* Implements the attach method, that
* attaches the terminal to a WebSocket stream.
*
* The bidirectional argument indicates, whether the terminal should
* send data to the socket as well and is true, by default.
*/
(function (attach) {
if (typeof exports === 'object' && typeof module === 'object') {
/*
* CommonJS environment
*/
module.exports = attach(require('../../src/xterm'));
} else if (typeof define == 'function') {
/*
* Require.js is available
*/
define(['../../src/xterm'], attach);
} else {
/*
* Plain browser environment
*/
attach(window.Terminal);
}
})(function (Xterm) {
'use strict';
/**
* This module provides methods for attaching a terminal to a WebSocket
* stream.
*
* @module xterm/addons/attach/attach
*/
var exports = {};
/**
* Attaches the given terminal to the given socket.
*
* @param {Xterm} term - The terminal to be attached to the given socket.
* @param {WebSocket} socket - The socket to attach the current terminal.
* @param {boolean} bidirectional - Whether the terminal should send data
* to the socket as well.
* @param {boolean} buffered - Whether the rendering of incoming data
* should happen instantly or at a maximum
* frequency of 1 rendering per 10ms.
*/
exports.attach = function (term, socket, bidirectional, buffered) {
bidirectional = (typeof bidirectional == 'undefined') ? true : bidirectional;
term.socket = socket;
term._flushBuffer = function () {
term.write(term._attachSocketBuffer);
term._attachSocketBuffer = null;
clearTimeout(term._attachSocketBufferTimer);
term._attachSocketBufferTimer = null;
};
term._pushToBuffer = function (data) {
if (term._attachSocketBuffer) {
term._attachSocketBuffer += data;
} else {
term._attachSocketBuffer = data;
setTimeout(term._flushBuffer, 10);
}
};
term._getMessage = function (ev) {
if (buffered) {
term._pushToBuffer(ev.data);
} else {
term.write(ev.data);
}
};
term._sendData = function (data) {
socket.send(data);
};
socket.addEventListener('message', term._getMessage);
if (bidirectional) {
term.on('data', term._sendData);
}
socket.addEventListener('close', term.detach.bind(term, socket));
socket.addEventListener('error', term.detach.bind(term, socket));
};
/**
* Detaches the given terminal from the given socket
*
* @param {Xterm} term - The terminal to be detached from the given socket.
* @param {WebSocket} socket - The socket from which to detach the current
* terminal.
*/
exports.detach = function (term, socket) {
term.off('data', term._sendData);
socket = (typeof socket == 'undefined') ? term.socket : socket;
if (socket) {
socket.removeEventListener('message', term._getMessage);
}
delete term.socket;
};
/**
* Attaches the current terminal to the given socket
*
* @param {WebSocket} socket - The socket to attach the current terminal.
* @param {boolean} bidirectional - Whether the terminal should send data
* to the socket as well.
* @param {boolean} buffered - Whether the rendering of incoming data
* should happen instantly or at a maximum
* frequency of 1 rendering per 10ms.
*/
Xterm.prototype.attach = function (socket, bidirectional, buffered) {
return exports.attach(this, socket, bidirectional, buffered);
};
/**
* Detaches the current terminal from the given socket.
*
* @param {WebSocket} socket - The socket from which to detach the current
* terminal.
*/
Xterm.prototype.detach = function (socket) {
return exports.detach(this, socket);
};
return exports;
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@@ -0,0 +1,21 @@
.alert-top {
position: absolute;
top: 0;
right: 0;
width:100px;
display:none;
text-align: center;
padding: 3px;
height: 30px;
margin-bottom: 0px;
}
.alert-newfile {
text-align: center;
padding: 3px;
font-size: 15px;
}
.col-md-3 {
overflow-x: auto;
}

View File

@@ -0,0 +1,72 @@
<svg viewBox="0 0 340 100" xmlns="http://www.w3.org/2000/svg">
<style type="text/css">.st0{fill:#099CEC;}
.st1{fill:#066DA5;}
.st2{fill:#089CEC;}
.st3{fill:#FFFFFF;}
.st4{fill:#056BA2;}
.st5{opacity:0.6;fill:#101E26;enable-background:new ;}
.st6{opacity:0.1;fill:#101E26;enable-background:new ;}</style>
<title>full_horizontal</title>
<desc>Created with Sketch.</desc>
<g>
<title>background</title>
<rect fill="none" id="canvas_background" height="102" width="342" y="-1" x="-1"/>
</g>
<g>
<title>Layer 1</title>
<polygon points="58.62161469459534,32 69.6216299533844,32 69.6216299533844,42 58.62161469459534,42 " class="st0" id="Rectangle"/>
<polygon points="71.6216299533844,32 82.6216299533844,32 82.6216299533844,42 71.6216299533844,42 " class="st0" id="Rectangle_1_"/>
<polygon points="84.6216299533844,32 95.6216299533844,32 95.6216299533844,42 84.6216299533844,42 " class="st0" id="Rectangle_2_"/>
<polygon points="97.6216299533844,32 108.6216299533844,32 108.6216299533844,42 97.6216299533844,42 " class="st0" id="Rectangle_3_"/>
<polygon points="110.6216299533844,32 121.6216299533844,32 121.6216299533844,42 110.6216299533844,42 " class="st0" id="Rectangle_4_"/>
<polygon points="71.6216299533844,20 82.6216299533844,20 82.6216299533844,30 71.6216299533844,30 " class="st0" id="Rectangle_5_"/>
<polygon points="84.6216299533844,20 95.6216299533844,20 95.6216299533844,30 84.6216299533844,30 " class="st0" id="Rectangle_6_"/>
<polygon points="97.6216299533844,20 108.6216299533844,20 108.6216299533844,30 97.6216299533844,30 " class="st0" id="Rectangle_7_"/>
<polygon points="97.6216299533844,8 108.6216299533844,8 108.6216299533844,18 97.6216299533844,18 " class="st0" id="Rectangle_8_"/>
<path d="m296.221621,53.4c-0.4,-0.4 -0.9,-0.7 -1.5,-0.9c-0.6,-0.2 -1.3,-0.4 -2,-0.5c-0.7,-0.1 -1.4,-0.1 -2,-0.1c-1.4,0 -2.8,0.2 -4,0.7c-1.3,0.5 -2.4,1.1 -3.5,2l0,-0.4c0,-0.6 -0.2,-1.1 -0.7,-1.6c-0.4,-0.4 -1,-0.7 -1.6,-0.7c-0.6,0 -1.2,0.2 -1.6,0.7c-0.4,0.4 -0.7,1 -0.7,1.6l0,19.6c0,0.6 0.2,1.1 0.7,1.6c0.4,0.4 1,0.7 1.6,0.7c0.6,0 1.1,-0.2 1.6,-0.7c0.4,-0.4 0.7,-1 0.7,-1.6l0,-9.8c0,-1 0.2,-2 0.6,-2.9c0.4,-0.9 0.9,-1.7 1.6,-2.4c0.7,-0.7 1.5,-1.2 2.4,-1.6c0.9,-0.4 1.9,-0.6 2.9,-0.6c1.1,0 2,0.2 2.9,0.5c0.4,0.2 0.7,0.2 0.9,0.2c0.3,0 0.6,-0.1 0.9,-0.2c0.3,-0.1 0.5,-0.3 0.7,-0.5c0.2,-0.2 0.4,-0.4 0.5,-0.7c0.1,-0.3 0.2,-0.6 0.2,-0.9c0,-0.6 -0.2,-1.1 -0.6,-1.5zm-38.8,8.3c0.2,-0.8 0.6,-1.5 1.1,-2.2c0.5,-0.7 1,-1.2 1.7,-1.7c0.6,-0.5 1.4,-0.8 2.1,-1.1c0.8,-0.3 1.6,-0.4 2.4,-0.4c0.8,0 1.6,0.1 2.4,0.4c0.8,0.3 1.5,0.6 2.1,1.1c0.6,0.5 1.2,1 1.7,1.7c0.5,0.7 0.8,1.4 1.1,2.2l-14.6,0zm15.8,-6.2c-2.4,-2.3 -5.2,-3.5 -8.5,-3.5c-3.3,0 -6.2,1.2 -8.5,3.5c-2.3,2.3 -3.5,5.2 -3.5,8.5s1.2,6.2 3.5,8.5c2.3,2.3 5.2,3.5 8.5,3.5c3,0 5.6,-1 7.9,-2.9c0.4,-0.4 0.6,-1 0.6,-1.6c0,-0.6 -0.2,-1.2 -0.6,-1.6c-0.4,-0.4 -1,-0.6 -1.6,-0.6c-0.6,0 -1.1,0.2 -1.5,0.6c-0.7,0.6 -1.4,1 -2.2,1.3c-0.8,0.3 -1.7,0.4 -2.6,0.4c-0.8,0 -1.6,-0.1 -2.4,-0.4c-0.8,-0.3 -1.5,-0.6 -2.1,-1.1c-0.6,-0.5 -1.2,-1 -1.7,-1.7c-0.5,-0.7 -0.8,-1.4 -1.1,-2.2l17,0c0.6,0 1.2,-0.2 1.6,-0.6c0.4,-0.4 0.7,-1 0.7,-1.6c0,-1.7 -0.3,-3.2 -0.9,-4.6c-0.6,-1.5 -1.5,-2.8 -2.6,-3.9zm-22.6,-1.3c0,-0.3 -0.1,-0.6 -0.2,-0.9c-0.1,-0.3 -0.3,-0.5 -0.5,-0.7c-0.2,-0.2 -0.4,-0.4 -0.7,-0.5c-0.3,-0.1 -0.6,-0.2 -0.9,-0.2c-0.4,0 -0.8,0.1 -1.2,0.3l-12.8,8.4l0,-16.7c0,-0.6 -0.2,-1.2 -0.7,-1.6c-0.4,-0.4 -1,-0.7 -1.6,-0.7c-0.6,0 -1.2,0.2 -1.6,0.7c-0.4,0.4 -0.7,1 -0.7,1.6l0,29.9c0,0.6 0.2,1.1 0.7,1.6c0.4,0.4 1,0.7 1.6,0.7c0.6,0 1.1,-0.2 1.6,-0.7c0.4,-0.4 0.7,-1 0.7,-1.6l0,-7.8l2.6,-1.7l9.9,11.2c0.4,0.4 0.9,0.6 1.5,0.6c0.3,0 0.6,-0.1 0.9,-0.2c0.3,-0.1 0.5,-0.3 0.7,-0.5c0.2,-0.2 0.4,-0.4 0.5,-0.7c0.1,-0.3 0.2,-0.6 0.2,-0.9c0,-0.6 -0.2,-1.1 -0.6,-1.6l-9.2,-10.4l9,-5.8c0.5,-0.4 0.8,-1 0.8,-1.8zm-36.9,4.4c0.7,-0.7 1.5,-1.2 2.4,-1.6c0.9,-0.4 1.9,-0.6 2.9,-0.6c0.9,0 1.8,0.2 2.6,0.5c0.8,0.3 1.6,0.8 2.3,1.4c0.4,0.3 0.9,0.5 1.5,0.5c0.6,0 1.2,-0.2 1.6,-0.6c0.4,-0.4 0.6,-1 0.6,-1.6c0,-0.7 -0.3,-1.2 -0.8,-1.7c-2.2,-1.9 -4.8,-2.9 -7.8,-2.9c-3.3,0 -6.2,1.2 -8.5,3.5c-2.3,2.3 -3.5,5.2 -3.5,8.5s1.2,6.2 3.5,8.5c2.3,2.3 5.2,3.5 8.5,3.5c3,0 5.6,-1 7.8,-2.9c0.5,-0.5 0.7,-1 0.7,-1.7c0,-0.6 -0.2,-1.2 -0.6,-1.6c-0.4,-0.4 -1,-0.6 -1.6,-0.6c-0.5,0 -1,0.2 -1.4,0.5c-0.7,0.6 -1.5,1.1 -2.3,1.4c-0.8,0.3 -1.7,0.5 -2.6,0.5c-1,0 -2,-0.2 -2.9,-0.6c-0.9,-0.4 -1.7,-0.9 -2.4,-1.6c-0.7,-0.7 -1.2,-1.5 -1.6,-2.4c-0.4,-0.9 -0.6,-1.9 -0.6,-2.9c0,-1 0.2,-2 0.6,-2.9c0.4,-1.1 0.9,-1.9 1.6,-2.6zm-13.9,8.3c-0.4,0.9 -0.9,1.7 -1.6,2.4c-0.7,0.7 -1.5,1.2 -2.4,1.6c-0.9,0.4 -1.9,0.6 -2.9,0.6c-1.1,0 -2,-0.2 -3,-0.6c-0.9,-0.4 -1.7,-0.9 -2.4,-1.6c-0.7,-0.7 -1.2,-1.5 -1.6,-2.4c-0.4,-0.9 -0.6,-1.9 -0.6,-2.9s0.2,-2 0.6,-2.9c0.4,-0.9 0.9,-1.7 1.6,-2.4c0.7,-0.7 1.5,-1.2 2.4,-1.6c0.9,-0.4 1.9,-0.6 3,-0.6c1,0 2,0.2 2.9,0.6c0.9,0.4 1.7,0.9 2.4,1.6c0.7,0.7 1.2,1.5 1.6,2.4c0.4,0.9 0.6,1.9 0.6,2.9s-0.2,2 -0.6,2.9zm1.6,-11.4c-2.4,-2.3 -5.2,-3.5 -8.5,-3.5c-3.3,0 -6.2,1.2 -8.5,3.5c-2.4,2.3 -3.5,5.2 -3.5,8.5s1.2,6.2 3.5,8.5c2.3,2.3 5.2,3.5 8.5,3.5c3.3,0 6.1,-1.2 8.5,-3.5c2.3,-2.3 3.5,-5.2 3.5,-8.5c0,-1.7 -0.3,-3.2 -0.9,-4.6s-1.5,-2.8 -2.6,-3.9zm-27.8,11.4c-0.4,0.9 -0.9,1.7 -1.6,2.4c-0.7,0.7 -1.5,1.2 -2.4,1.6c-0.9,0.4 -1.9,0.6 -2.9,0.6c-1.1,0 -2,-0.2 -3,-0.6c-0.9,-0.4 -1.7,-0.9 -2.4,-1.6c-0.7,-0.7 -1.2,-1.5 -1.6,-2.4c-0.4,-0.9 -0.6,-1.9 -0.6,-2.9s0.2,-2 0.6,-2.9c0.4,-0.9 0.9,-1.7 1.6,-2.4c0.7,-0.7 1.5,-1.2 2.4,-1.6c0.9,-0.4 1.9,-0.6 3,-0.6c1,0 2,0.2 2.9,0.6c0.9,0.4 1.7,0.9 2.4,1.6c0.7,0.7 1.2,1.5 1.6,2.4c0.4,0.9 0.6,1.9 0.6,2.9s-0.2,2 -0.6,2.9zm2.8,-25.3c-0.6,0 -1.2,0.2 -1.6,0.6c-0.4,0.4 -0.6,1 -0.6,1.6l0,10.7c-2.2,-1.8 -4.7,-2.6 -7.5,-2.6c-3.3,0 -6.2,1.2 -8.5,3.5c-2.3,2.3 -3.5,5.2 -3.5,8.5s1.2,6.2 3.5,8.5s5.2,3.5 8.5,3.5c3.3,0 6.1,-1.2 8.5,-3.5c2.3,-2.3 3.5,-5.2 3.5,-8.5l0,-20c0,-0.6 -0.2,-1.2 -0.7,-1.6c-0.4,-0.5 -1,-0.7 -1.6,-0.7z" class="st1" id="Fill-1"/>
<path d="m141.521621,34c-0.2,-4.3 -2.4,-7.9 -6.5,-11.1l-1.5,-1l-1,1.5c-2,3 -2.8,7 -2.5,10.6c0.2,2.2 1,4.7 2.5,6.5c-1.1,0.9 -4.7,2.6 -9.5,2.5l-71.5,0c-1.3,7.6 0.9,36.2 34,36.2c24.6,0 44.8,-11 54,-34.2c3,0 11.1,0.5 15,-7c0.1,-0.1 1,-2 1,-2l-1.5,-1c-2.2,-1.5 -8.2,-2.1 -12.5,-1z" class="st2" id="Shape"/>
<path d="m58.621621,32l0,10l11,0l0,-10l-11,0zm-2,-2l15,0l0,14l-15,0l0,-14z" class="st1" id="Rectangle-38-Copy"/>
<path d="m78.321621,65.8c-8.2,2.8 -16.9,3.2 -18.8,3.2c6.1,5.1 17,14.7 35,9.2c-3.3,-0.9 -11,-3.9 -16.2,-12.4z" class="st3" id="Fill-21"/>
<path d="m77.421621,64c-3.9,2.2 -8.5,3.5 -13.3,3.5c-2.4,0 -4.8,-0.3 -7.1,-0.9c1.4,2.4 1.4,2.4 1.4,2.4c1.8,0.4 3.7,0.5 5.6,0.5c5.2,0 10,-1.4 14.3,-3.8c-0.3,-0.5 -0.6,-1.1 -0.9,-1.7z" class="st4" id="Shape_1_"/>
<ellipse ry="3.5" rx="3.5" cy="59.2" cx="85.021621" class="st3" id="Oval"/>
<path id="svg_1" d="m87.021621,59.1c-0.1,0.1 -0.3,0.1 -0.5,0.1c-0.6,0 -1,-0.5 -1,-1c0,-0.3 0.2,-0.6 0.4,-0.8c-0.3,-0.1 -0.6,-0.2 -0.9,-0.2c-1.1,0 -2,0.9 -2,2s0.9,2 2,2c1.1,0 2,-0.9 2,-2c0,0 0,-0.1 0,-0.1z" class="st5"/>
<path d="m154.121621,34.9c-2.2,-1.5 -8.4,-2.2 -12.5,-1l-0.9,1.3c-13.4,27.7 -30.2,39.5 -53.1,40c-25,-0.1 -31.2,-13.2 -33,-31.2l-4,0c-0.5,7.6 1.4,35.3 34.8,35.3c24.6,0 44.8,-11 54.2,-34.2c3.1,0.1 11.6,0.1 15.3,-7.6l0.8,-1.6l-1.6,-1z" class="st6" id="Shape_2_"/>
<path d="m154.621621,34.1l2.2,1.5l-0.4,0.8c-0.1,0.2 -0.1,0.2 -0.2,0.3c-0.1,0.2 -0.2,0.5 -0.3,0.7c-0.3,0.6 -0.4,0.9 -0.5,1.1c-1.7,3.3 -4.3,5.4 -7.5,6.6c-2.4,0.9 -4.6,1.1 -7.7,1c-9.2,22.3 -28.9,34.2 -54.7,34.2c-15.1,0 -25.2,-5.8 -30.8,-15.8c-2.1,-3.8 -3.5,-8.1 -4.1,-12.5c-0.5,-3.5 -0.5,-6.8 -0.1,-9.2l0.1,-0.8l0.8,0l71.5,0c3.2,0.1 6.2,-0.8 8,-1.8c-1.1,-1.7 -1.8,-3.9 -2.1,-6.2c-0.3,-4 0.6,-8.1 2.7,-11.2l1.6,-2.3l2.5,1.5c4,3.1 6.2,6.6 6.8,10.7c4.3,-0.8 9.8,-0.2 12.2,1.4zm-1.1,1.7c-2.1,-1.4 -7.8,-1.9 -11.7,-0.9l-1.2,0.3l-0.1,-1.2c-0.2,-3.9 -2.2,-7.3 -6.1,-10.3l-0.6,-0.4l-0.4,0.7c-1.8,2.7 -2.6,6.4 -2.3,9.9c0.2,2.3 1,4.5 2.3,6l0.7,0.8l-0.8,0.6c-2,1.5 -5.9,2.8 -10.1,2.7l-70.8,0c-0.2,2.1 -0.2,4.8 0.2,7.7c0.6,4.1 1.9,8.2 3.9,11.8c5.2,9.3 14.6,14.8 29,14.8c25.2,0 44.3,-11.7 53.1,-33.6l0.3,-0.6l0.7,0c0.3,0 0.5,0 0.5,0c3,0.1 5,-0.1 7.1,-0.9c2.8,-1 4.9,-2.8 6.4,-5.6c0.1,-0.2 0.1,-0.2 0.2,-0.3c0.1,-0.2 0.2,-0.4 0.3,-0.6c0,-0.1 0.1,-0.2 0.1,-0.2l-0.7,-0.7z" class="st4" id="Shape_3_"/>
<rect height="6" width="1" class="st1" y="34" x="60.621621" id="Rectangle-5"/>
<rect height="6" width="1" class="st1" y="34" x="63.621621" id="Rectangle-5_1_"/>
<rect height="6" width="1" class="st1" y="34" x="66.621621" id="Rectangle-5_2_"/>
<path d="m71.621621,32l0,10l11,0l0,-10l-11,0zm-2,-2l15,0l0,14l-15,0l0,-14z" class="st1" id="Rectangle-38-Copy_1_"/>
<rect height="6" width="1" class="st1" y="34" x="73.621621" id="Rectangle-5_3_"/>
<rect height="6" width="1" class="st1" y="34" x="76.621621" id="Rectangle-5_4_"/>
<rect height="6" width="1" class="st1" y="34" x="79.621621" id="Rectangle-5_5_"/>
<path d="m84.621621,32l0,10l11,0l0,-10l-11,0zm-2,-2l15,0l0,14l-15,0l0,-14z" class="st1" id="Rectangle-38-Copy_2_"/>
<rect height="6" width="1" class="st1" y="34" x="86.621621" id="Rectangle-5_6_"/>
<rect height="6" width="1" class="st1" y="34" x="89.621621" id="Rectangle-5_7_"/>
<rect height="6" width="1" class="st1" y="34" x="92.621621" id="Rectangle-5_8_"/>
<path d="m97.621621,32l0,10l11,0l0,-10l-11,0zm-2,-2l15,0l0,14l-15,0l0,-14z" class="st1" id="Rectangle-38-Copy_3_"/>
<rect height="6" width="1" class="st1" y="34" x="99.621621" id="Rectangle-5_9_"/>
<rect height="6" width="1" class="st1" y="34" x="102.621621" id="Rectangle-5_10_"/>
<rect height="6" width="1" class="st1" y="34" x="105.621621" id="Rectangle-5_11_"/>
<path d="m110.621621,32l0,10l11,0l0,-10l-11,0zm-2,-2l15,0l0,14l-15,0l0,-14z" class="st1" id="Rectangle-38-Copy_4_"/>
<rect height="6" width="1" class="st1" y="34" x="112.621621" id="Rectangle-5_12_"/>
<rect height="6" width="1" class="st1" y="34" x="115.621621" id="Rectangle-5_13_"/>
<rect height="6" width="1" class="st1" y="34" x="118.621621" id="Rectangle-5_14_"/>
<path d="m71.621621,20l0,10l11,0l0,-10l-11,0zm-2,-2l15,0l0,14l-15,0l0,-14z" class="st1" id="Rectangle-38-Copy_5_"/>
<rect height="6" width="1" class="st1" y="22" x="76.621621" id="Rectangle-5_15_"/>
<rect height="6" width="1" class="st1" y="22" x="79.621621" id="Rectangle-5_16_"/>
<path d="m84.621621,20l0,10l11,0l0,-10l-11,0zm-2,-2l15,0l0,14l-15,0l0,-14z" class="st1" id="Rectangle-38-Copy_6_"/>
<rect height="6" width="1" class="st1" y="22" x="86.621621" id="Rectangle-5_17_"/>
<rect height="6" width="1" class="st1" y="22" x="89.621621" id="Rectangle-5_18_"/>
<rect height="6" width="1" class="st1" y="22" x="92.621621" id="Rectangle-5_19_"/>
<path d="m97.621621,20l0,10l11,0l0,-10l-11,0zm-2,-2l15,0l0,14l-15,0l0,-14z" class="st1" id="Rectangle-38-Copy_7_"/>
<rect height="6" width="1" class="st1" y="22" x="99.621621" id="Rectangle-5_20_"/>
<rect height="6" width="1" class="st1" y="22" x="102.621621" id="Rectangle-5_21_"/>
<rect height="6" width="1" class="st1" y="22" x="105.621621" id="Rectangle-5_22_"/>
<path d="m97.621621,8l0,10l11,0l0,-10l-11,0zm-2,-2l15,0l0,14l-15,0l0,-14z" class="st1" id="Rectangle-38-Copy_8_"/>
<rect height="6" width="1" class="st1" y="10" x="102.621621" id="Rectangle-5_23_"/>
<rect height="6" width="1" class="st1" y="10" x="99.621621" id="Rectangle-5_24_"/>
<rect height="6" width="1" class="st1" y="10" x="105.621621" id="Rectangle-5_25_"/>
<rect height="6" width="1" class="st1" y="22" x="73.621621" id="Rectangle-5_26_"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -0,0 +1,82 @@
/* Space out content a bit */
body {
padding-top: 1.5rem;
padding-bottom: 1.5rem;
}
/* Everything but the jumbotron gets side spacing for mobile first views */
.header,
.marketing,
.footer {
padding-right: 1rem;
padding-left: 1rem;
}
/* Custom page header */
.header {
padding-bottom: 1rem;
border-bottom: .05rem solid #e5e5e5;
}
/* Make the masthead heading the same height as the navigation */
.header h3 {
margin-top: 0;
margin-bottom: 0;
line-height: 3rem;
}
/* Custom page footer */
.footer {
padding-top: 1.5rem;
color: #777;
border-top: .05rem solid #e5e5e5;
}
/* Customize container */
@media (min-width: 48em) {
.container {
max-width: 46rem;
}
}
.container-narrow > hr {
margin: 2rem 0;
}
/* Main marketing message and sign up button */
.jumbotron {
text-align: center;
border-bottom: .05rem solid #e5e5e5;
}
.jumbotron .btn {
padding: .75rem 1.5rem;
font-size: 1.5rem;
}
.btn.dropdown-toggle, .dropdown-menu a {
cursor: pointer;
}
/* Supporting marketing content */
.marketing {
margin: 3rem 0;
}
.marketing p + h4 {
margin-top: 1.5rem;
}
/* Responsive: Portrait tablets and up */
@media screen and (min-width: 48em) {
/* Remove the padding we set earlier */
.header,
.marketing,
.footer {
padding-right: 0;
padding-left: 0;
}
/* Space out the masthead */
.header {
margin-bottom: 2rem;
}
/* Remove the bottom border on the jumbotron for visual effect */
.jumbotron {
border-bottom: 0;
}
}

11
handlers/www/assets/package-lock.json generated Normal file
View File

@@ -0,0 +1,11 @@
{
"requires": true,
"lockfileVersion": 1,
"dependencies": {
"xterm": {
"version": "3.14.5",
"resolved": "https://registry.npmjs.org/xterm/-/xterm-3.14.5.tgz",
"integrity": "sha512-DVmQ8jlEtL+WbBKUZuMxHMBgK/yeIZwkXB81bH+MGaKKnJGYwA+770hzhXPfwEIokK9On9YIFPRleVp/5G7z9g=="
}
}
}

View File

@@ -0,0 +1,3 @@
Terminal.applyAddon(fit);
Terminal.applyAddon(fullscreen);

View File

@@ -0,0 +1,26 @@
<svg><defs>
<g id="cake"><path d="M12 6c1.11 0 2-.9 2-2 0-.38-.1-.73-.29-1.03L12 0l-1.71 2.97c-.19.3-.29.65-.29 1.03 0 1.1.9 2 2 2zm4.6 9.99l-1.07-1.07-1.08 1.07c-1.3 1.3-3.58 1.31-4.89 0l-1.07-1.07-1.09 1.07C6.75 16.64 5.88 17 4.96 17c-.73 0-1.4-.23-1.96-.61V21c0 .55.45 1 1 1h16c.55 0 1-.45 1-1v-4.61c-.56.38-1.23.61-1.96.61-.92 0-1.79-.36-2.44-1.01zM18 9h-5V7h-2v2H6c-1.66 0-3 1.34-3 3v1.54c0 1.08.88 1.96 1.96 1.96.52 0 1.02-.2 1.38-.57l2.14-2.13 2.13 2.13c.74.74 2.03.74 2.77 0l2.14-2.13 2.13 2.13c.37.37.86.57 1.38.57 1.08 0 1.96-.88 1.96-1.96V12C21 10.34 19.66 9 18 9z"/></g>
<g id="domain"><path d="M12 7V3H2v18h20V7H12zM6 19H4v-2h2v2zm0-4H4v-2h2v2zm0-4H4V9h2v2zm0-4H4V5h2v2zm4 12H8v-2h2v2zm0-4H8v-2h2v2zm0-4H8V9h2v2zm0-4H8V5h2v2zm10 12h-8v-2h2v-2h-2v-2h2v-2h-2V9h8v10zm-2-8h-2v2h2v-2zm0 4h-2v2h2v-2z"/></g>
<g id="group"><path d="M16 11c1.66 0 2.99-1.34 2.99-3S17.66 5 16 5c-1.66 0-3 1.34-3 3s1.34 3 3 3zm-8 0c1.66 0 2.99-1.34 2.99-3S9.66 5 8 5C6.34 5 5 6.34 5 8s1.34 3 3 3zm0 2c-2.33 0-7 1.17-7 3.5V19h14v-2.5c0-2.33-4.67-3.5-7-3.5zm8 0c-.29 0-.62.02-.97.05 1.16.84 1.97 1.97 1.97 3.45V19h6v-2.5c0-2.33-4.67-3.5-7-3.5z"/></g>
<g id="group-add"><path d="M8 10H5V7H3v3H0v2h3v3h2v-3h3v-2zm10 1c1.66 0 2.99-1.34 2.99-3S19.66 5 18 5c-.32 0-.63.05-.91.14.57.81.9 1.79.9 2.86s-.34 2.04-.9 2.86c.28.09.59.14.91.14zm-5 0c1.66 0 2.99-1.34 2.99-3S14.66 5 13 5c-1.66 0-3 1.34-3 3s1.34 3 3 3zm6.62 2.16c.83.73 1.38 1.66 1.38 2.84v2h3v-2c0-1.54-2.37-2.49-4.38-2.84zM13 13c-2 0-6 1-6 3v2h12v-2c0-2-4-3-6-3z"/></g>
<g id="location-city"><path d="M15 11V5l-3-3-3 3v2H3v14h18V11h-6zm-8 8H5v-2h2v2zm0-4H5v-2h2v2zm0-4H5V9h2v2zm6 8h-2v-2h2v2zm0-4h-2v-2h2v2zm0-4h-2V9h2v2zm0-4h-2V5h2v2zm6 12h-2v-2h2v2zm0-4h-2v-2h2v2z"/></g>
<g id="mood"><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm3.5-9c.83 0 1.5-.67 1.5-1.5S16.33 8 15.5 8 14 8.67 14 9.5s.67 1.5 1.5 1.5zm-7 0c.83 0 1.5-.67 1.5-1.5S9.33 8 8.5 8 7 8.67 7 9.5 7.67 11 8.5 11zm3.5 6.5c2.33 0 4.31-1.46 5.11-3.5H6.89c.8 2.04 2.78 3.5 5.11 3.5z"/></g>
<g id="notifications"><path d="M11.5 22c1.1 0 2-.9 2-2h-4c0 1.1.9 2 2 2zm6.5-6v-5.5c0-3.07-2.13-5.64-5-6.32V3.5c0-.83-.67-1.5-1.5-1.5S10 2.67 10 3.5v.68c-2.87.68-5 3.25-5 6.32V16l-2 2v1h17v-1l-2-2z"/></g>
<g id="notifications-none"><path d="M11.5 22c1.1 0 2-.9 2-2h-4c0 1.1.9 2 2 2zm6.5-6v-5.5c0-3.07-2.13-5.64-5-6.32V3.5c0-.83-.67-1.5-1.5-1.5S10 2.67 10 3.5v.68c-2.87.68-5 3.25-5 6.32V16l-2 2v1h17v-1l-2-2zm-2 1H7v-6.5C7 8.01 9.01 6 11.5 6S16 8.01 16 10.5V17z"/></g>
<g id="notifications-off"><path d="M11.5 22c1.1 0 2-.9 2-2h-4c0 1.1.9 2 2 2zM18 10.5c0-3.07-2.13-5.64-5-6.32V3.5c0-.83-.67-1.5-1.5-1.5S10 2.67 10 3.5v.68c-.51.12-.99.32-1.45.56L18 14.18V10.5zm-.27 8.5l2 2L21 19.73 4.27 3 3 4.27l2.92 2.92C5.34 8.16 5 9.29 5 10.5V16l-2 2v1h14.73z"/></g>
<g id="notifications-on"><path d="M6.58 3.58L5.15 2.15C2.76 3.97 1.18 6.8 1.03 10h2c.15-2.65 1.51-4.97 3.55-6.42zM19.97 10h2c-.15-3.2-1.73-6.03-4.13-7.85l-1.43 1.43c2.05 1.45 3.41 3.77 3.56 6.42zm-1.97.5c0-3.07-2.13-5.64-5-6.32V3.5c0-.83-.67-1.5-1.5-1.5S10 2.67 10 3.5v.68c-2.87.68-5 3.25-5 6.32V16l-2 2v1h17v-1l-2-2v-5.5zM11.5 22c.14 0 .27-.01.4-.04.65-.13 1.19-.58 1.44-1.18.1-.24.16-.5.16-.78h-4c0 1.1.9 2 2 2z"/></g>
<g id="notifications-paused"><path d="M11.5 22c1.1 0 2-.9 2-2h-4c0 1.1.9 2 2 2zm6.5-6v-5.5c0-3.07-2.13-5.64-5-6.32V3.5c0-.83-.67-1.5-1.5-1.5S10 2.67 10 3.5v.68c-2.87.68-5 3.25-5 6.32V16l-2 2v1h17v-1l-2-2zm-4-6.2l-2.8 3.4H14V15H9v-1.8l2.8-3.4H9V8h5v1.8z"/></g>
<g id="pages"><path d="M3 5v6h5L7 7l4 1V3H5c-1.1 0-2 .9-2 2zm5 8H3v6c0 1.1.9 2 2 2h6v-5l-4 1 1-4zm9 4l-4-1v5h6c1.1 0 2-.9 2-2v-6h-5l1 4zm2-14h-6v5l4-1-1 4h5V5c0-1.1-.9-2-2-2z"/></g>
<g id="party-mode"><path d="M20 4h-3.17L15 2H9L7.17 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-8 3c1.63 0 3.06.79 3.98 2H12c-1.66 0-3 1.34-3 3 0 .35.07.69.18 1H7.1c-.06-.32-.1-.66-.1-1 0-2.76 2.24-5 5-5zm0 10c-1.63 0-3.06-.79-3.98-2H12c1.66 0 3-1.34 3-3 0-.35-.07-.69-.18-1h2.08c.07.32.1.66.1 1 0 2.76-2.24 5-5 5z"/></g>
<g id="people"><path d="M16 11c1.66 0 2.99-1.34 2.99-3S17.66 5 16 5c-1.66 0-3 1.34-3 3s1.34 3 3 3zm-8 0c1.66 0 2.99-1.34 2.99-3S9.66 5 8 5C6.34 5 5 6.34 5 8s1.34 3 3 3zm0 2c-2.33 0-7 1.17-7 3.5V19h14v-2.5c0-2.33-4.67-3.5-7-3.5zm8 0c-.29 0-.62.02-.97.05 1.16.84 1.97 1.97 1.97 3.45V19h6v-2.5c0-2.33-4.67-3.5-7-3.5z"/></g>
<g id="people-outline"><path d="M16.5 13c-1.2 0-3.07.34-4.5 1-1.43-.67-3.3-1-4.5-1C5.33 13 1 14.08 1 16.25V19h22v-2.75c0-2.17-4.33-3.25-6.5-3.25zm-4 4.5h-10v-1.25c0-.54 2.56-1.75 5-1.75s5 1.21 5 1.75v1.25zm9 0H14v-1.25c0-.46-.2-.86-.52-1.22.88-.3 1.96-.53 3.02-.53 2.44 0 5 1.21 5 1.75v1.25zM7.5 12c1.93 0 3.5-1.57 3.5-3.5S9.43 5 7.5 5 4 6.57 4 8.5 5.57 12 7.5 12zm0-5.5c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2zm9 5.5c1.93 0 3.5-1.57 3.5-3.5S18.43 5 16.5 5 13 6.57 13 8.5s1.57 3.5 3.5 3.5zm0-5.5c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2z"/></g>
<g id="person"><path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/></g>
<g id="person-add"><path d="M15 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm-9-2V7H4v3H1v2h3v3h2v-3h3v-2H6zm9 4c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/></g>
<g id="person-outline"><path d="M12 5.9c1.16 0 2.1.94 2.1 2.1s-.94 2.1-2.1 2.1S9.9 9.16 9.9 8s.94-2.1 2.1-2.1m0 9c2.97 0 6.1 1.46 6.1 2.1v1.1H5.9V17c0-.64 3.13-2.1 6.1-2.1M12 4C9.79 4 8 5.79 8 8s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm0 9c-2.67 0-8 1.34-8 4v3h16v-3c0-2.66-5.33-4-8-4z"/></g>
<g id="plus-one"><path d="M10 8H8v4H4v2h4v4h2v-4h4v-2h-4zm4.5-1.92V7.9l2.5-.5V18h2V5z"/></g>
<g id="poll"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM9 17H7v-7h2v7zm4 0h-2V7h2v10zm4 0h-2v-4h2v4z"/></g>
<g id="public"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z"/></g>
<g id="school"><path d="M5 13.18v4L12 21l7-3.82v-4L12 17l-7-3.82zM12 3L1 9l11 6 9-4.91V17h2V9L12 3z"/></g>
<g id="share"><path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81 1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9c-1.66 0-3 1.34-3 3s1.34 3 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.16c-.05.21-.08.43-.08.65 0 1.61 1.31 2.92 2.92 2.92 1.61 0 2.92-1.31 2.92-2.92s-1.31-2.92-2.92-2.92z"/></g>
<g id="whatshot"><path d="M13.5.67s.74 2.65.74 4.8c0 2.06-1.35 3.73-3.41 3.73-2.07 0-3.63-1.67-3.63-3.73l.03-.36C5.21 7.51 4 10.62 4 14c0 4.42 3.58 8 8 8s8-3.58 8-8C20 8.61 17.41 3.8 13.5.67zM11.71 19c-1.78 0-3.22-1.4-3.22-3.14 0-1.62 1.05-2.76 2.81-3.12 1.77-.36 3.6-1.21 4.62-2.58.39 1.29.59 2.65.59 4.04 0 2.65-2.15 4.8-4.8 4.8z"/></g>
</defs></svg>

After

Width:  |  Height:  |  Size: 6.8 KiB

View File

@@ -0,0 +1,99 @@
@import url('https://fonts.googleapis.com/css?family=Rationale');
.selected button {
background-color: rgba(158,158,158,0.2);
}
.terminal-container {
background-color: #000;
padding: 0;
display: flex;
align-items: stretch;
justify-content: stretch;
flex: 1;
}
.terminal-instance{
width: 100%;
}
.clock {
font-family: 'Rationale', sans-serif;
font-size: 3.0em;
color: #1da4eb;
text-align: center;
}
.welcome {
background-color: #e7e7e7;
}
.welcome > div {
text-align: center;
}
.welcome > div > img {
max-width: 100%;
}
.g-recaptcha div {
margin-left: auto;
margin-right: auto;
margin-bottom: auto;
margin-top: 50px;
}
.uploadStatus .bottom-block {
display: block;
position: relative;
background-color: rgba(255, 235, 169, 0.25);
height: 30px;
width: 100%;
}
.uploadStatus .bottom-block > span {
display: inline-block;
padding: 8px;
font-size: 0.9em;
}
.uploadStatus {
display: block;
position: relative;
width: 100%;
border: 2px solid #aad1f9;
transition: opacity 0.1s linear;
border-top: 0px;
}
.disconnected {
background-color: #FDF4B6;
}
md-input-container {
margin-bottom: 0;
}
md-input-container .md-errors-spacer {
height: 0;
min-height: 0;
}
.stats {
min-height: 230px;
}
::-webkit-scrollbar {
-webkit-appearance: none;
width: 7px;
}
::-webkit-scrollbar-thumb {
border-radius: 4px;
background-color: rgba(0,0,0,.5);
-webkit-box-shadow: 0 0 1px rgba(255,255,255,.5);
}
.md-mini {
min-width: 24px;
}
.dragover {
opacity: 0.5;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

View File

@@ -0,0 +1,106 @@
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.attach = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
function attach(term, socket, bidirectional, buffered) {
var addonTerminal = term;
bidirectional = (typeof bidirectional === 'undefined') ? true : bidirectional;
addonTerminal.__socket = socket;
addonTerminal.__flushBuffer = function () {
addonTerminal.write(addonTerminal.__attachSocketBuffer);
addonTerminal.__attachSocketBuffer = null;
};
addonTerminal.__pushToBuffer = function (data) {
if (addonTerminal.__attachSocketBuffer) {
addonTerminal.__attachSocketBuffer += data;
}
else {
addonTerminal.__attachSocketBuffer = data;
setTimeout(addonTerminal.__flushBuffer, 10);
}
};
var myTextDecoder;
addonTerminal.__getMessage = function (ev) {
var str;
if (typeof ev.data === 'object') {
if (!myTextDecoder) {
myTextDecoder = new TextDecoder();
}
if (ev.data instanceof ArrayBuffer) {
str = myTextDecoder.decode(ev.data);
displayData(str);
}
else {
var fileReader_1 = new FileReader();
fileReader_1.addEventListener('load', function () {
str = myTextDecoder.decode(fileReader_1.result);
displayData(str);
});
fileReader_1.readAsArrayBuffer(ev.data);
}
}
else if (typeof ev.data === 'string') {
displayData(ev.data);
}
else {
throw Error("Cannot handle \"" + typeof ev.data + "\" websocket message.");
}
};
function displayData(str, data) {
if (buffered) {
addonTerminal.__pushToBuffer(str || data);
}
else {
addonTerminal.write(str || data);
}
}
addonTerminal.__sendData = function (data) {
if (socket.readyState !== 1) {
return;
}
socket.send(data);
};
addonTerminal._core.register(addSocketListener(socket, 'message', addonTerminal.__getMessage));
if (bidirectional) {
addonTerminal.__dataListener = addonTerminal.onData(addonTerminal.__sendData);
addonTerminal._core.register(addonTerminal.__dataListener);
}
addonTerminal._core.register(addSocketListener(socket, 'close', function () { return detach(addonTerminal, socket); }));
addonTerminal._core.register(addSocketListener(socket, 'error', function () { return detach(addonTerminal, socket); }));
}
exports.attach = attach;
function addSocketListener(socket, type, handler) {
socket.addEventListener(type, handler);
return {
dispose: function () {
if (!handler) {
return;
}
socket.removeEventListener(type, handler);
handler = null;
}
};
}
function detach(term, socket) {
var addonTerminal = term;
addonTerminal.__dataListener.dispose();
addonTerminal.__dataListener = undefined;
socket = (typeof socket === 'undefined') ? addonTerminal.__socket : socket;
if (socket) {
socket.removeEventListener('message', addonTerminal.__getMessage);
}
delete addonTerminal.__socket;
}
exports.detach = detach;
function apply(terminalConstructor) {
terminalConstructor.prototype.attach = function (socket, bidirectional, buffered) {
attach(this, socket, bidirectional, buffered);
};
terminalConstructor.prototype.detach = function (socket) {
detach(this, socket);
};
}
exports.apply = apply;
},{}]},{},[1])(1)
});
//# sourceMappingURL=attach.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,51 @@
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.fit = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
function proposeGeometry(term) {
if (!term.element.parentElement) {
return null;
}
var parentElementStyle = window.getComputedStyle(term.element.parentElement);
var parentElementHeight = parseInt(parentElementStyle.getPropertyValue('height'));
var parentElementWidth = Math.max(0, parseInt(parentElementStyle.getPropertyValue('width')));
var elementStyle = window.getComputedStyle(term.element);
var elementPadding = {
top: parseInt(elementStyle.getPropertyValue('padding-top')),
bottom: parseInt(elementStyle.getPropertyValue('padding-bottom')),
right: parseInt(elementStyle.getPropertyValue('padding-right')),
left: parseInt(elementStyle.getPropertyValue('padding-left'))
};
var elementPaddingVer = elementPadding.top + elementPadding.bottom;
var elementPaddingHor = elementPadding.right + elementPadding.left;
var availableHeight = parentElementHeight - elementPaddingVer;
var availableWidth = parentElementWidth - elementPaddingHor - term._core.viewport.scrollBarWidth;
var geometry = {
cols: Math.floor(availableWidth / term._core._renderCoordinator.dimensions.actualCellWidth),
rows: Math.floor(availableHeight / term._core._renderCoordinator.dimensions.actualCellHeight)
};
return geometry;
}
exports.proposeGeometry = proposeGeometry;
function fit(term) {
var geometry = proposeGeometry(term);
if (geometry) {
if (term.rows !== geometry.rows || term.cols !== geometry.cols) {
term._core._renderCoordinator.clear();
term.resize(geometry.cols, geometry.rows);
}
}
}
exports.fit = fit;
function apply(terminalConstructor) {
terminalConstructor.prototype.proposeGeometry = function () {
return proposeGeometry(this);
};
terminalConstructor.prototype.fit = function () {
fit(this);
};
}
exports.apply = apply;
},{}]},{},[1])(1)
});
//# sourceMappingURL=fit.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"fit.js","sources":["../../../src/addons/fit/fit.ts","../../../node_modules/browser-pack/_prelude.js"],"sourcesContent":["/**\n * Copyright (c) 2014 The xterm.js authors. All rights reserved.\n * @license MIT\n *\n * Fit terminal columns and rows to the dimensions of its DOM element.\n *\n * ## Approach\n *\n * Rows: Truncate the division of the terminal parent element height by the\n * terminal row height.\n * Columns: Truncate the division of the terminal parent element width by the\n * terminal character width (apply display: inline at the terminal\n * row and truncate its width with the current number of columns).\n */\n\nimport { Terminal } from 'xterm';\n\nexport interface IGeometry {\n rows: number;\n cols: number;\n}\n\nexport function proposeGeometry(term: Terminal): IGeometry {\n if (!term.element.parentElement) {\n return null;\n }\n const parentElementStyle = window.getComputedStyle(term.element.parentElement);\n const parentElementHeight = parseInt(parentElementStyle.getPropertyValue('height'));\n const parentElementWidth = Math.max(0, parseInt(parentElementStyle.getPropertyValue('width')));\n const elementStyle = window.getComputedStyle(term.element);\n const elementPadding = {\n top: parseInt(elementStyle.getPropertyValue('padding-top')),\n bottom: parseInt(elementStyle.getPropertyValue('padding-bottom')),\n right: parseInt(elementStyle.getPropertyValue('padding-right')),\n left: parseInt(elementStyle.getPropertyValue('padding-left'))\n };\n const elementPaddingVer = elementPadding.top + elementPadding.bottom;\n const elementPaddingHor = elementPadding.right + elementPadding.left;\n const availableHeight = parentElementHeight - elementPaddingVer;\n const availableWidth = parentElementWidth - elementPaddingHor - (<any>term)._core.viewport.scrollBarWidth;\n const geometry = {\n cols: Math.floor(availableWidth / (<any>term)._core._renderCoordinator.dimensions.actualCellWidth),\n rows: Math.floor(availableHeight / (<any>term)._core._renderCoordinator.dimensions.actualCellHeight)\n };\n return geometry;\n}\n\nexport function fit(term: Terminal): void {\n const geometry = proposeGeometry(term);\n if (geometry) {\n // Force a full render\n if (term.rows !== geometry.rows || term.cols !== geometry.cols) {\n (<any>term)._core._renderCoordinator.clear();\n term.resize(geometry.cols, geometry.rows);\n }\n }\n}\n\nexport function apply(terminalConstructor: typeof Terminal): void {\n (<any>terminalConstructor.prototype).proposeGeometry = function (): IGeometry {\n return proposeGeometry(this);\n };\n\n (<any>terminalConstructor.prototype).fit = function (): void {\n fit(this);\n };\n}\n",null],"names":[],"mappings":"ACAA;;;ADsBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAvBA;AAyBA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AATA;AAWA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AARA;"}

View File

@@ -0,0 +1,10 @@
.xterm.fullscreen {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
width: auto;
height: auto;
z-index: 255;
}

View File

@@ -0,0 +1,29 @@
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.fullscreen = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
function toggleFullScreen(term, fullscreen) {
var fn;
if (typeof fullscreen === 'undefined') {
fn = (term.element.classList.contains('fullscreen')) ?
term.element.classList.remove : term.element.classList.add;
}
else if (!fullscreen) {
fn = term.element.classList.remove;
}
else {
fn = term.element.classList.add;
}
fn = fn.bind(term.element.classList);
fn('fullscreen');
}
exports.toggleFullScreen = toggleFullScreen;
function apply(terminalConstructor) {
terminalConstructor.prototype.toggleFullScreen = function (fullscreen) {
toggleFullScreen(this, fullscreen);
};
}
exports.apply = apply;
},{}]},{},[1])(1)
});
//# sourceMappingURL=fullscreen.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"fullscreen.js","sources":["../../../src/addons/fullscreen/fullscreen.ts","../../../node_modules/browser-pack/_prelude.js"],"sourcesContent":["/**\n * Copyright (c) 2014 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { Terminal } from 'xterm';\n\n/**\n * Toggle the given terminal's fullscreen mode.\n * @param term The terminal to toggle full screen mode\n * @param fullscreen Toggle fullscreen on (true) or off (false)\n */\nexport function toggleFullScreen(term: Terminal, fullscreen: boolean): void {\n let fn: (...tokens: string[]) => void;\n\n if (typeof fullscreen === 'undefined') {\n fn = (term.element.classList.contains('fullscreen')) ?\n term.element.classList.remove : term.element.classList.add;\n } else if (!fullscreen) {\n fn = term.element.classList.remove;\n } else {\n fn = term.element.classList.add;\n }\n\n fn = fn.bind(term.element.classList);\n fn('fullscreen');\n}\n\nexport function apply(terminalConstructor: typeof Terminal): void {\n (<any>terminalConstructor.prototype).toggleFullScreen = function (fullscreen: boolean): void {\n toggleFullScreen(this, fullscreen);\n };\n}\n",null],"names":[],"mappings":"ACAA;;;ADYA;AACA;AAEA;AACA;AACA;AACA;AAAA;AACA;AACA;AAAA;AACA;AACA;AAEA;AACA;AACA;AAdA;AAgBA;AACA;AACA;AACA;AACA;AAJA;"}

View File

@@ -0,0 +1,262 @@
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.search = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var NON_WORD_CHARACTERS = ' ~!@#$%^&*()+`-=[]{}|\;:"\',./<>?';
var LINES_CACHE_TIME_TO_LIVE = 15 * 1000;
var SearchHelper = (function () {
function SearchHelper(_terminal) {
this._terminal = _terminal;
this._linesCache = null;
this._linesCacheTimeoutId = 0;
this._destroyLinesCache = this._destroyLinesCache.bind(this);
}
SearchHelper.prototype.findNext = function (term, searchOptions) {
var incremental = searchOptions.incremental;
var result;
if (!term || term.length === 0) {
this._terminal.clearSelection();
return false;
}
var startCol = 0;
var startRow = this._terminal.buffer.viewportY;
if (this._terminal.hasSelection()) {
var currentSelection = this._terminal.getSelectionPosition();
startRow = incremental ? currentSelection.startRow : currentSelection.endRow;
startCol = incremental ? currentSelection.startColumn : currentSelection.endColumn;
}
this._initLinesCache();
var findingRow = startRow;
var cumulativeCols = startCol;
while (this._terminal.buffer.getLine(findingRow).isWrapped) {
findingRow--;
cumulativeCols += this._terminal.cols;
}
result = this._findInLine(term, findingRow, cumulativeCols, searchOptions);
if (!result) {
for (var y = startRow + 1; y < this._terminal.buffer.baseY + this._terminal.rows; y++) {
result = this._findInLine(term, y, 0, searchOptions);
if (result) {
break;
}
}
}
if (!result) {
for (var y = 0; y < findingRow; y++) {
result = this._findInLine(term, y, 0, searchOptions);
if (result) {
break;
}
}
}
return this._selectResult(result);
};
SearchHelper.prototype.findPrevious = function (term, searchOptions) {
var result;
if (!term || term.length === 0) {
this._terminal.clearSelection();
return false;
}
var isReverseSearch = true;
var startRow = this._terminal.buffer.viewportY + this._terminal.rows - 1;
var startCol = this._terminal.cols;
if (this._terminal.hasSelection()) {
var currentSelection = this._terminal.getSelectionPosition();
startRow = currentSelection.startRow;
startCol = currentSelection.startColumn;
}
this._initLinesCache();
result = this._findInLine(term, startRow, startCol, searchOptions, isReverseSearch);
if (!result) {
var cumulativeCols = this._terminal.cols;
if (this._terminal.buffer.getLine(startRow).isWrapped) {
cumulativeCols += startCol;
}
for (var y = startRow - 1; y >= 0; y--) {
result = this._findInLine(term, y, cumulativeCols, searchOptions, isReverseSearch);
if (result) {
break;
}
if (this._terminal.buffer.getLine(y).isWrapped) {
cumulativeCols += this._terminal.cols;
}
else {
cumulativeCols = this._terminal.cols;
}
}
}
if (!result) {
var searchFrom = this._terminal.buffer.baseY + this._terminal.rows - 1;
var cumulativeCols = this._terminal.cols;
for (var y = searchFrom; y >= startRow; y--) {
result = this._findInLine(term, y, cumulativeCols, searchOptions, isReverseSearch);
if (result) {
break;
}
if (this._terminal.buffer.getLine(y).isWrapped) {
cumulativeCols += this._terminal.cols;
}
else {
cumulativeCols = this._terminal.cols;
}
}
}
return this._selectResult(result);
};
SearchHelper.prototype._initLinesCache = function () {
var _this = this;
if (!this._linesCache) {
this._linesCache = new Array(this._terminal.buffer.length);
this._cursorMoveListener = this._terminal.onCursorMove(function () { return _this._destroyLinesCache(); });
this._resizeListener = this._terminal.onResize(function () { return _this._destroyLinesCache(); });
}
window.clearTimeout(this._linesCacheTimeoutId);
this._linesCacheTimeoutId = window.setTimeout(function () { return _this._destroyLinesCache(); }, LINES_CACHE_TIME_TO_LIVE);
};
SearchHelper.prototype._destroyLinesCache = function () {
this._linesCache = null;
if (this._cursorMoveListener) {
this._cursorMoveListener.dispose();
this._cursorMoveListener = undefined;
}
if (this._resizeListener) {
this._resizeListener.dispose();
this._resizeListener = undefined;
}
if (this._linesCacheTimeoutId) {
window.clearTimeout(this._linesCacheTimeoutId);
this._linesCacheTimeoutId = 0;
}
};
SearchHelper.prototype._isWholeWord = function (searchIndex, line, term) {
return (((searchIndex === 0) || (NON_WORD_CHARACTERS.indexOf(line[searchIndex - 1]) !== -1)) &&
(((searchIndex + term.length) === line.length) || (NON_WORD_CHARACTERS.indexOf(line[searchIndex + term.length]) !== -1)));
};
SearchHelper.prototype._findInLine = function (term, row, col, searchOptions, isReverseSearch) {
if (searchOptions === void 0) { searchOptions = {}; }
if (isReverseSearch === void 0) { isReverseSearch = false; }
if (this._terminal.buffer.getLine(row).isWrapped) {
return;
}
var stringLine = this._linesCache ? this._linesCache[row] : void 0;
if (stringLine === void 0) {
stringLine = this.translateBufferLineToStringWithWrap(row, true);
if (this._linesCache) {
this._linesCache[row] = stringLine;
}
}
var searchTerm = searchOptions.caseSensitive ? term : term.toLowerCase();
var searchStringLine = searchOptions.caseSensitive ? stringLine : stringLine.toLowerCase();
var resultIndex = -1;
if (searchOptions.regex) {
var searchRegex = RegExp(searchTerm, 'g');
var foundTerm = void 0;
if (isReverseSearch) {
while (foundTerm = searchRegex.exec(searchStringLine.slice(0, col))) {
resultIndex = searchRegex.lastIndex - foundTerm[0].length;
term = foundTerm[0];
searchRegex.lastIndex -= (term.length - 1);
}
}
else {
foundTerm = searchRegex.exec(searchStringLine.slice(col));
if (foundTerm && foundTerm[0].length > 0) {
resultIndex = col + (searchRegex.lastIndex - foundTerm[0].length);
term = foundTerm[0];
}
}
}
else {
if (isReverseSearch) {
if (col - searchTerm.length >= 0) {
resultIndex = searchStringLine.lastIndexOf(searchTerm, col - searchTerm.length);
}
}
else {
resultIndex = searchStringLine.indexOf(searchTerm, col);
}
}
if (resultIndex >= 0) {
if (resultIndex >= this._terminal.cols) {
row += Math.floor(resultIndex / this._terminal.cols);
resultIndex = resultIndex % this._terminal.cols;
}
if (searchOptions.wholeWord && !this._isWholeWord(resultIndex, searchStringLine, term)) {
return;
}
var line = this._terminal.buffer.getLine(row);
for (var i = 0; i < resultIndex; i++) {
var cell = line.getCell(i);
var char = cell.char;
if (char.length > 1) {
resultIndex -= char.length - 1;
}
var charWidth = cell.width;
if (charWidth === 0) {
resultIndex++;
}
}
return {
term: term,
col: resultIndex,
row: row
};
}
};
SearchHelper.prototype.translateBufferLineToStringWithWrap = function (lineIndex, trimRight) {
var lineString = '';
var lineWrapsToNext;
do {
var nextLine = this._terminal.buffer.getLine(lineIndex + 1);
lineWrapsToNext = nextLine ? nextLine.isWrapped : false;
lineString += this._terminal.buffer.getLine(lineIndex).translateToString(!lineWrapsToNext && trimRight).substring(0, this._terminal.cols);
lineIndex++;
} while (lineWrapsToNext);
return lineString;
};
SearchHelper.prototype._selectResult = function (result) {
if (!result) {
this._terminal.clearSelection();
return false;
}
this._terminal.select(result.col, result.row, result.term.length);
this._terminal.scrollLines(result.row - this._terminal.buffer.viewportY);
return true;
};
return SearchHelper;
}());
exports.SearchHelper = SearchHelper;
},{}],2:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var SearchHelper_1 = require("./SearchHelper");
function findNext(terminal, term, searchOptions) {
if (searchOptions === void 0) { searchOptions = {}; }
var addonTerminal = terminal;
if (!addonTerminal.__searchHelper) {
addonTerminal.__searchHelper = new SearchHelper_1.SearchHelper(addonTerminal);
}
return addonTerminal.__searchHelper.findNext(term, searchOptions);
}
exports.findNext = findNext;
function findPrevious(terminal, term, searchOptions) {
var addonTerminal = terminal;
if (!addonTerminal.__searchHelper) {
addonTerminal.__searchHelper = new SearchHelper_1.SearchHelper(addonTerminal);
}
return addonTerminal.__searchHelper.findPrevious(term, searchOptions);
}
exports.findPrevious = findPrevious;
function apply(terminalConstructor) {
terminalConstructor.prototype.findNext = function (term, searchOptions) {
return findNext(this, term, searchOptions);
};
terminalConstructor.prototype.findPrevious = function (term, searchOptions) {
return findPrevious(this, term, searchOptions);
};
}
exports.apply = apply;
},{"./SearchHelper":1}]},{},[2])(2)
});
//# sourceMappingURL=search.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,70 @@
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.terminado = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
function terminadoAttach(term, socket, bidirectional, buffered) {
var addonTerminal = term;
bidirectional = (typeof bidirectional === 'undefined') ? true : bidirectional;
addonTerminal.__socket = socket;
addonTerminal.__flushBuffer = function () {
addonTerminal.write(addonTerminal.__attachSocketBuffer);
addonTerminal.__attachSocketBuffer = null;
};
addonTerminal.__pushToBuffer = function (data) {
if (addonTerminal.__attachSocketBuffer) {
addonTerminal.__attachSocketBuffer += data;
}
else {
addonTerminal.__attachSocketBuffer = data;
setTimeout(addonTerminal.__flushBuffer, 10);
}
};
addonTerminal.__getMessage = function (ev) {
var data = JSON.parse(ev.data);
if (data[0] === 'stdout') {
if (buffered) {
addonTerminal.__pushToBuffer(data[1]);
}
else {
addonTerminal.write(data[1]);
}
}
};
addonTerminal.__sendData = function (data) {
socket.send(JSON.stringify(['stdin', data]));
};
addonTerminal.__setSize = function (size) {
socket.send(JSON.stringify(['set_size', size.rows, size.cols]));
};
socket.addEventListener('message', addonTerminal.__getMessage);
if (bidirectional) {
addonTerminal._core.register(addonTerminal.onData(addonTerminal.__sendData));
}
addonTerminal._core.register(addonTerminal.onResize(addonTerminal.__setSize));
socket.addEventListener('close', function () { return terminadoDetach(addonTerminal, socket); });
socket.addEventListener('error', function () { return terminadoDetach(addonTerminal, socket); });
}
exports.terminadoAttach = terminadoAttach;
function terminadoDetach(term, socket) {
var addonTerminal = term;
addonTerminal.__dataListener.dispose();
addonTerminal.__dataListener = undefined;
socket = (typeof socket === 'undefined') ? addonTerminal.__socket : socket;
if (socket) {
socket.removeEventListener('message', addonTerminal.__getMessage);
}
delete addonTerminal.__socket;
}
exports.terminadoDetach = terminadoDetach;
function apply(terminalConstructor) {
terminalConstructor.prototype.terminadoAttach = function (socket, bidirectional, buffered) {
return terminadoAttach(this, socket, bidirectional, buffered);
};
terminalConstructor.prototype.terminadoDetach = function (socket) {
return terminadoDetach(this, socket);
};
}
exports.apply = apply;
},{}]},{},[1])(1)
});
//# sourceMappingURL=terminado.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"terminado.js","sources":["../../../src/addons/terminado/terminado.ts","../../../node_modules/browser-pack/_prelude.js"],"sourcesContent":["/**\n * Copyright (c) 2016 The xterm.js authors. All rights reserved.\n * @license MIT\n *\n * This module provides methods for attaching a terminal to a terminado\n * WebSocket stream.\n */\n\nimport { Terminal } from 'xterm';\nimport { ITerminadoAddonTerminal } from './Interfaces';\n\n/**\n * Attaches the given terminal to the given socket.\n *\n * @param term The terminal to be attached to the given socket.\n * @param socket The socket to attach the current terminal.\n * @param bidirectional Whether the terminal should send data to the socket as well.\n * @param buffered Whether the rendering of incoming data should happen instantly or at a maximum\n * frequency of 1 rendering per 10ms.\n */\nexport function terminadoAttach(term: Terminal, socket: WebSocket, bidirectional: boolean, buffered: boolean): void {\n const addonTerminal = <ITerminadoAddonTerminal>term;\n bidirectional = (typeof bidirectional === 'undefined') ? true : bidirectional;\n addonTerminal.__socket = socket;\n\n addonTerminal.__flushBuffer = () => {\n addonTerminal.write(addonTerminal.__attachSocketBuffer);\n addonTerminal.__attachSocketBuffer = null;\n };\n\n addonTerminal.__pushToBuffer = (data: string) => {\n if (addonTerminal.__attachSocketBuffer) {\n addonTerminal.__attachSocketBuffer += data;\n } else {\n addonTerminal.__attachSocketBuffer = data;\n setTimeout(addonTerminal.__flushBuffer, 10);\n }\n };\n\n addonTerminal.__getMessage = (ev: MessageEvent) => {\n const data = JSON.parse(ev.data);\n if (data[0] === 'stdout') {\n if (buffered) {\n addonTerminal.__pushToBuffer(data[1]);\n } else {\n addonTerminal.write(data[1]);\n }\n }\n };\n\n addonTerminal.__sendData = (data: string) => {\n socket.send(JSON.stringify(['stdin', data]));\n };\n\n addonTerminal.__setSize = (size: {rows: number, cols: number}) => {\n socket.send(JSON.stringify(['set_size', size.rows, size.cols]));\n };\n\n socket.addEventListener('message', addonTerminal.__getMessage);\n\n if (bidirectional) {\n addonTerminal._core.register(addonTerminal.onData(addonTerminal.__sendData));\n }\n addonTerminal._core.register(addonTerminal.onResize(addonTerminal.__setSize));\n\n socket.addEventListener('close', () => terminadoDetach(addonTerminal, socket));\n socket.addEventListener('error', () => terminadoDetach(addonTerminal, socket));\n}\n\n/**\n * Detaches the given terminal from the given socket\n *\n * @param term The terminal to be detached from the given socket.\n * @param socket The socket from which to detach the current terminal.\n */\nexport function terminadoDetach(term: Terminal, socket: WebSocket): void {\n const addonTerminal = <ITerminadoAddonTerminal>term;\n addonTerminal.__dataListener.dispose();\n addonTerminal.__dataListener = undefined;\n\n socket = (typeof socket === 'undefined') ? addonTerminal.__socket : socket;\n\n if (socket) {\n socket.removeEventListener('message', addonTerminal.__getMessage);\n }\n\n delete addonTerminal.__socket;\n}\n\nexport function apply(terminalConstructor: typeof Terminal): void {\n /**\n * Attaches the current terminal to the given socket\n *\n * @param socket - The socket to attach the current terminal.\n * @param bidirectional - Whether the terminal should send data to the socket as well.\n * @param buffered - Whether the rendering of incoming data should happen instantly or at a\n * maximum frequency of 1 rendering per 10ms.\n */\n (<any>terminalConstructor.prototype).terminadoAttach = function (socket: WebSocket, bidirectional: boolean, buffered: boolean): void {\n return terminadoAttach(this, socket, bidirectional, buffered);\n };\n\n /**\n * Detaches the current terminal from the given socket.\n *\n * @param socket The socket from which to detach the current terminal.\n */\n (<any>terminalConstructor.prototype).terminadoDetach = function (socket: WebSocket): void {\n return terminadoDetach(this, socket);\n };\n}\n",null],"names":[],"mappings":"ACAA;;;ADoBA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AA/CA;AAuDA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AAZA;AAcA;AASA;AACA;AACA;AAOA;AACA;AACA;AACA;AArBA;"}

View File

@@ -0,0 +1,42 @@
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.webLinks = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var protocolClause = '(https?:\\/\\/)';
var domainCharacterSet = '[\\da-z\\.-]+';
var negatedDomainCharacterSet = '[^\\da-z\\.-]+';
var domainBodyClause = '(' + domainCharacterSet + ')';
var tldClause = '([a-z\\.]{2,6})';
var ipClause = '((\\d{1,3}\\.){3}\\d{1,3})';
var localHostClause = '(localhost)';
var portClause = '(:\\d{1,5})';
var hostClause = '((' + domainBodyClause + '\\.' + tldClause + ')|' + ipClause + '|' + localHostClause + ')' + portClause + '?';
var pathCharacterSet = '(\\/[\\/\\w\\.\\-%~:+]*)*([^:"\'\\s])';
var pathClause = '(' + pathCharacterSet + ')?';
var queryStringHashFragmentCharacterSet = '[0-9\\w\\[\\]\\(\\)\\/\\?\\!#@$%&\'*+,:;~\\=\\.\\-]*';
var queryStringClause = '(\\?' + queryStringHashFragmentCharacterSet + ')?';
var hashFragmentClause = '(#' + queryStringHashFragmentCharacterSet + ')?';
var negatedPathCharacterSet = '[^\\/\\w\\.\\-%]+';
var bodyClause = hostClause + pathClause + queryStringClause + hashFragmentClause;
var start = '(?:^|' + negatedDomainCharacterSet + ')(';
var end = ')($|' + negatedPathCharacterSet + ')';
var strictUrlRegex = new RegExp(start + protocolClause + bodyClause + end);
function handleLink(event, uri) {
window.open(uri, '_blank');
}
function webLinksInit(term, handler, options) {
if (handler === void 0) { handler = handleLink; }
if (options === void 0) { options = {}; }
options.matchIndex = 1;
term.registerLinkMatcher(strictUrlRegex, handler, options);
}
exports.webLinksInit = webLinksInit;
function apply(terminalConstructor) {
terminalConstructor.prototype.webLinksInit = function (handler, options) {
webLinksInit(this, handler, options);
};
}
exports.apply = apply;
},{}]},{},[1])(1)
});
//# sourceMappingURL=webLinks.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"webLinks.js","sources":["../../../src/addons/webLinks/webLinks.ts","../../../node_modules/browser-pack/_prelude.js"],"sourcesContent":["/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { Terminal, ILinkMatcherOptions } from 'xterm';\n\nconst protocolClause = '(https?:\\\\/\\\\/)';\nconst domainCharacterSet = '[\\\\da-z\\\\.-]+';\nconst negatedDomainCharacterSet = '[^\\\\da-z\\\\.-]+';\nconst domainBodyClause = '(' + domainCharacterSet + ')';\nconst tldClause = '([a-z\\\\.]{2,6})';\nconst ipClause = '((\\\\d{1,3}\\\\.){3}\\\\d{1,3})';\nconst localHostClause = '(localhost)';\nconst portClause = '(:\\\\d{1,5})';\nconst hostClause = '((' + domainBodyClause + '\\\\.' + tldClause + ')|' + ipClause + '|' + localHostClause + ')' + portClause + '?';\nconst pathCharacterSet = '(\\\\/[\\\\/\\\\w\\\\.\\\\-%~:+]*)*([^:\"\\'\\\\s])';\nconst pathClause = '(' + pathCharacterSet + ')?';\nconst queryStringHashFragmentCharacterSet = '[0-9\\\\w\\\\[\\\\]\\\\(\\\\)\\\\/\\\\?\\\\!#@$%&\\'*+,:;~\\\\=\\\\.\\\\-]*';\nconst queryStringClause = '(\\\\?' + queryStringHashFragmentCharacterSet + ')?';\nconst hashFragmentClause = '(#' + queryStringHashFragmentCharacterSet + ')?';\nconst negatedPathCharacterSet = '[^\\\\/\\\\w\\\\.\\\\-%]+';\nconst bodyClause = hostClause + pathClause + queryStringClause + hashFragmentClause;\nconst start = '(?:^|' + negatedDomainCharacterSet + ')(';\nconst end = ')($|' + negatedPathCharacterSet + ')';\nconst strictUrlRegex = new RegExp(start + protocolClause + bodyClause + end);\n\nfunction handleLink(event: MouseEvent, uri: string): void {\n window.open(uri, '_blank');\n}\n\n/**\n * Initialize the web links addon, registering the link matcher.\n * @param term The terminal to use web links within.\n * @param handler A custom handler to use.\n * @param options Custom options to use, matchIndex will always be ignored.\n */\nexport function webLinksInit(term: Terminal, handler: (event: MouseEvent, uri: string) => void = handleLink, options: ILinkMatcherOptions = {}): void {\n options.matchIndex = 1;\n term.registerLinkMatcher(strictUrlRegex, handler, options);\n}\n\nexport function apply(terminalConstructor: typeof Terminal): void {\n (<any>terminalConstructor.prototype).webLinksInit = function (handler?: (event: MouseEvent, uri: string) => void, options?: ILinkMatcherOptions): void {\n webLinksInit(this, handler, options);\n };\n}\n",null],"names":[],"mappings":"ACAA;;;ADOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAQA;AAAA;AAAA;AACA;AACA;AACA;AAHA;AAKA;AACA;AACA;AACA;AACA;AAJA;"}

View File

@@ -0,0 +1,45 @@
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.zmodem = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var zmodem;
function zmodemAttach(ws, opts) {
if (opts === void 0) { opts = {}; }
var term = this;
var senderFunc = function (octets) { return ws.send(new Uint8Array(octets)); };
var zsentry;
function shouldWrite() {
return !!zsentry.get_confirmed_session() || !opts.noTerminalWriteOutsideSession;
}
zsentry = new zmodem.Sentry({
to_terminal: function (octets) {
if (shouldWrite()) {
term.write(String.fromCharCode.apply(String, octets));
}
},
sender: senderFunc,
on_retract: function () { return term.emit('zmodemRetract'); },
on_detect: function (detection) { return term.emit('zmodemDetect', detection); }
});
function handleWSMessage(evt) {
if (typeof evt.data === 'string') {
if (shouldWrite()) {
term.write(evt.data);
}
}
else {
zsentry.consume(evt.data);
}
}
ws.binaryType = 'arraybuffer';
ws.addEventListener('message', handleWSMessage);
}
function apply(terminalConstructor) {
zmodem = (typeof window === 'object') ? window.Zmodem : { Browser: null };
terminalConstructor.prototype.zmodemAttach = zmodemAttach;
terminalConstructor.prototype.zmodemBrowser = zmodem.Browser;
}
exports.apply = apply;
},{}]},{},[1])(1)
});
//# sourceMappingURL=zmodem.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"zmodem.js","sources":["../../../src/addons/zmodem/zmodem.ts","../../../node_modules/browser-pack/_prelude.js"],"sourcesContent":["/**\n * Copyright (c) 2017 The xterm.js authors. All rights reserved.\n * @license MIT\n */\n\nimport { Terminal } from 'xterm';\n\n/**\n *\n * Allow xterm.js to handle ZMODEM uploads and downloads.\n *\n * This addon is a wrapper around zmodem.js. It adds the following to the\n * Terminal class:\n *\n * - function `zmodemAttach(<WebSocket>, <Object>)` - creates a Zmodem.Sentry\n * on the passed WebSocket object. The Object passed is optional and\n * can contain:\n * - noTerminalWriteOutsideSession: Suppress writes from the Sentry\n * object to the Terminal while there is no active Session. This\n * is necessary for compatibility with, for example, the\n * `attach.js` addon.\n *\n * - event `zmodemDetect` - fired on Zmodem.Sentrys `on_detect` callback.\n * Passes the zmodem.js Detection object.\n *\n * - event `zmodemRetract` - fired on Zmodem.Sentrys `on_retract` callback.\n *\n * Youll need to provide logic to handle uploads and downloads.\n * See zmodem.jss documentation for more details.\n *\n * **IMPORTANT:** After you confirm() a zmodem.js Detection, if you have\n * used the `attach` or `terminado` addons, youll need to suspend their\n * operation for the duration of the ZMODEM session. (The demo does this\n * via `detach()` and a re-`attach()`.)\n */\n\nlet zmodem: any;\n\nexport interface IZmodemOptions {\n noTerminalWriteOutsideSession?: boolean;\n}\n\nfunction zmodemAttach(ws: WebSocket, opts: IZmodemOptions = {}): void {\n const term = this;\n const senderFunc = (octets: ArrayLike<number>) => ws.send(new Uint8Array(octets));\n\n let zsentry: any;\n\n function shouldWrite(): boolean {\n return !!zsentry.get_confirmed_session() || !opts.noTerminalWriteOutsideSession;\n }\n\n zsentry = new zmodem.Sentry({\n to_terminal: (octets: ArrayLike<number>) => {\n if (shouldWrite()) {\n term.write(\n String.fromCharCode.apply(String, octets)\n );\n }\n },\n sender: senderFunc,\n on_retract: () => (<any>term).emit('zmodemRetract'),\n on_detect: (detection: any) => (<any>term).emit('zmodemDetect', detection)\n });\n\n function handleWSMessage(evt: MessageEvent): void {\n\n // In testing with xterm.jss demo the first message was\n // always text even if the rest were binary. While that\n // may be specific to xterm.jss demo, ultimately we\n // should reject anything that isnt binary.\n if (typeof evt.data === 'string') {\n if (shouldWrite()) {\n term.write(evt.data);\n }\n }\n else {\n zsentry.consume(evt.data);\n }\n }\n\n ws.binaryType = 'arraybuffer';\n ws.addEventListener('message', handleWSMessage);\n}\n\nexport function apply(terminalConstructor: typeof Terminal): void {\n zmodem = (typeof window === 'object') ? (<any>window).Zmodem : {Browser: null}; // Nullify browser for tests\n\n (<any>terminalConstructor.prototype).zmodemAttach = zmodemAttach;\n (<any>terminalConstructor.prototype).zmodemBrowser = zmodem.Browser;\n}\n",null],"names":[],"mappings":"ACAA;;;ADoCA;AAMA;AAAA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AAEA;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AALA;"}

View File

@@ -0,0 +1,171 @@
/**
* Copyright (c) 2014 The xterm.js authors. All rights reserved.
* Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
* https://github.com/chjj/term.js
* @license MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* Originally forked from (with the author's permission):
* Fabrice Bellard's javascript vt100 for jslinux:
* http://bellard.org/jslinux/
* Copyright (c) 2011 Fabrice Bellard
* The original design remains. The terminal itself
* has been extended to include xterm CSI codes, among
* other features.
*/
/**
* Default styles for xterm.js
*/
.xterm {
font-feature-settings: "liga" 0;
position: relative;
user-select: none;
-ms-user-select: none;
-webkit-user-select: none;
}
.xterm.focus,
.xterm:focus {
outline: none;
}
.xterm .xterm-helpers {
position: absolute;
top: 0;
/**
* The z-index of the helpers must be higher than the canvases in order for
* IMEs to appear on top.
*/
z-index: 10;
}
.xterm .xterm-helper-textarea {
/*
* HACK: to fix IE's blinking cursor
* Move textarea out of the screen to the far left, so that the cursor is not visible.
*/
position: absolute;
opacity: 0;
left: -9999em;
top: 0;
width: 0;
height: 0;
z-index: -10;
/** Prevent wrapping so the IME appears against the textarea at the correct position */
white-space: nowrap;
overflow: hidden;
resize: none;
}
.xterm .composition-view {
/* TODO: Composition position got messed up somewhere */
background: #000;
color: #FFF;
display: none;
position: absolute;
white-space: nowrap;
z-index: 1;
}
.xterm .composition-view.active {
display: block;
}
.xterm .xterm-viewport {
/* On OS X this is required in order for the scroll bar to appear fully opaque */
background-color: #000;
overflow-y: scroll;
cursor: default;
position: absolute;
right: 0;
left: 0;
top: 0;
bottom: 0;
}
.xterm .xterm-screen {
position: relative;
}
.xterm .xterm-screen canvas {
position: absolute;
left: 0;
top: 0;
}
.xterm .xterm-scroll-area {
visibility: hidden;
}
.xterm-char-measure-element {
display: inline-block;
visibility: hidden;
position: absolute;
top: 0;
left: -9999em;
line-height: normal;
}
.xterm {
cursor: text;
}
.xterm.enable-mouse-events {
/* When mouse events are enabled (eg. tmux), revert to the standard pointer cursor */
cursor: default;
}
.xterm.xterm-cursor-pointer {
cursor: pointer;
}
.xterm.column-select.focus {
/* Column selection mode */
cursor: crosshair;
}
.xterm .xterm-accessibility,
.xterm .xterm-message {
position: absolute;
left: 0;
top: 0;
bottom: 0;
right: 0;
z-index: 100;
color: transparent;
}
.xterm .live-region {
position: absolute;
left: -9999px;
width: 1px;
height: 1px;
overflow: hidden;
}
.xterm-dim {
opacity: 0.5;
}
.xterm-underline {
text-decoration: underline;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

51
handlers/www/bypass.html Normal file
View File

@@ -0,0 +1,51 @@
<!doctype html>
<html ng-app="DockerPlay" ng-controller="BypassController">
<head>
<title>Docker Playground</title>
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/angular_material/1.1.0/angular-material.min.css">
<link rel="stylesheet" href="/assets/style.css" />
</head>
<body class="welcome">
<div>
<h1>Welcome!</h1>
<h2>We're bypassing the Captcha and redirecting you now..</h2>
<form id="welcomeFormBypass" method="POST" action="/">
<button id="start" type="submit">Start Session</button>
<input id="stack" type="hidden" name="stack" value=""/>
<input id="stack_name" type="hidden" name="stack_name" value=""/>
<input id="image_name" type="hidden" name="image_name" value=""/>
</form>
</div>
</body>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular-animate.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular-aria.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angular_material/1.1.0/angular-material.min.js"></script>
<script src="/assets/app.js"></script>
<script>
function getParameterByName(name, url) {
if (!url) url = window.location.href;
name = name.replace(/[\[\]]/g, "\\$&");
var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return '';
return decodeURIComponent(results[2].replace(/\+/g, " "));
}
var stack = getParameterByName('stack');
if (stack) {
document.getElementById('stack').value = stack;
}
var stackName = getParameterByName('stack_name');
if (stack) {
document.getElementById('stack_name').value = stackName;
}
var imageName = getParameterByName('image_name');
if (stack) {
document.getElementById('image_name').value = imageName;
}
</script>
</html>

View File

@@ -0,0 +1,341 @@
<!doctype html>
<html ng-app="DockerPlay" ng-controller="PlayController">
<head>
<title>Docker Playground</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700,400italic|Material+Icons" />
<link rel="stylesheet" href="https://unpkg.com/angular-material@1.1.10/angular-material.min.css">
<link rel="stylesheet" href="/assets/xterm/xterm.css" />
<link rel="stylesheet" href="/assets/xterm/addons/fullscreen/fullscreen.css" />
<link rel="stylesheet" href="/assets/style.css" />
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-89019737-1', 'auto');
ga('send', 'pageview');
</script>
<script type="text/javascript" src="//cdn.bizible.com/scripts/bizible.js"async=""></script>
</head>
<body>
<div layout="column" style="height:100%;" ng-cloak>
<section id="sessionEnd" layout="row" flex ng-if="!isAlive">
<md-content flex layout-padding ng-if="!instances.length">
<div layout="column" layout-align="top center">
<p>
<strong>Your session has expired.</strong>
</p>
</div>
<div flex></div>
</md-content>
</section>
<section ng-if="!connected" class="disconnected" layout="row" layout-align="center center">
<h1 class="md-headline">No connection to server. Reconnecting...</h1>
<md-progress-circular class="md-hue-2" md-diameter="20px"></md-progress-circular>
</section>
<section id="popupContainer" layout="row" flex ng-if="isAlive">
<md-sidenav
class="md-sidenav-left"
md-component-id="left"
md-is-locked-open="$mdMedia('gt-sm')"
md-whiteframe="4" layout="column">
<md-toolbar class="md-theme-indigo">
<span class="clock">{{ttl}}</span>
<md-button class="md-warn md-raised" ng-click="closeSession()">Close session</md-button>
<div class="md-toolbar-tools">
<h1 class="md-toolbar-tools">Instances</h1>
<templates-icon></templates-icon>
<settings-icon></settings-icon><br/>
</div>
<div class="md-toolbar-tools" ng-if="playground.allow_windows_instances">
<md-switch ng-model="type.windows">
Windows containers {{windows}}
</md-switch>
</div>
</md-toolbar>
<md-content layout-padding>
<md-button ng-click="newInstance()" ng-disabled="isInstanceBeingCreated" class="md-primary">{{newInstanceBtnText}}</md-button>
<md-list class="md-dense" flex>
<md-list-item ng-switch on="instance.isManager || instance.isK8sManager" class="md-2-line" ng-repeat="instance in instances | orderBy:'hostname'" ng-click="showInstance(instance)" ng-class="instance.name == selectedInstance.name ? 'selected' : false">
<md-icon ng-switch-when="true" style="color: blue" md-svg-icon="person"></md-icon>
<md-icon ng-switch-when="false" md-svg-icon="person-outline"></md-icon>
<div class="md-list-item-text" layout="column">
<h3>{{instance.ip}}</h3>
<h4>{{instance.hostname}}</h4>
</div>
<md-divider ng-if="!$last"></md-divider>
</md-list-item>
</md-list>
</md-content>
</md-sidenav>
<md-content flex layout-padding ng-if="!instances.length">
<div layout="column" layout-align="top center">
<p>Add instances to your playground.</p>
<p><strong>Sessions and all their instances are deleted after {{ttl}} hours.</strong></p>
</div>
<div flex></div>
</md-content>
<md-content flex layout="column" ng-repeat="instance in instances" ng-show="instance.name == selectedInstance.name" ngf-drop class="drop-box" ngf-drag-over-class="'dragover'" ngf-max-size="100000000" ngf-change="uploadFiles($files, $invalidFiles)" ngf-multiple="true">
<md-card class="stats" md-theme="default" md-theme-watch>
<md-card-title>
<md-card-title-text>
<span class="md-headline">{{instance.name}}</span>
</md-card-title-text>
</md-card-title>
<md-card-content>
<div layout-gt-sm="row">
<md-input-container class="md-icon-float md-block">
<label>IP</label>
<input ng-model="instance.ip" type="text" readonly="readonly">
</md-input-container>
<md-button class="md-raised" ng-click="openPort(instance)">
Open Port
</md-button>
<md-chips ng-model="instance.ports" name="port" readonly="true" md-removable="false">
<md-chip-template>
<strong><a href="{{getProxyUrl(instance, $chip)}}" title="{{getProxyUrl(instance, $chip)}}" target="_blank">{{$chip}}</a></strong>
</md-chip-template>
</md-chips>
<md-chips ng-model="instance.swarmPorts" name="port" readonly="true" md-removable="false">
<md-chip-template>
<strong><a href="{{getProxyUrl(instance, $chip)}}" title="{{getProxyUrl(instance, $chip)}}" target="_blank">{{$chip}}</a></strong>
</md-chip-template>
</md-chips>
</div>
<div layout-gt-sm="row">
<md-input-container class="md-block" flex-gt-sm>
<label>Memory</label>
<input ng-model="instance.mem" type="text" readonly="readonly">
</md-input-container>
<md-input-container class="md-block" flex-gt-sm>
<label>CPU</label>
<input ng-model="instance.cpu" type="text" readonly="readonly">
</md-input-container>
</div>
<div layout-gt-sm="row">
<md-input-container>
<label>SSH</label>
<input value="ssh {{instance.proxy_host}}@direct.{{host}}" type="text" readonly="readonly" size="50">
<md-icon ngclipboard data-clipboard-text="ssh {{instance.proxy_host}}@direct.{{host}} "class="material-icons">content_copy
<md-tooltip md-direction="top">Copy!</md-tooltip>
</md-icon>
</md-input-container>
<div class="md-block" glex-gt-sm></div>
</div>
</md-card-content>
<md-card-actions>
<md-button class="md-warn md-raised" ng-click="deleteInstance(instance)" ng-disabled="isInstanceBeingDeleted">{{deleteInstanceBtnText}}</md-button>
<md-button class="md-raised" ng-click="openEditor(instance)">
<md-icon class="material-icons">insert_drive_file</md-icon> Editor
</md-button>
</md-card-actions>
</md-card>
<md-card flex md-theme="default" md-theme-watch >
<div ng-show="uploadMessage" class="uploadStatus">
<md-progress-linear md-mode="determinate" value="{{uploadProgress}}"></md-progress-linear>
<div class="bottom-block">
<span>{{uploadMessage}}</span>
</div>
</div>
<div ng-show="instance.status=='reconnect'" class="uploadStatus">Connection has been lost. Sometimes this happens when a windows instance is joining a swarm. Trying to reconnect terminal...</div>
<id class="terminal-container container-{{instance.name}}">
<div class="terminal-instance" id="terminal-{{instance.name}}"></div>
</id>
</md-card>
</md-content>
</section>
</div>
<div style="visibility: hidden;">
<div class="md-dialog-container" id="builderDialog">
<md-dialog>
<md-toolbar>
<div class="md-toolbar-tools">
<h2>Session stack builder</h2>
<span flex></span>
</div>
</md-toolbar>
<md-dialog-content layout-padding>
<div flex="100" style="margin: 20px 0px;">
We are building your stack. This might take a while.<br/>
</div>
<div id="builder-terminal" style="height: 450px; width: 800px">
</div>
<div layout="row" ng-if="ready">
<div flex="100" style="margin-top: 20px; text-align:center; font-weight: bold; color: green;">
Your session is ready!
</div>
</div>
</md-dialog-content>
<md-dialog-actions layout="row" ng-if="ready">
<span flex></span>
<md-button ng-click="closeSessionBuilder()">
Close
</md-button>
</md-dialog-actions>
</md-dialog>
</div>
</div>
<script type="text/ng-template" id="templates-modal.html">
<md-toolbar>
<div class="md-toolbar-tools">
<h2>Templates</h2>
<span flex></span>
<md-button class="md-icon-button" ng-click="$ctrl.close()">
<md-icon class="material-icon" aria-label="Close dialog">close</md-icon>
</md-button>
</div>
</md-toolbar>
<md-dialog-content>
<div class="md-dialog-content" style="width:600px;">
<div layout="row" layout-sm="column" layout-align="space-around" ng-if="building">
<md-progress-circular md-mode="indeterminate"></md-progress-circular>
</div>
<div layout="row" ng-if="errorMessage">
<div flex="100" style="margin-top: 20px; text-align:center; font-weight: bold; color: red;">
{{errorMessage}}
</div>
</div>
<md-list flex ng-if="!building">
<md-list-item class="md-3-line" ng-repeat="template in templates" ng-click="$ctrl.setupSession(template.setup)">
<md-card md-theme="default" md-theme-watch>
<md-card-title>
<md-card-title-text>
<span class="md-headline">{{template.title}}</span>
</md-card-title-text>
<md-card-title-media>
<div class="md-media-sm card-media"><img ng-src="{{template.icon}}" style="height: 75px;" class="md-card-image"></div>
</md-card-title-media>
</md-card-title>
</md-card>
</md-list-item>
</md-list>
</div>
</md-dialog-content>
<md-dialog-actions layout="row">
<span flex></span>
<md-button ng-click="$ctrl.close()">
Close
</md-button>
</md-dialog-actions>
</script>
<script type="text/ng-template" id="settings-modal.html">
<md-toolbar>
<div class="md-toolbar-tools">
<h2>Settings</h2>
<span flex></span>
<md-button class="md-icon-button" ng-click="$ctrl.close()">
<md-icon class="material-icon" aria-label="Close dialog">close</md-icon>
</md-button>
</div>
</md-toolbar>
<md-dialog-content>
<div class="md-dialog-content" style="width:600px;">
<div layout="row">
<div flex="50">
<md-input-container class="md-block" flex-gt-sm>
<label>Keyboard Shortcut Preset</label>
<md-select ng-model="$ctrl.currentShortcutConfig" ng-model-options="{getterSetter: true}" placeholder="Keyboard shortcut prefix">
<md-option ng-repeat="preset in $ctrl.keyboardShortcutPresets" value="{{preset}}">
{{preset.name}}
</md-option>
</md-select>
</md-input-container>
</div>
<div flex="10"></div>
<div flex="40">
<div ng-if="$ctrl.selectedShortcutPreset">
Preset details:
<ul>
<li ng-if="$ctrl.selectedShortcutPreset.presets.length == 0">No presets defined</li>
<li ng-repeat="preset in $ctrl.selectedShortcutPreset.presets">
<code>{{preset.command}}</code> - {{preset.description}}
</li>
</ul>
</div>
</div>
</div>
<div layout="row">
<div flex="50">
<md-input-container class="md-block" flex-gt-sm>
<label>Instance Image</label>
<md-select ng-model="$ctrl.currentDesiredInstanceImage" ng-model-options="{getterSetter: true}" placeholder="New Instance Image">
<md-option ng-repeat="image in $ctrl.instanceImages" value="{{image}}">
{{ image }}
</md-option>
</md-select>
</md-input-container>
</div>
</div>
<div layout="row">
<div flex="50">
<md-input-container class="md-block" flex-gt-sm>
<label>Terminal Font Size</label>
<md-select ng-model="$ctrl.currentTerminalFontSize" ng-model-options="{getterSetter: true}">
<md-option ng-repeat="size in $ctrl.terminalFontSizes" value="{{size}}">
{{ size }}
</md-option>
</md-select>
</md-input-container>
</div>
</div>
</div>
</md-dialog-content>
<md-dialog-actions layout="row">
<span flex></span>
<md-button ng-click="$ctrl.close()">
Close
</md-button>
</md-dialog-actions>
</script>
<script src="https://unpkg.com/reconnectingwebsocket@1.0.0/reconnecting-websocket.min.js" integrity="sha384-FtJyC+/3fgtPbqlacLHdGwBrmPjKoYBsiqNF5/BEprsnIXB4xtXLCJRx7Xx+TWKP" crossorigin="anonymous"></script>
<script
src="https://code.jquery.com/jquery-3.2.1.min.js"
integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4="
crossorigin="anonymous"></script>
<script src="https://unpkg.com/angular@1.5.5/angular.min.js"></script>
<script src="https://unpkg.com/angular-animate@1.5.5/angular-animate.min.js"></script>
<script src="https://unpkg.com/angular-aria@1.5.5/angular-aria.min.js"></script>
<script src="https://unpkg.com/angular-messages@1.5.5/angular-messages.min.js"></script>
<script src="https://unpkg.com/angular-material@1.1.0/angular-material.min.js"></script>
<script src="https://cdn.rawgit.com/zenorocha/clipboard.js/master/dist/clipboard.min.js"></script>
<script src="https://unpkg.com/ngclipboard@2.0.0/dist/ngclipboard.min.js"></script>
<script src="https://unpkg.com/ng-file-upload@12.2.13/dist/ng-file-upload-all.min.js" integrity="sha384-NbBOS/QuqJqwWOtYg/L3ZDhgl/6GFyvkRMypJQLgoisMPtJiHj5uQ+3bj8V8Muwm" crossorigin="anonymous"></script>
<script src="/assets/xterm/xterm.js"></script>
<script src="/assets/xterm/addons/fit/fit.js"></script>
<script src="/assets/xterm/addons/fullscreen/fullscreen.js"></script>
<script src="/assets/setup-xterm.js"></script>
<script src="/assets/attach.js"></script>
<script src="https://unpkg.com/moment@2.16.0/min/moment.min.js"></script>
<script src="/assets/app.js"></script>
<script type="text/javascript" charset="utf-8">
window.onbeforeunload = function (e) {
e = e || window.event;
// For IE and Firefox prior to version 4
if (e) {
e.returnValue = 'Make sure you saved your session URL';
}
// For Safari
return 'Make sure you saved your session URL';
};
</script>
</body>
</html>

View File

@@ -0,0 +1,172 @@
<!DOCTYPE html>
<html lang="en" ng-app="PWDLanding" ng-controller="LoginController">
<head>
<script src="https://unpkg.com/angular@1.6.6/angular.min.js"></script>
<script src="https://unpkg.com/angular-cookies@1.6.6/angular-cookies.min.js"></script>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<title>Play with Docker</title>
<!-- Bootstrap core CSS -->
<link rel="stylesheet" href="https://unpkg.com/bootstrap@4.0.0-beta/dist/css/bootstrap.min.css" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
<!-- Custom styles for this template -->
<link href="/assets/landing.css" rel="stylesheet">
<script type="text/javascript" src="//cdn.bizible.com/scripts/bizible.js"async=""></script>
</head>
<body>
<div class="container">
<div class="header clearfix">
<nav>
<ul class="nav nav-pills float-right">
<li class="nav-item">
<a class="nav-link" href="https://github.com/play-with-docker/play-with-docker">Contribute</a>
</li>
</ul>
</nav>
</div>
<div class="jumbotron" ng-cloak>
<img src="https://www.docker.com/sites/default/files/Whale%20Logo332_5.png" />
<h1 class="display-3">Play with Docker</h1>
<p class="lead">A simple, interactive and fun playground to learn Docker</p>
<div ng-hide="loggedIn" class="btn-group" role="group">
<button id="btnGroupDrop1" type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Login
</button>
<div class="dropdown-menu" aria-labelledby="btnGroupDrop1">
<a ng-repeat="provider in providers" class="dropdown-item" ng-click="login(provider)">{{provider}}</a>
</div>
</div>
<form id="landingForm" method="POST" action="/">
<p ng-show="loggedIn"><a class="btn btn-lg btn-success" href="#" ng-click="start()" role="button">Start</a></p>
<input id="stack" type="hidden" name="stack" value=""/>
<input id="stack_name" type="hidden" name="stack_name" value=""/>
<input id="image_name" type="hidden" name="image_name" value=""/>
</form>
</div>
<div class="row marketing">
<div class="col-lg-12">
<p>Play with Docker (PWD) is a project hacked by <a href="https://www.twitter.com/marcosnils">Marcos Liljedhal</a> and <a href="https://www.twitter.com/xetorthio">Jonathan Leibiusky</a> and sponsored by Docker Inc.</p>
<p>PWD is a Docker playground which allows users to run Docker commands in a matter of seconds. It gives the experience of having a free Alpine Linux Virtual Machine in browser, where you can build and run Docker containers and even create clusters in <a href="https://docs.docker.com/engine/swarm/">Docker Swarm Mode</a>. Under the hood Docker-in-Docker (DinD) is used to give the effect of multiple VMs/PCs. In addition to the playground, PWD also includes a training site composed of a large set of Docker labs and quizzes from beginner to advanced level available at <a href="http://training.play-with-docker.com/">training.play-with-docker.com</a>.</p>
</div>
</div>
<footer class="footer">
<p>Your use of Play With Docker is subject to the Docker Terms of Service which can be accessed <a target="_blank" href="https://www.docker.com/legal/docker-terms-service">here</a></p>
<p>&copy; Play with Docker 2017 - 2020</p>
</footer>
</div>
[[ if .SegmentId ]]
<script>
!function(){var analytics=window.analytics=window.analytics||[];if(!analytics.initialize)if(analytics.invoked)window.console&&console.error&&console.error("Segment snippet included twice.");else{analytics.invoked=!0;analytics.methods=["trackSubmit","trackClick","trackLink","trackForm","pageview","identify","reset","group","track","ready","alias","debug","page","once","off","on"];analytics.factory=function(t){return function(){var e=Array.prototype.slice.call(arguments);e.unshift(t);analytics.push(e);return analytics}};for(var t=0;t<analytics.methods.length;t++){var e=analytics.methods[t];analytics[e]=analytics.factory(e)}analytics.load=function(t){var e=document.createElement("script");e.type="text/javascript";e.async=!0;e.src=("https:"===document.location.protocol?"https://":"http://")+"cdn.segment.com/analytics.js/v1/"+t+"/analytics.min.js";var n=document.getElementsByTagName("script")[0];n.parentNode.insertBefore(e,n)};analytics.SNIPPET_VERSION="4.0.0";
analytics.load('[[ .SegmentId ]]');
analytics.page();
}}();
</script>
[[ end ]]
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://unpkg.com/popper.js@1.11.0/dist/umd/popper.min.js" integrity="sha384-b/U6ypiBEHpOf/4+1nzFpr53nxSS+GLCkfwBdFNTxtclqqenISfwAzpKaMNFNmj4" crossorigin="anonymous"></script>
<script src="https://unpkg.com/bootstrap@4.0.0-beta/dist/js/bootstrap.min.js" integrity="sha384-h0AbiXch4ZDo7tp9hKZ4TsHbi047NrKGLO3SEJAg45jXxnGIfYzk4Si90RDIqNm1" crossorigin="anonymous"></script>
<script>
angular.module('PWDLanding', [])
.controller('LoginController', ['$scope', '$http', '$window', function($scope, $http, $window) {
$scope.providers = [];
$scope.loggedIn = false;
$scope.user = null;
function checkLoggedIn() {
$http({
method: 'GET',
url: '/users/me'
}).then(function(response) {
[[ if .SegmentId ]]
analytics.identify(response.data.provider_user_id, {"email": response.data.email});
[[ end ]]
$scope.user = response.data;
$scope.loggedIn = true;
}, function(response) {
[[ if .SegmentId ]]
analytics.identify();
[[ end ]]
console.log('ERROR', response);
$scope.user = null;
$scope.loggedIn = false;
});
}
checkLoggedIn();
$http({
method: 'GET',
url: '/oauth/providers'
}).then(function(response) {
$scope.providers = response.data;
if ($scope.providers.length == 0) {
$scope.loggedIn = true;
}
}, function(response) {
console.log('ERROR', response);
});
$scope.login = function(provider) {
var width = screen.width*0.6;
// fixed height as the login window is not responsive
var height = 620;
var x = screen.width/2 - width/2;
var y = screen.height/2 - height/2;
$window.open('/oauth/providers/' + provider + '/login', 'PWDLogin', 'width='+width+',height='+height+',left='+x+',top='+y);
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
var eventer = window[eventMethod];
var messageEvent = eventMethod == "attachEvent" ? "onmessage" : "message";
// Listen to message from child window
eventer(messageEvent,function(e) {
if (e.data === 'done') {
checkLoggedIn();
}
}, false);
}
$scope.start = function() {
function getParameterByName(name, url) {
if (!url) url = window.location.href;
name = name.replace(/[\[\]]/g, "\\$&");
var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return '';
return decodeURIComponent(results[2].replace(/\+/g, " "));
}
var stack = getParameterByName('stack');
if (stack) {
document.getElementById('stack').value = stack;
}
var stackName = getParameterByName('stack_name');
if (stackName) {
document.getElementById('stack_name').value = stackName;
}
var imageName = getParameterByName('image_name');
if (imageName) {
document.getElementById('image_name').value = imageName;
}
document.getElementById('landingForm').submit();
}
}]);
</script>
</body>
</html>

177
handlers/www/editor.html Normal file
View File

@@ -0,0 +1,177 @@
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>Editor</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/zTree.v3/3.5.29/css/metroStyle/metroStyle.min.css" integrity="sha256-9gdJ9sAxTV5MghBon+jq1V85ef8ALkS632yIIjAvAxc=" crossorigin="anonymous" />
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css" integrity="sha384-PsH8R72JQ3SOdhVi3uxftmaW6Vc51MKb0q5P2rRUpPvrszuE4W1povHYgTpBfshb" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/open-iconic/1.1.1/font/css/open-iconic-bootstrap.min.css" integrity="sha256-BJ/G+e+y7bQdrYkS2RBTyNfBHpA9IuGaPmf9htub5MQ=" crossorigin="anonymous" />
<link rel="stylesheet" href="/assets/editor.css">
</head>
<body>
<div class="container-fluid ">
<div id="alert-info" class="alert alert-info alert-top" role="alert">
<span class="alert-msg"></span>
</div>
<div class="row" style="height: 100%">
<div class="col-md-3">
<div>
<div class="alert alert-info alert-newfile" role="alert">
<span><strong>Create or upload</strong> files in the session terminal and then refresh </span>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<button type="button" id='treeReloadBtn' class="btn btn-sm">
<span class="oi oi-reload" title="Refresh" aria-hidden="true"></span>
</button>
</div>
<div id="fileTree" class="ztree"></div>
</div>
<div class="col-md-8">
<!-- Nav tabs -->
<ul class="nav nav-tabs" id="tabs" role="tablist">
</ul>
<!-- Tab panes -->
<div class="tab-content">
</div>
</div>
</div>
</div>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>
<script type="text/javascript" src="https://cdn.rawgit.com/zTree/zTree_v3/4f2717d4/js/jquery.ztree.core.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.3/umd/popper.min.js" integrity="sha384-vFJXuSJphROIrBnz7yo7oB41mKfc8JzQZiCq4NCceLEaO4IHwicKwpJf9c9IpFgh" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/js/bootstrap.min.js" integrity="sha384-alpBpkh1PFOepccYVYDB4do5UnbKysX5WZXm3XxPqe5iKTfUKjNkCk9SaVuEZflJ" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.9/ace.js" integrity="sha256-U//RSeH3TR3773Rk+1lAufJnRjEaG5LcdbvGV72kHEM=" crossorigin="anonymous"></script>
<script src="https://cdn.rawgit.com/beatgammit/base64-js/be644dec/base64js.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
var alertShow = function(msg) {
$div = $('#alert-info');
$div.find('.alert-msg').text(msg);
if ($div.css('display') === 'none') {
// fadein, fadeout.
$div.fadeIn(1000).delay(1000).fadeOut(1000);
}
};
//regiters events for newly created tab elements
var registerEvents = function(tabId) {
//this method will register event on close icon on the tab..
$(".closeTab").click(function () {
//there are multiple elements which has .closeTab icon so close the tab whose close icon is clicked
var tabContentId = $(this).parent().attr("href");
$(this).parent().parent().remove(); //remove li of tab
$('#tabs a:last').tab('show'); // Select first tab
$(tabContentId).remove(); //remove respective tab content
});
$('#fileReload_'+tabId+'Btn').click(function() {
var path = $(this).attr('data-file-path');
loadFile(path, tabId);
});
$('#fileSave_'+tabId+'Btn').click(function() {
var path = $(this).attr('data-file-path');
saveFile(path, tabId);
});
};
var getFilePath = function(treeNode) {
var parent = treeNode.getParentNode();
var path = '';
if (parent) {
return getFilePath(parent) + '/' + treeNode.name;
} else {
return treeNode.name;
}
};
var treeClick = function(event, treeId, treeNode, clickFlag) {
if (!treeNode.isParent) {
var tabId = treeNode.tId;
if ($('#tab_' + tabId + '').length == 0) {
var filePath = getFilePath(treeNode);
$('.nav-tabs').append('<li class="nav-item"><a class="nav-link" data-toggle="tab" role="tab" href="#tab_' + tabId + '"><button class="close closeTab" type="button" >×</button>'+treeNode.name+'</a></li>');
$('.tab-content').append(' \
<div class="tab-pane" id="tab_' + tabId + '"> \
<div style="height: 40px; width: 100%; padding-top: 5px; background-color: #F0F0F0"> \
<button type="button" id="fileSave_'+tabId+'Btn" data-file-path="'+filePath+'" class="btn btn-info btn-sm"> \
<span class="oi oi-data-transfer-upload" title="Save" aria-hidden="true"></span> Save \
</button> \
<button type="button" id="fileReload_'+tabId+'Btn" data-file-path="'+filePath+'" class="btn btn-info btn-sm"> \
<span class="oi oi-reload" title="Save" aria-hidden="true"></span> Reload \
</button> \
</div> \
<div id="editor_'+ tabId +'" style="height: calc(100vh - 82px); width: 100%;"></div> \
</div> \
');
loadFile(filePath, tabId);
registerEvents(tabId);
}
$('#tabs a[href="#tab_'+ tabId +'"]').tab('show');
}
};
var loadFile = function(filePath, tabId) {
var editor = ace.edit('editor_'+tabId);
$.get('./file?path='+filePath)
.done(function( fileBase64 ) {
var bytes = base64js.toByteArray(fileBase64);
editor.setValue((new TextDecoder("utf-8")).decode(bytes), -1);
editor.focus();
alertShow('file loaded')
});
}
var saveFile = function(filePath, tabId) {
var editor = ace.edit('editor_'+tabId);
var fileData = new Blob([editor.getValue()], { type: 'text/plain' });
var data = new FormData();
var fileName = filePath.substr(filePath.lastIndexOf('/'))
data.append(fileName,fileData, fileName);
$.ajax({
url: 'uploads?path='+filePath.substr(0, filePath.lastIndexOf('/')),
data: data,
cache: false,
contentType: false,
processData: false,
method: 'POST',
type: 'POST', // For jQuery < 1.9
success: function(data) {
alertShow('file saved')
}
});
};
var setting = {
data: {
key: {
children: "contents"
}
},
callback: {
onClick: treeClick
}
};
var populateTree = function() {
$.getJSON('./fstree')
.done(function( treeData ) {
treeData[0].open = true;
$.fn.zTree.init($("#fileTree"), setting, treeData);
});
}
// Attach handlers to tree reload btn
$('#treeReloadBtn').click(populateTree);
// populate tree whenever the page starts
populateTree();
});
</script>
</body>
</html>

263
handlers/www/k8s/index.html Normal file
View File

@@ -0,0 +1,263 @@
<!doctype html>
<html ng-app="DockerPlay" ng-controller="PlayController">
<head>
<title>Docker Playground</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700,400italic|Material+Icons" />
<link rel="stylesheet" href="https://unpkg.com/angular-material@1.1.10/angular-material.min.css">
<link rel="stylesheet" href="/assets/xterm/xterm.css" />
<link rel="stylesheet" href="/assets/xterm/addons/fullscreen/fullscreen.css" />
<link rel="stylesheet" href="/assets/style.css" />
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-89019737-1', 'auto');
ga('send', 'pageview');
</script>
<script type="text/javascript" src="//cdn.bizible.com/scripts/bizible.js"async=""></script>
</head>
<body>
<div layout="column" style="height:100%;" ng-cloak>
<section id="sessionEnd" layout="row" flex ng-if="!isAlive">
<md-content flex layout-padding ng-if="!instances.length">
<div layout="column" layout-align="top center">
<p>
<strong>Your session has expired.</strong>
</p>
</div>
<div flex></div>
</md-content>
</section>
<section ng-if="!connected" class="disconnected" layout="row" layout-align="center center">
<h1 class="md-headline">No connection to server. Reconnecting...</h1>
<md-progress-circular class="md-hue-2" md-diameter="20px"></md-progress-circular>
</section>
<section id="popupContainer" layout="row" flex ng-if="isAlive">
<md-sidenav
class="md-sidenav-left"
md-component-id="left"
md-theme="kube"
md-is-locked-open="$mdMedia('gt-sm')"
md-whiteframe="4" layout="column">
<md-toolbar class="md-accent md-hue-3">
<span class="clock">{{ttl}}</span>
<md-button class="md-warn md-raised" ng-click="closeSession()">Close session</md-button>
<div class="md-toolbar-tools">
<h1 class="md-toolbar-tools">Instances</h1>
<settings-icon></settings-icon><br/>
</div>
<div class="md-toolbar-tools" ng-if="playground.allow_windows_instances">
<md-switch ng-model="type.windows">
Windows containers {{windows}}
</md-switch>
</div>
</md-toolbar>
<md-content layout-padding>
<md-button ng-click="newInstance()" ng-disabled="isInstanceBeingCreated" class="md-primary">{{newInstanceBtnText}}</md-button>
<md-list class="md-dense" flex>
<md-list-item ng-switch on="instance.isManager || instance.isK8sManager" class="md-2-line" ng-repeat="instance in instances | orderBy:'hostname'" ng-click="showInstance(instance)" ng-class="instance.name == selectedInstance.name ? 'selected' : false">
<md-icon ng-switch-when="true" style="color: blue" md-svg-icon="person"></md-icon>
<md-icon ng-switch-when="false" md-svg-icon="person-outline"></md-icon>
<div class="md-list-item-text" layout="column">
<h3>{{instance.ip}}</h3>
<h4>{{instance.hostname}}</h4>
</div>
<md-divider ng-if="!$last"></md-divider>
</md-list-item>
</md-list>
</md-content>
</md-sidenav>
<md-content flex layout-padding ng-if="!instances.length">
<div layout="column" layout-align="top center">
<p>Add instances to your playground.</p>
<p><strong>Sessions and all their instances are deleted after {{ttl}} hours.</strong></p>
</div>
<div flex></div>
</md-content>
<md-content flex layout="column" ng-repeat="instance in instances" ng-show="instance.name == selectedInstance.name" ngf-drop class="drop-box" ngf-drag-over-class="'dragover'" ngf-max-size="100000000" ngf-change="uploadFiles($files, $invalidFiles)" ngf-multiple="true">
<md-card class="stats" md-theme="default" md-theme-watch>
<md-card-title>
<md-card-title-text>
<span class="md-headline">{{instance.name}}</span>
</md-card-title-text>
</md-card-title>
<md-card-content>
<div layout-gt-sm="row">
<md-input-container class="md-icon-float md-block">
<label>IP</label>
<input ng-model="instance.ip" type="text" readonly="readonly">
</md-input-container>
<md-chips ng-model="instance.ports" name="port" readonly="true" md-removable="false">
<md-chip-template>
<strong><a href="{{getProxyUrl(instance, $chip)}}" title="{{getProxyUrl(instance, $chip)}}" target="_blank">{{$chip}}</a></strong>
</md-chip-template>
</md-chips>
<md-chips ng-model="instance.swarmPorts" name="port" readonly="true" md-removable="false">
<md-chip-template>
<strong><a href="{{getProxyUrl(instance, $chip)}}" title="{{getProxyUrl(instance, $chip)}}" target="_blank">{{$chip}}</a></strong>
</md-chip-template>
</md-chips>
</div>
<div layout-gt-sm="row">
<md-input-container class="md-block" flex-gt-sm>
<label>Memory</label>
<input ng-model="instance.mem" type="text" readonly="readonly">
</md-input-container>
<md-input-container class="md-block" flex-gt-sm>
<label>CPU</label>
<input ng-model="instance.cpu" type="text" readonly="readonly">
</md-input-container>
</div>
<div layout-gt-sm="row">
<md-input-container>
<label>URL</label>
<input value="{{instance.proxy_host}}.direct.{{host}}" type="text" readonly="readonly" size="50">
</md-input-container>
<div class="md-block" glex-gt-sm></div>
</div>
</md-card-content>
<md-card-actions>
<md-button class="md-warn md-raised" ng-click="deleteInstance(instance)" ng-disabled="isInstanceBeingDeleted">{{deleteInstanceBtnText}}</md-button>
</md-card-actions>
</md-card>
<md-card flex md-theme="default" md-theme-watch >
<div ng-show="uploadMessage" class="uploadStatus">
<md-progress-linear md-mode="determinate" value="{{uploadProgress}}"></md-progress-linear>
<div class="bottom-block">
<span>{{uploadMessage}}</span>
</div>
</div>
<div ng-show="instance.status=='reconnect'" class="uploadStatus">Connection has been lost. Sometimes this happens when a windows instance is joining a swarm. Trying to reconnect terminal...</div>
<id class="terminal-container container-{{instance.name}}">
<div class="terminal-instance" id="terminal-{{instance.name}}"></div>
</id>
</md-card>
</md-content>
</section>
</div>
<script type="text/ng-template" id="settings-modal.html">
<md-toolbar md-theme="kube">
<div class="md-toolbar-tools">
<h2>Settings</h2>
<span flex></span>
<md-button class="md-icon-button" ng-click="$ctrl.close()">
<md-icon class="material-icon" aria-label="Close dialog">close</md-icon>
</md-button>
</div>
</md-toolbar>
<md-dialog-content>
<div class="md-dialog-content" style="width:600px;">
<div layout="row">
<div flex="50">
<md-input-container class="md-block" flex-gt-sm>
<label>Keyboard Shortcut Preset</label>
<md-select ng-model="$ctrl.currentShortcutConfig" ng-model-options="{getterSetter: true}" placeholder="Keyboard shortcut prefix">
<md-option ng-repeat="preset in $ctrl.keyboardShortcutPresets" value="{{preset}}">
{{preset.name}}
</md-option>
</md-select>
</md-input-container>
</div>
<div flex="10"></div>
<div flex="40">
<div ng-if="$ctrl.selectedShortcutPreset">
Preset details:
<ul>
<li ng-if="$ctrl.selectedShortcutPreset.presets.length == 0">No presets defined</li>
<li ng-repeat="preset in $ctrl.selectedShortcutPreset.presets">
<code>{{preset.command}}</code> - {{preset.description}}
</li>
</ul>
</div>
</div>
</div>
<div layout="row">
<div flex="50">
<md-input-container class="md-block" flex-gt-sm>
<label>Instance Image</label>
<md-select ng-model="$ctrl.currentDesiredInstanceImage" ng-model-options="{getterSetter: true}" placeholder="New Instance Image">
<md-option ng-repeat="image in $ctrl.instanceImages" value="{{image}}">
{{ image }}
</md-option>
</md-select>
</md-input-container>
</div>
</div>
<div layout="row">
<div flex="50">
<md-input-container class="md-block" flex-gt-sm>
<label>Terminal Font Size</label>
<md-select ng-model="$ctrl.currentTerminalFontSize" ng-model-options="{getterSetter: true}">
<md-option ng-repeat="size in $ctrl.terminalFontSizes" value="{{size}}">
{{ size }}
</md-option>
</md-select>
</md-input-container>
</div>
</div>
</div>
</md-dialog-content>
<md-dialog-actions layout="row">
<span flex></span>
<md-button ng-click="$ctrl.close()">
Close
</md-button>
</md-dialog-actions>
</script>
<script src="https://unpkg.com/reconnectingwebsocket@1.0.0/reconnecting-websocket.min.js" integrity="sha384-FtJyC+/3fgtPbqlacLHdGwBrmPjKoYBsiqNF5/BEprsnIXB4xtXLCJRx7Xx+TWKP" crossorigin="anonymous"></script>
<script
src="https://code.jquery.com/jquery-3.2.1.min.js"
integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4="
crossorigin="anonymous"></script>
<script src="https://unpkg.com/angular@1.5.5/angular.min.js"></script>
<script src="https://unpkg.com/angular-animate@1.5.5/angular-animate.min.js"></script>
<script src="https://unpkg.com/angular-aria@1.5.5/angular-aria.min.js"></script>
<script src="https://unpkg.com/angular-messages@1.5.5/angular-messages.min.js"></script>
<script src="https://unpkg.com/angular-material@1.1.0/angular-material.min.js"></script>
<script src="https://cdn.rawgit.com/zenorocha/clipboard.js/master/dist/clipboard.min.js"></script>
<script src="https://unpkg.com/ngclipboard@2.0.0/dist/ngclipboard.min.js"></script>
<script src="https://unpkg.com/ng-file-upload@12.2.13/dist/ng-file-upload-all.min.js" integrity="sha384-NbBOS/QuqJqwWOtYg/L3ZDhgl/6GFyvkRMypJQLgoisMPtJiHj5uQ+3bj8V8Muwm" crossorigin="anonymous"></script>
<script src="/assets/xterm/xterm.js"></script>
<script src="/assets/xterm/addons/fit/fit.js"></script>
<script src="/assets/xterm/addons/fullscreen/fullscreen.js"></script>
<script src="/assets/setup-xterm.js"></script>
<script src="/assets/attach.js"></script>
<script src="https://unpkg.com/moment@2.16.0/min/moment.min.js"></script>
<script src="/assets/app.js"></script>
<script type="text/javascript" charset="utf-8">
window.onbeforeunload = function (e) {
e = e || window.event;
// For IE and Firefox prior to version 4
if (e) {
e.returnValue = 'Make sure you saved your session URL';
}
// For Safari
return 'Make sure you saved your session URL';
};
</script>
</body>
</html>

View File

@@ -0,0 +1,171 @@
<!DOCTYPE html>
<html lang="en" ng-app="PWDLanding" ng-controller="LoginController">
<head>
<script src="https://unpkg.com/angular@1.6.6/angular.min.js"></script>
<script src="https://unpkg.com/angular-cookies@1.6.6/angular-cookies.min.js"></script>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<title>Play with Kubernetes</title>
<!-- Bootstrap core CSS -->
<link rel="stylesheet" href="https://unpkg.com/bootstrap@4.0.0-beta/dist/css/bootstrap.min.css" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
<!-- Custom styles for this template -->
<link href="/assets/landing.css" rel="stylesheet">
<script type="text/javascript" src="//cdn.bizible.com/scripts/bizible.js"async=""></script>
</head>
<body>
<div class="container">
<div class="header clearfix">
<nav>
<ul class="nav nav-pills float-right">
<li class="nav-item">
<a class="nav-link" href="https://github.com/play-with-docker/play-with-docker">Contribute</a>
</li>
</ul>
</nav>
</div>
<div class="jumbotron" ng-cloak>
<img src="https://zdnet2.cbsistatic.com/hub/i/r/2015/07/21/bb0de0fc-5d9c-47c3-96dd-42ed50858fdb/resize/370xauto/8999227b80cc063f94a76f2b628b0499/kubernetes-logo.png" />
<h1 class="display-3">Play with Kubernetes</h1>
<p class="lead">A simple, interactive and fun playground to learn Kubernetes</p>
<div ng-hide="loggedIn" class="btn-group" role="group">
<button id="btnGroupDrop1" type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Login
</button>
<div class="dropdown-menu" aria-labelledby="btnGroupDrop1">
<a ng-repeat="provider in providers" class="dropdown-item" ng-click="login(provider)">{{provider}}</a>
</div>
</div>
<form id="landingForm" method="POST" action="/">
<p ng-show="loggedIn"><a class="btn btn-lg btn-success" href="#" ng-click="start()" role="button">Start</a></p>
<input id="stack" type="hidden" name="stack" value=""/>
<input id="stack_name" type="hidden" name="stack_name" value=""/>
<input id="image_name" type="hidden" name="image_name" value=""/>
</form>
</div>
<div class="row marketing">
<div class="col-lg-12">
<p>Play with Kubernetes is a labs site provided by <a href="https://docker.com">Docker</a> and created by Tutorius. Play with Kubernetes is a playground which allows users to run K8s clusters in a matter of seconds. It gives the experience of having a free Alpine Linux Virtual Machine in browser. Under the hood Docker-in-Docker (DinD) is used to give the effect of multiple VMs/PCs.</p>
<p>If you want to learn more about Kubernetes, consider the <a href="https://training.play-with-kubernetes.com/">Play with Kubernetes Classroom</a> which provides more directed learning using an integrated Play with Kubernetes commandline.</p>
</div>
</div>
<footer class="footer">
<p>Your use of Play With Docker is subject to the Docker Terms of Service which can be accessed <a target="_blank" href="https://www.docker.com/legal/docker-terms-service">here</a></p>
<p>Site provided by <a href="https://docker.com">Docker, Inc.</a>, created by Tutorius</p>
</footer>
</div>
[[ if .SegmentId ]]
<script>
!function(){var analytics=window.analytics=window.analytics||[];if(!analytics.initialize)if(analytics.invoked)window.console&&console.error&&console.error("Segment snippet included twice.");else{analytics.invoked=!0;analytics.methods=["trackSubmit","trackClick","trackLink","trackForm","pageview","identify","reset","group","track","ready","alias","debug","page","once","off","on"];analytics.factory=function(t){return function(){var e=Array.prototype.slice.call(arguments);e.unshift(t);analytics.push(e);return analytics}};for(var t=0;t<analytics.methods.length;t++){var e=analytics.methods[t];analytics[e]=analytics.factory(e)}analytics.load=function(t){var e=document.createElement("script");e.type="text/javascript";e.async=!0;e.src=("https:"===document.location.protocol?"https://":"http://")+"cdn.segment.com/analytics.js/v1/"+t+"/analytics.min.js";var n=document.getElementsByTagName("script")[0];n.parentNode.insertBefore(e,n)};analytics.SNIPPET_VERSION="4.0.0";
analytics.load('[[ .SegmentId ]]');
analytics.page();
}}();
</script>
[[ end ]]
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://unpkg.com/popper.js@1.11.0/dist/umd/popper.min.js" integrity="sha384-b/U6ypiBEHpOf/4+1nzFpr53nxSS+GLCkfwBdFNTxtclqqenISfwAzpKaMNFNmj4" crossorigin="anonymous"></script>
<script src="https://unpkg.com/bootstrap@4.0.0-beta/dist/js/bootstrap.min.js" integrity="sha384-h0AbiXch4ZDo7tp9hKZ4TsHbi047NrKGLO3SEJAg45jXxnGIfYzk4Si90RDIqNm1" crossorigin="anonymous"></script>
<script>
angular.module('PWDLanding', [])
.controller('LoginController', ['$scope', '$http', '$window', function($scope, $http, $window) {
$scope.providers = [];
$scope.loggedIn = false;
$scope.user = null;
function checkLoggedIn() {
$http({
method: 'GET',
url: '/users/me'
}).then(function(response) {
[[ if .SegmentId ]]
analytics.identify(response.data.provider_user_id, {"email": response.data.email});
[[ end ]]
$scope.user = response.data;
$scope.loggedIn = true;
}, function(response) {
[[ if .SegmentId ]]
analytics.identify();
[[ end ]]
console.log('ERROR', response);
$scope.user = null;
$scope.loggedIn = false;
});
}
checkLoggedIn();
$http({
method: 'GET',
url: '/oauth/providers'
}).then(function(response) {
$scope.providers = response.data;
if ($scope.providers.length == 0) {
$scope.loggedIn = true;
}
}, function(response) {
console.log('ERROR', response);
});
$scope.login = function(provider) {
var width = screen.width*0.6;
// fixed height as the login window is not responsive
var height = 620;
var x = screen.width/2 - width/2;
var y = screen.height/2 - height/2;
$window.open('/oauth/providers/' + provider + '/login', 'PWDLogin', 'width='+width+',height='+height+',left='+x+',top='+y);
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
var eventer = window[eventMethod];
var messageEvent = eventMethod == "attachEvent" ? "onmessage" : "message";
// Listen to message from child window
eventer(messageEvent,function(e) {
if (e.data === 'done') {
checkLoggedIn();
}
}, false);
}
$scope.start = function() {
function getParameterByName(name, url) {
if (!url) url = window.location.href;
name = name.replace(/[\[\]]/g, "\\$&");
var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return '';
return decodeURIComponent(results[2].replace(/\+/g, " "));
}
var stack = getParameterByName('stack');
if (stack) {
document.getElementById('stack').value = stack;
}
var stackName = getParameterByName('stack_name');
if (stackName) {
document.getElementById('stack_name').value = stackName;
}
var imageName = getParameterByName('image_name');
if (imageName) {
document.getElementById('image_name').value = imageName;
}
document.getElementById('landingForm').submit();
}
}]);
</script>
</body>
</html>

22
handlers/www/ooc.html Normal file
View File

@@ -0,0 +1,22 @@
<!doctype html>
<html>
<head>
<title>Docker Playground</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700,400italic|Material+Icons" />
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/angular_material/1.1.0/angular-material.min.css">
<link rel="stylesheet" href="/assets/style.css" />
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-89019737-1', 'auto');
ga('send', 'pageview');
</script>
</head>
<body>
<div layout="column" style="height:100%;">
We are really sorry but we are out of capacity and cannot create your session at the moment. Please try again later.
</div>
</body>
</html>

2
handlers/www/robots.txt Normal file
View File

@@ -0,0 +1,2 @@
User-agent: *
Disallow: /