1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2024-12-24 23:25:24 +00:00

Unified VNC/SPICE console interfaces with bootstrap

This commit is contained in:
Benjamin Renard 2016-04-18 00:25:46 +02:00
parent 2f63d35804
commit 69b955261f
3 changed files with 374 additions and 388 deletions

View file

@ -0,0 +1,127 @@
{% load i18n %}
<html>
<head>
<meta charset="utf-8">
<link rel="shortcut icon" href="{{ STATIC_URL }}img/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="{{ STATIC_URL }}css/bootstrap.min.css">
<link href="{{ STATIC_URL }}css/webvirtcloud.css" rel="stylesheet">
<style>
body {
margin: 0;
padding: 0;
background-color: #313131;
}
.navbar {
margin-bottom: 2px;
}
#main_container {
padding: 0;
width: 100%;
max-width: none;
height: 100%;
background-color:#494949;
border-bottom-right-radius: 800px 600px;
}
#main_container canvas {
padding-left: 0;
padding-right: 0;
margin-left: auto;
margin-right: auto;
display: block;
margin: auto;
}
#status {
z-index: 10000;
width: 80%;
position: absolute;
top: 5px;
left: 10%;
text-align: center;
}
</style>
{% block head %}{% endblock %}
</head>
<body>
<nav class="navbar navbar-inverse navbar-static-top" role="navigation">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand">{{ instance.name }}</a>
</div>
<div class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">Send key(s) <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li onclick='sendCtrlAltDel();'><a href='#'>Ctrl+Alt+Del</a></li>
<li class="divider"></li>
<li onclick='sendCtrlAltFN(0);'><a href='#'>Ctrl+Alt+F1</a></li>
<li onclick='sendCtrlAltFN(1);'><a href='#'>Ctrl+Alt+F2</a></li>
<li onclick='sendCtrlAltFN(2);'><a href='#'>Ctrl+Alt+F3</a></li>
<li onclick='sendCtrlAltFN(3);'><a href='#'>Ctrl+Alt+F4</a></li>
<li onclick='sendCtrlAltFN(4);'><a href='#'>Ctrl+Alt+F5</a></li>
<li onclick='sendCtrlAltFN(5);'><a href='#'>Ctrl+Alt+F6</a></li>
<li onclick='sendCtrlAltFN(6);'><a href='#'>Ctrl+Alt+F7</a></li>
<li onclick='sendCtrlAltFN(7);'><a href='#'>Ctrl+Alt+F8</a></li>
<li onclick='sendCtrlAltFN(8);'><a href='#'>Ctrl+Alt+F9</a></li>
<li onclick='sendCtrlAltFN(9);'><a href='#'>Ctrl+Alt+F10</a></li>
<li onclick='sendCtrlAltFN(10);'><a href='#'>Ctrl+Alt+F11</a></li>
<li onclick='sendCtrlAltFN(11);'><a href='#'>Ctrl+Alt+F12</a></li>
</ul>
</li>
<li onclick='fullscreen()'><a href='#'>{% trans "Fullscreen" %}</a></li>
{% block navbarmenu %}{% endblock %}
</ul>
</div>
</div>
</nav>
<div id='main_container' class="container">
{% block content %}{% endblock %}
</div>
<script src="{{ STATIC_URL }}js/jquery.js"></script>
<script src="{{ STATIC_URL }}js/bootstrap.min.js"></script>
<script>
function log_message(msg,type) {
var exist=$('#status').is('div');
status_div=$('<div id="status" class="alert alert-'+type+' role="alert"><button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>'+msg+'</div>');
if (exist) {
$('#status').remove();
$('body').prepend(status_div);
}
else {
status_div.hide();
$('body').prepend(status_div);
status_div.fadeIn(200);
}
if (type!='danger') {
status_div.delay(3000).fadeOut(200);
}
}
function log_error(msg) {
log_message(msg,'danger');
}
function log_info(msg) {
log_message(msg,'info');
}
</script>
{% block foot %}{% endblock %}
</body>
</html>

View file

@ -1,158 +1,44 @@
{% extends "console-base.html" %}
{% load i18n %} {% load i18n %}
<html> {% load staticfiles %}
<head> {% block head %}
<link rel="shortcut icon" href="{{ STATIC_URL }}img/favicon.ico"> <script src="{% static "js/spice-html5/spicearraybuffer.js" %}"></script>
<script src="{{ STATIC_URL }}js/spice-html5/spicearraybuffer.js"></script> <script src="{% static "js/spice-html5/enums.js" %}"></script>
<script src="{{ STATIC_URL }}js/spice-html5/enums.js"></script> <script src="{% static "js/spice-html5/atKeynames.js" %}"></script>
<script src="{{ STATIC_URL }}js/spice-html5/atKeynames.js"></script> <script src="{% static "js/spice-html5/utils.js" %}"></script>
<script src="{{ STATIC_URL }}js/spice-html5/utils.js"></script> <script src="{% static "js/spice-html5/png.js" %}"></script>
<script src="{{ STATIC_URL }}js/spice-html5/png.js"></script> <script src="{% static "js/spice-html5/lz.js" %}"></script>
<script src="{{ STATIC_URL }}js/spice-html5/lz.js"></script> <script src="{% static "js/spice-html5/quic.js" %}"></script>
<script src="{{ STATIC_URL }}js/spice-html5/quic.js"></script> <script src="{% static "js/spice-html5/bitmap.js" %}"></script>
<script src="{{ STATIC_URL }}js/spice-html5/bitmap.js"></script> <script src="{% static "js/spice-html5/spicedataview.js" %}"></script>
<script src="{{ STATIC_URL }}js/spice-html5/spicedataview.js"></script> <script src="{% static "js/spice-html5/spicetype.js" %}"></script>
<script src="{{ STATIC_URL }}js/spice-html5/spicetype.js"></script> <script src="{% static "js/spice-html5/spicemsg.js" %}"></script>
<script src="{{ STATIC_URL }}js/spice-html5/spicemsg.js"></script> <script src="{% static "js/spice-html5/wire.js" %}"></script>
<script src="{{ STATIC_URL }}js/spice-html5/wire.js"></script> <script src="{% static "js/spice-html5/spiceconn.js" %}"></script>
<script src="{{ STATIC_URL }}js/spice-html5/spiceconn.js"></script> <script src="{% static "js/spice-html5/display.js" %}"></script>
<script src="{{ STATIC_URL }}js/spice-html5/display.js"></script> <script src="{% static "js/spice-html5/main.js" %}"></script>
<script src="{{ STATIC_URL }}js/spice-html5/main.js"></script> <script src="{% static "js/spice-html5/inputs.js" %}"></script>
<script src="{{ STATIC_URL }}js/spice-html5/inputs.js"></script> <script src="{% static "js/spice-html5/webm.js" %}"></script>
<script src="{{ STATIC_URL }}js/spice-html5/webm.js"></script> <script src="{% static "js/spice-html5/playback.js" %}"></script>
<script src="{{ STATIC_URL }}js/spice-html5/playback.js"></script> <script src="{% static "js/spice-html5/simulatecursor.js" %}"></script>
<script src="{{ STATIC_URL }}js/spice-html5/simulatecursor.js"></script> <script src="{% static "js/spice-html5/cursor.js" %}"></script>
<script src="{{ STATIC_URL }}js/spice-html5/cursor.js"></script> <script src="{% static "js/spice-html5/thirdparty/jsbn.js" %}"></script>
<script src="{{ STATIC_URL }}js/spice-html5/thirdparty/jsbn.js"></script> <script src="{% static "js/spice-html5/thirdparty/rsa.js" %}"></script>
<script src="{{ STATIC_URL }}js/spice-html5/thirdparty/rsa.js"></script> <script src="{% static "js/spice-html5/thirdparty/prng4.js" %}"></script>
<script src="{{ STATIC_URL }}js/spice-html5/thirdparty/prng4.js"></script> <script src="{% static "js/spice-html5/thirdparty/rng.js" %}"></script>
<script src="{{ STATIC_URL }}js/spice-html5/thirdparty/rng.js"></script> <script src="{% static "js/spice-html5/thirdparty/sha1.js" %}"></script>
<script src="{{ STATIC_URL }}js/spice-html5/thirdparty/sha1.js"></script> <script src="{% static "js/spice-html5/ticket.js" %}"></script>
<script src="{{ STATIC_URL }}js/spice-html5/ticket.js"></script> <script src="{% static "js/spice-html5/resize.js" %}"></script>
<script src="{{ STATIC_URL }}js/spice-html5/resize.js"></script> <script src="{% static "js/spice-html5/filexfer.js" %}"></script>
<script src="{{ STATIC_URL }}js/spice-html5/filexfer.js"></script>
<style> {% endblock %}
body {
margin: 0;
padding: 0;
font-family: Helvetica;
background-color:#494949;
}
#status { {% block content %}
text-align: center; <div id='spice_container'></div>
width: 100%; {% endblock %}
margin: 0;
font-size: 1em;
height: 1.6em;
padding-top: 0.3em;
background-color: #222;
}
#spice-area { {% block foot %}
border-bottom-right-radius: 800px 600px; <script>
background-color: #313131;
height: 100%;
}
#spice-screen canvas {
padding-left: 0;
padding-right: 0;
margin-left: auto;
margin-right: auto;
display: block;
}
#spice-menu {
list-style-type: none;
margin: 0px;
padding: 0px;
border: 0px none;
position: absolute;
top: 0px;
z-index: 100;
}
#spice-menu li {
width: auto;
float: left;
margin: 2px;
padding: 0px;
border: 0px none;
position: relative;
height: 18px;
cursor: pointer;
padding: 0.2em;
background: #eee;
border-radius: 3px;
}
#spice-menu .spice-submenu {
display: none;
opacity: 0;
list-style-type: none;
margin: 0;
padding: 0;
border: 0;
overflow: hidden;
position: absolute;
}
#spice-menu .spice-submenu li {
float: none;
margin: 0;
padding: 0;
border: 0;
border-top: 1px solid white;
border-right: 1px solid white;
font: normal 14px sans-serif;
z-index: 100;
white-space: nowrap;
min-width: 100px;
padding: 0.2em;
border-radius: 0;
}
#spice-menu li:hover > .spice-submenu {
opacity: 1;
display: block;
margin: 2px 0 0 -2px;
}
#spice-menu li:hover > .spice-submenu li {
height: auto;
}
</style>
</head>
<body>
<div id="header">
<ul id='spice-menu'>
<li>Send key(s)
<ul class='spice-submenu'>
<li onclick='sendCtrlAltDel();'>Ctrl+Alt+Del</li>
<li onclick='sendCtrlAltFN(0);'>Ctrl+Alt+F1</li>
<li onclick='sendCtrlAltFN(1);'>Ctrl+Alt+F2</li>
<li onclick='sendCtrlAltFN(2);'>Ctrl+Alt+F3</li>
<li onclick='sendCtrlAltFN(3);'>Ctrl+Alt+F4</li>
<li onclick='sendCtrlAltFN(4);'>Ctrl+Alt+F5</li>
<li onclick='sendCtrlAltFN(5);'>Ctrl+Alt+F6</li>
<li onclick='sendCtrlAltFN(6);'>Ctrl+Alt+F7</li>
<li onclick='sendCtrlAltFN(7);'>Ctrl+Alt+F8</li>
<li onclick='sendCtrlAltFN(8);'>Ctrl+Alt+F9</li>
<li onclick='sendCtrlAltFN(9);'>Ctrl+Alt+F10</li>
<li onclick='sendCtrlAltFN(10);'>Ctrl+Alt+F11</li>
<li onclick='sendCtrlAltFN(11);'>Ctrl+Alt+F12</li>
</ul>
</li>
<li onclick='fullscreen()'>Fullscreen</li>
</ul>
<div id="status"></div>
</div>
<div id="spice-area">
<div id="spice-screen" class="spice-screen"></div>
</div>
<script>
var sc; var sc;
function spice_set_cookie(name, value, days) { function spice_set_cookie(name, value, days) {
@ -171,28 +57,6 @@
: defvalue; : defvalue;
} }
var status_div=false;
function log_message(msg,color,bgcolor) {
if (!status_div) {
status_div=document.getElementById('status');
}
status_div.innerHTML=msg;
if (color) {
status_div.style.color=color;
}
if (bgcolor) {
status_div.style.backgroundColor=bgcolor;
}
}
function log_error(msg) {
log_message(msg,'#000','#f44');
}
function log_info(msg) {
log_message(msg,'#000','#eee');
}
function spice_error(e) function spice_error(e)
{ {
console.log(e); console.log(e);
@ -224,7 +88,7 @@
try try
{ {
sc = new SpiceMainConn({uri: uri, password: password, screen_id: "spice-screen", sc = new SpiceMainConn({uri: uri, password: password, screen_id: "spice_container",
onsuccess: spice_success, onerror: spice_error, onagent: agent_connected }); onsuccess: spice_success, onerror: spice_error, onagent: agent_connected });
} }
catch (e) catch (e)
@ -259,7 +123,6 @@
} }
function agent_connected(sc) { function agent_connected(sc) {
console.log('Connected');
window.addEventListener('resize', handle_resize); window.addEventListener('resize', handle_resize);
window.spice_connection = this; window.spice_connection = this;
@ -278,7 +141,6 @@
console.log("File API is not supported"); console.log("File API is not supported");
log_info('Drag and drop transfer not supported.'); log_info('Drag and drop transfer not supported.');
} }
log_info('Connected');
} }
function sendCtrlAltFN(f) { function sendCtrlAltFN(f) {
@ -306,7 +168,7 @@
} }
function fullscreen() { function fullscreen() {
var screen=document.getElementById('spice-screen'); var screen=document.getElementById('spice_container');
if(screen.requestFullscreen) { if(screen.requestFullscreen) {
screen.requestFullscreen(); screen.requestFullscreen();
} else if(screen.mozRequestFullScreen) { } else if(screen.mozRequestFullScreen) {
@ -328,6 +190,5 @@
var password = '{{ console_passwd }}'; var password = '{{ console_passwd }}';
log_info('Connecting ...'); log_info('Connecting ...');
connect(uri,password); connect(uri,password);
</script> </script>
</body> {% endblock %}
</html>

View file

@ -1,26 +1,23 @@
{% extends "console-base.html" %}
{% load i18n %} {% load i18n %}
<html> {% load staticfiles %}
<head> {% block head %}
<link rel="shortcut icon" href="{{ STATIC_URL }}img/favicon.ico"> <script src="{% static "js/novnc/util.js" %}"></script>
<link rel="stylesheet" href="{{ STATIC_URL }}js/novnc/base.css" title="plain">
<!-- {% endblock %}
<script type='text/javascript'
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script> {% block navbarmenu %}
-->
<script src="{{ STATIC_URL }}js/novnc/util.js"></script>
</head>
<body style="margin: 0px;">
<div id="noVNC_screen">
<div id="noVNC_status_bar" class="noVNC_status_bar" style="margin-top: 0px;">
<table border=0 width="100%">
<tr>
<td>
<div id="noVNC_status">{% trans "Loading..." %}</div>
</td>
<td width="32%" style="text-align:right;">
<div id="noVNC_buttons">
<!-- dirty fix for keyboard on iOS devices --> <!-- dirty fix for keyboard on iOS devices -->
<input type="button" id="showKeyboard" value="Keyboard" title="Show Keyboard"/> <li id="showKeyboard"><a href='#'>{% trans "Show Keyboad" %}</a></li>
{% endblock %}
{% block content %}
<div id='noVNC_area'>
<canvas id="noVNC_canvas" width="640px" height="20px">
{% trans "Canvas not supported." %}
</canvas>
</div>
<!-- Note that Google Chrome on Android doesn't respect any of these, <!-- Note that Google Chrome on Android doesn't respect any of these,
html attributes which attempt to disable text suggestions on the html attributes which attempt to disable text suggestions on the
on-screen keyboard. Let's hope Chrome implements the ime-mode on-screen keyboard. Let's hope Chrome implements the ime-mode
@ -29,20 +26,12 @@
<textarea id="keyboardinput" autocapitalize="off" <textarea id="keyboardinput" autocapitalize="off"
autocorrect="off" autocomplete="off" spellcheck="false" autocorrect="off" autocomplete="off" spellcheck="false"
mozactionhint="Enter" onsubmit="return false;" mozactionhint="Enter" onsubmit="return false;"
style="ime-mode: disabled;"> style="display: none;">
</textarea> </textarea>
<input type=button value="Ctrl+Alt+Del" id="sendCtrlAltDelButton"> {% endblock %}
<input type=button value="Fullscreen" id="askFullscreen">
</div> {% block foot %}
</td>
</tr>
</table>
</div>
<canvas id="noVNC_canvas" width="640px" height="20px">
{% trans "Canvas not supported." %}
</canvas>
</div>
<script> <script>
/*jslint white: false */ /*jslint white: false */
/*global window, $, Util, RFB, */ /*global window, $, Util, RFB, */
@ -60,18 +49,32 @@
var rfb; var rfb;
function passwordRequired(rfb) { function passwordRequired(rfb) {
var msg; var modal;
msg = '<form onsubmit="return setPassword();"'; modal = '<div class="modal fade">';
msg += 'role="form"'; modal += ' <div class="modal-dialog">';
msg += ' style="margin-bottom: 0px">'; modal += ' <div class="modal-content">';
msg += 'Password Required: '; modal += ' <div class="modal-header">';
msg += '<input type=password size=10 id="password_input" class="noVNC_status">'; modal += ' <h4 class="modal-title">{% trans "Password required" %}</h4>';
msg += '<\/form>'; modal += ' </div>';
$D('noVNC_status_bar').setAttribute("class", "noVNC_status_warn"); modal += ' <div class="modal-body">';
$D('noVNC_status').innerHTML = msg; modal += ' <form id="password_form" onsubmit="return setPassword();">';
modal += ' <div class="form-group">';
modal += ' <label for="password_input">Password</label>';
modal += ' <input type="password" class="form-control" id="password_input" placeholder="Password"/>';
modal += ' </div>';
modal += ' </form>';
modal += ' </div>';
modal += ' <div class="modal-footer">';
modal += ' <button type="button" class="btn btn-primary" data-dismiss="modal" onclick="return setPassword();">{% trans "OK" %}</button>';
modal += ' </div>';
modal += ' </div>';
modal += ' </div>';
modal += '</div>';
$('body').append(modal);
$('div.modal').modal();
} }
function setPassword() { function setPassword() {
rfb.sendPassword($D('password_input').value); rfb.sendPassword($('#password_input').val());
return false; return false;
} }
function sendCtrlAltDel() { function sendCtrlAltDel() {
@ -79,6 +82,19 @@
return false; return false;
} }
function sendCtrlAltFN(f) {
var keys_code=[0xFFBE,0xFFBF,0xFFC0,0xFFC1,0xFFC2,0xFFC3,0xFFC4,0xFFC5,0xFFC6,0xFFC7,0xFFC8,0xFFC9];
if (keys_code[f]==undefined) {
return;
}
rfb.sendKey(0xFFE3, 'down');
rfb.sendKey(0xFFE9, 'down');
rfb.sendKey(keys_code[f], 'down');
rfb.sendKey(keys_code[f], 'up');
rfb.sendKey(0xFFE9, 'up');
rfb.sendKey(0xFFE3, 'up');
};
// dirty fix for keyboard on iOS devices // dirty fix for keyboard on iOS devices
function showKeyboard() { function showKeyboard() {
var kbi, skb, l; var kbi, skb, l;
@ -103,48 +119,36 @@
function updateState(rfb, state, oldstate, msg) { function updateState(rfb, state, oldstate, msg) {
var s, sb, cad, af, level; var s, sb, cad, af, level;
s = $D('noVNC_status');
sb = $D('noVNC_status_bar');
cad = $D('sendCtrlAltDelButton'); cad = $D('sendCtrlAltDelButton');
af = $D('askFullscreen'); af = $D('askFullscreen');
switch (state) { switch (state) {
case 'failed': case 'failed':
level = "error"; level = "danger";
break; break;
case 'fatal': case 'fatal':
level = "error"; level = "danger";
break; break;
case 'normal': case 'normal':
level = "normal"; level = "info";
break; break;
case 'disconnected': case 'disconnected':
level = "normal"; level = "info";
break; break;
case 'loaded': case 'loaded':
level = "normal"; level = "info";
break; break;
default: default:
level = "warn"; level = "warning";
break; break;
} }
if (state === "normal") {
cad.disabled = false;
af.disabled = false;
}
else {
cad.disabled = true;
af.disabled = true;
}
if (typeof(msg) !== 'undefined') { if (typeof(msg) !== 'undefined') {
sb.setAttribute("class", "noVNC_status_" + level); log_message(msg,level);
s.innerHTML = msg;
} }
} }
function fullscreen() { function fullscreen() {
var screen=document.getElementById('noVNC_canvas'); var screen=document.getElementById('main_container');
if(screen.requestFullscreen) { if(screen.requestFullscreen) {
screen.requestFullscreen(); screen.requestFullscreen();
} else if(screen.mozRequestFullScreen) { } else if(screen.mozRequestFullScreen) {
@ -159,11 +163,6 @@
window.onscriptsload = function () { window.onscriptsload = function () {
var host, port, password, path, token; var host, port, password, path, token;
$D('sendCtrlAltDelButton').style.display = "inline";
$D('sendCtrlAltDelButton').onclick = sendCtrlAltDel;
$D('askFullscreen').style.display = "inline";
$D('askFullscreen').onclick = fullscreen;
// dirty fix for keyboard on iOS devices // dirty fix for keyboard on iOS devices
if (isTouchDevice) { if (isTouchDevice) {
$D('showKeyboard').onclick = showKeyboard; $D('showKeyboard').onclick = showKeyboard;
@ -188,7 +187,7 @@
return; return;
} }
rfb = new RFB({'target': $D('noVNC_canvas'), rfb = new RFB({'target': document.getElementById('noVNC_canvas'),
'encrypt': WebUtil.getQueryVar('encrypt', 'encrypt': WebUtil.getQueryVar('encrypt',
(window.location.protocol === "https:")), (window.location.protocol === "https:")),
'repeaterID': WebUtil.getQueryVar('repeaterID', ''), 'repeaterID': WebUtil.getQueryVar('repeaterID', ''),
@ -201,5 +200,4 @@
rfb.connect(host, port, password, path); rfb.connect(host, port, password, path);
}; };
</script> </script>
</body> {% endblock %}
</html>