integrated webserver ... (#697)

* initial commit of webconfig

* update example config with webconfig and fix format of file
update debian postinst script for install example config
pull/698/head^2
redPanther 7 years ago committed by brindosch
parent d2f47251f5
commit 7dfb9f1967

@ -116,10 +116,10 @@ find_package(GitVersion)
configure_file("${PROJECT_SOURCE_DIR}/HyperionConfig.h.in" "${PROJECT_BINARY_DIR}/HyperionConfig.h")
include_directories("${PROJECT_BINARY_DIR}")
if(ENABLE_QT5)
ADD_DEFINITIONS ( -DENABLE_QT5 )
if( NOT ENABLE_QT5)
#ADD_DEFINITIONS ( -DENABLE_QT5 )
#find_package(Qt5Widgets)
else()
#else()
# Add specific cmake modules to find qt4 (default version finds first available QT which might not be qt4)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/qt4)
endif()

@ -32,6 +32,7 @@
// Define to enable profiler for development purpose
#cmakedefine ENABLE_PROFILER
#cmakedefine ENABLE_QT5
// the hyperion build id string
#define HYPERION_VERSION_ID "${HYPERION_VERSION_ID}"

@ -0,0 +1,609 @@
@font-face {
font-family: 'fontello';
src: url('../res/fontello.ttf') format('truetype'), url('../res/fontello.woff') format('woff');
font-weight: normal;
font-style: normal;
}
html, body {
height: 100%;
width: 100%;
padding: 0;
margin: 0;
color: #A6B4B4;
background-color: #2C2C2C;
}
* {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-box-sizing: border-box;
box-sizing: border-box;
-webkit-touch-callout: none;
-webkit-text-size-adjust: none;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
font-family: 'Lucida Grande', Helvetica, Arial, Roboto, serif;
}
#app {
height: 100%;
width: 100%;
display: -webkit-flex;
-webkit-flex-direction: column;
-webkit-flex-wrap: nowrap;
display: flex;
flex-direction: column;
flex-wrap: nowrap;
background-color: #2C2C2C;
min-width: 320px;
min-height: 460px;
}
.work {
-webkit-flex: 1;
flex: 1;
position: relative;
overflow: hidden;
}
.footer {
border-top: 1px solid #919F9F;
display: -webkit-flex;
-webkit-justify-content: space-around;
display: flex;
justify-content: space-around;
}
.footer .button {
width: 100%;
text-align: center;
position: relative;
padding: 5px;
}
.touchrect {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 10;
}
.icon {
font-family: "fontello";
}
.footer .button .icon {
font-size: 25px;
margin-top: 8px;
}
.footer .button .title {
font-size: 12px;
margin-top: 8px;
margin-bottom: 8px;
}
.footer .button:active,
.footer .button.selected {
color: #FFFFFF;
}
.container {
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 400%;
display: flex;
display: -webkit-flex;
-webkit-transition: left 0.5s ease-in-out;
transition: left 0.5s ease-in-out;
}
.contentarea {
width: 100%;
height: 100%;
overflow-y: auto;
-ms-overflow-y: auto;
-ms-overflow-style: -ms-autohiding-scrollbar;
position: relative;
-webkit-overflow-scrolling: touch;
}
#color {
display: -webkit-flex;
-webkit-flex-direction: column;
display: flex;
flex-direction: column;
}
#colorpicker {
position: relative;
-webkit-flex: 1;
flex: 1;
}
#colorwheelbg {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
}
#pointer {
width: 30px;
height: 30px;
position: absolute;
border: 2px solid #FFFFFF;
border-radius: 15px;
left: calc(50% - 15px);
top: calc(50% - 15px);
}
ul {
list-style: none;
padding: 0;
margin: 0;
}
li {
display: block;
}
.horizontal {
display: -webkit-flex;
-webkit-flex: 1;
display: flex;
flex: 1;
position: relative;
}
li .delete_icon {
font-family: "fontello";
font-size: 20px;
color: red;
height: 40px;
width: 40px;
line-height: 40px;
text-align: center;
}
li .edit_icon {
font-family: "fontello";
font-size: 20px;
height: 40px;
width: 40px;
line-height: 40px;
text-align: center;
}
.locked .edit_icon,
.locked .delete_icon {
display: none;
}
li .titlebox {
-webkit-flex: 1;
flex: 1;
}
.titlebox label {
display: block;
line-height: 20px;
height: 20px;
}
.titlebox label.title {
font-size: 16px;
}
.titlebox label.subtitle {
font-size: 10px;
}
/*
li:not(:last-child) {
border-bottom: 1px solid #919F9F;
}
*/
li {
border-bottom: 1px solid #919F9F;
}
li input[type=range] {
width: 100%;
}
#transform li {
padding: 0;
}
#transform .icon {
width: 40px;
text-align: center;
}
.group,
.grouplist {
margin-top: 10px;
}
.grouplist .header {
display: flex;
justify-content: space-between;
border-bottom: 1px solid gray;
align-items: center;
padding-left: 15px;
font-size: 16px;
display: -webkit-flex;
-webkit-justify-content: space-between;
-webkit-align-items: center;
}
.group ul,
.grouplist ul {
overflow-y: hidden;
-webkit-transition: all 0.5s ease-in-out;
transition: all 0.5s ease-in-out;
}
.group[collapsed=true] ul {
max-height: 0;
}
.group li label {
margin: 4px 0 0 50px;
display: block;
background: none;
}
.group > .header {
border-bottom: 1px solid gray;
padding: 3px 0 3px 10px;
position: relative;
}
.group > .header label {
display: block;
background: none;
}
.group > .header label:first-child {
font-size: 16px;
}
.group > .header label:last-child {
font-size: 12px;
}
.group > .header label:first-child:after {
font-family: "fontello";
content: '\e808';
font-size: 20px;
transition: all 0.5s ease-in-out;
-webkit-transform-origin: 50% 33%;
-webkit-transform: rotateZ(180deg);
transform-origin: 50% 33%;
transform: rotateZ(180deg);
-ms-transform-origin: 50% 33%;
-ms-transform: rotateZ(180deg);
position: absolute;
right: 24px;
top: 0;
bottom: 0;
}
.grouplist .header .callout {
font-family: "fontello";
font-size: 20px;
padding: 10px;
}
.grouplist .header .callout:active {
color: #FFFFFF;
}
.group[collapsed=true] > .header label:first-child:after {
-webkit-transform: rotateZ(0deg);
-ms-transform: rotateZ(0deg);
transform: rotateZ(0deg);
}
.wrapper {
display: flex;
align-items: center;
display: -webkit-flex;
-webkit-align-items: center;
}
.wrapper .value {
width: 40px;
background: transparent;
border: 0;
color: #FFFFFF;
margin: auto 5px;
}
#transform .group .slider {
-webkit-flex: 1;
flex: 1;
width: 100%;
}
.slider {
position: relative;
margin-left: 20px;
margin-right: 20px;
}
.slider .track {
border-radius: 4px;
height: 4px;
border: 1px solid #BDC3C7;
background-color: #FFFFFF;
}
.slider .thumb {
box-sizing: border-box;
border-radius: 10px;
height: 20px;
width: 20px;
border: 2px solid #BDC3C7;
background-color: #FFFFFF;
position: absolute;
top: -8px;
margin-left: -10px;
}
.wrapper .icon {
display: block;
font-size: 30px;
}
.red .icon {
color: #FF0000;
}
.green .icon {
color: #00FF00;
}
.blue .icon {
color: #0000FF;
}
.msg {
position: relative;
margin: auto;
padding: 5px 10px;
font-size: 12px;
font-weight: bold;
}
.error {
background-color: #D70000;
color: #FFFFFF;
}
.status {
background-color: #00A200;
color: #FFFFFF;
}
.wrapper_msg {
position: absolute;
bottom: 5px;
width: 100%;
display: -webkit-flex;
display: flex;
visibility: visible;
opacity: 1;
-webkit-transition: opacity 0.2s linear;
transition: opacity 0.2s linear;
}
.invisible {
opacity: 0;
visibility: hidden;
}
.hidden {
display: none !important;
}
.inputline {
display: -webkit-flex;
-webkit-justify-content: space-between;
-webkit-align-items: center;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 14px;
padding: 6px 10px;
}
#settings .inputline input {
background: none;
border: 1px solid rgba(100, 100, 100, 0.4);
height: 100%;
color: white;
text-align: right;
font-size: inherit;
-webkit-user-select: text;
-moz-user-select: text;
-ms-user-select: text;
user-select: text;
outline: none;
}
#settings .inputline input:focus {
color: white;
}
.line {
width: 100%;
position: relative;
padding: 5px 0;
}
button {
margin: auto 10px;
border: 1px #A6B4B4 solid;
background: none;
color: #A6B4B4;
padding: 6px 20px;
}
button:active {
border: 1px #FFFFFF solid;
background-color: transparent;
color: #FFFFFF;
outline: none;
}
button:focus {
outline: none;
}
.spinner {
height: 20px;
width: 20px;
display: inline-flex;
display: -webkit-inline-flex;
-webkit-animation: rotation .8s infinite linear;
animation: rotation .8s infinite linear;
border: 6px inset #D7D7D7;
border-radius: 50%;
float: right;
margin-right: 15px;
}
@-webkit-keyframes rotation {
from {
-ms-transform: rotate(0deg);
}
to {
-ms-transform: rotate(359deg);
}
}
@-webkit-keyframes rotation {
from {
-webkit-transform: rotate(0deg);
}
to {
-webkit-transform: rotate(359deg);
}
}
@keyframes rotation {
from {
transform: rotate(0deg);
}
to {
transform: rotate(359deg);
}
}
li.selected {
background-color: rgba(100, 100, 100, 0.5);
}
#effects li {
height: 40px;
line-height: 40px;
padding-left: 20px;
}
.info {
font-size: 16px;
position: absolute;
top: 50%;
width: 100%;
text-align: center;
}
#color #buttonctrl {
display: -webkit-flex;
-webkit-justify-content: space-between;
-webkit-align-items: flex-end;
display: flex;
justify-content: space-between;
align-items: flex-end;
margin: 10px auto 35px auto;
}
#color #buttonctrl > .icon {
font-size: 27px;
line-height: 100%;
padding: 0 20px;
}
#color #buttonctrl > .icon:active {
color: #FFFFFF;
}
#color .slider {
position: relative;
width: 70%;
margin: 10px auto;
height: 20px;
}
#color .slider .track {
width: 100%;
height: 100%;
background-image: -webkit-linear-gradient(left, #000000 0%, #FFFFFF 100%);
background-image: linear-gradient(to right, #000000 0%, #FFFFFF 100%);
}
#color .slider .thumb {
background: transparent;
border: 3px solid rgba(255, 255, 255, 1.0);
width: 14px;
border-radius: 4px;
height: 28px;
position: absolute;
top: -4px;
margin-left: -7px;
}
#color .value {
outline: none;
border: 1px solid white;
font-family: Monaco, monospace;
text-align: center;
background: transparent;
width: 100px;
color: white;
font-size: 16px;
display: block;
-webkit-user-select: text;
-moz-user-select: text;
-ms-user-select: text;
user-select: text;
}
.checkbox::before {
display: table-cell;
font-family: "fontello";
content: '\e803';
height: 40px;
width: 40px;
font-size: 20px;
vertical-align: middle;
text-align: center;
}
.selected .checkbox::before {
content: '\e802';
}

@ -0,0 +1,69 @@
<!DOCTYPE html>
<html manifest="manifest.appcache">
<head>
<title>Hyperion remote control</title>
<meta charset="utf-8"/>
<meta name="format-detection" content="telephone=no"/>
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width"/>
<link href="css/index.css" rel="stylesheet">
<link rel="apple-touch-icon" href="res/icon_128.png">
<meta name="apple-mobile-web-app-capable" content="yes">
</head>
<body>
<div id="app">
<div class="work">
<div class="container">
<div id="color" class="contentarea">
<div id="colorpicker">
<img id="colorwheelbg" src="res/colorwheel.png" width="auto" height="90%"/>
<div id="pointer"></div>
<div class="touchrect"></div>
</div>
<div class="slider" id="brightness">
<div class="track"></div>
<div class="thumb"></div>
</div>
<div id="buttonctrl">
<div class="icon" id="clear_button">&#xe807;</div>
<input type="text" class="value" autocomplete="off" autocorrect="off" autocapitalize="off"/>
<div class="icon" id="clearall_button">&#xe617;</div>
</div>
</div>
<div id="effects" class="contentarea">
<label class="info">Need server connection to fetch list.</label>
<ul></ul>
</div>
<div id="transform" class="contentarea">
<label class="info">Need server connection to fetch list.</label>
<div class="values"></div>
</div>
<div id="settings" class="contentarea">
</div>
</div>
</div>
<div class="footer">
<div class="button selected" id="colorButton" data-area="color">
<div class="icon">&#xe800;</div>
<div class="title">Color</div>
<div class="touchrect"></div>
</div>
<div class="button" id="effectsButton" data-area="effects">
<div class="icon">&#xe602;</div>
<div class="title">Effects</div>
<div class="touchrect"></div>
</div>
<div class="button" id="thresholdButton" data-area="transform">
<div class="icon">&#xe601;</div>
<div class="title">Transform</div>
<div class="touchrect"></div>
</div>
<div class="button" id="settingsButton" data-area="settings">
<div class="icon">&#x6e;</div>
<div class="title">Settings</div>
<div class="touchrect"></div>
</div>
</div>
</div>
<script src="js/vendor/require.js" data-main="js/app/main"></script>
</body>
</html>

@ -0,0 +1,37 @@
/*global define, chrome */
define(['api/LocalStorage'], function (LocalStorage) {
'use strict';
return LocalStorage.subclass(/** @lends ChromeLocalStorage.prototype */{
/**
* @class ChromeLocalStorage
* @classdesc Chrome's persistent storage
* @constructs
* @extends LocalStorage
*/
constructor: function () {
},
get: function () {
chrome.storage.local.get('data', function (entry) {
if (chrome.runtime.lastError) {
this.emit('error', chrome.runtime.lastError.message);
} else {
this.emit('got', entry.data);
}
}.bind(this));
},
set: function (data) {
var entry = {};
entry.data = data;
chrome.storage.local.set(entry, function () {
if (chrome.runtime.lastError) {
this.emit('error', chrome.runtime.lastError.message);
} else {
this.emit('set');
}
}.bind(this));
}
});
});

@ -0,0 +1,57 @@
/*global chrome */
define(['api/Network'], function (Network) {
'use strict';
return Network.subclass(/** @lends ChromeNetwork.prototype */{
/**
* @class ChromeNetwork
* @extends Network
* @classdesc Network functions for chrome apps
* @constructs
*/
constructor: function () {
},
/**
* @overrides
* @param onSuccess
* @param onError
*/
getLocalInterfaces: function (onSuccess, onError) {
var ips = [];
chrome.system.network.getNetworkInterfaces(function (networkInterfaces) {
var i;
if (chrome.runtime.lastError) {
if (onError) {
onError('Could not get network interfaces');
}
return;
}
for (i = 0; i < networkInterfaces.length; i++) {
// check only ipv4
if (networkInterfaces[i].address.indexOf('.') === -1) {
continue;
}
ips.push(networkInterfaces[i].address);
}
if (onSuccess) {
onSuccess(ips);
}
});
},
/**
* @overrides
* @return {boolean}
*/
canDetectLocalAddress: function () {
return true;
}
}, true);
});

@ -0,0 +1,307 @@
/*global chrome */
define(['lib/stapes', 'api/Socket', 'utils/Tools'], function (Stapes, Socket, tools) {
'use strict';
return Socket.subclass(/** @lends ChromeTcpSocket.prototype */{
DEBUG: false,
/**
* @type {number}
*/
handle: null,
/**
* @type {function}
*/
currentResponseCallback: null,
/**
* @type {function}
*/
currentErrorCallback: null,
/**
* Temporary buffer for incoming data
* @type {Uint8Array}
*/
inputBuffer: null,
inputBufferIndex: 0,
readBufferTimerId: null,
/**
* @class ChromeTcpSocket
* @extends Socket
* @constructs
*/
constructor: function () {
this.inputBuffer = new Uint8Array(4096);
this.inputBufferIndex = 0;
chrome.sockets.tcp.onReceive.addListener(this.onDataReceived.bind(this));
chrome.sockets.tcp.onReceiveError.addListener(this.onError.bind(this));
},
create: function (onSuccess, onError) {
if (this.DEBUG) {
console.log('[DEBUG] Creating socket...');
}
chrome.sockets.tcp.create({bufferSize: 4096}, function (createInfo) {
if (this.DEBUG) {
console.log('[DEBUG] Socket created: ' + createInfo.socketId);
}
this.handle = createInfo.socketId;
if (onSuccess) {
onSuccess();
}
}.bind(this));
},
isConnected: function (resultCallback) {
if (this.DEBUG) {
console.log('[DEBUG] Checking if socket is connected...');
}
if (!this.handle) {
if (this.DEBUG) {
console.log('[DEBUG] Socket not created');
}
if (resultCallback) {
resultCallback(false);
}
return;
}
chrome.sockets.tcp.getInfo(this.handle, function (socketInfo) {
if (this.DEBUG) {
console.log('[DEBUG] Socket connected: ' + socketInfo.connected);
}
if (socketInfo.connected) {
if (resultCallback) {
resultCallback(true);
}
} else {
if (resultCallback) {
resultCallback(false);
}
}
}.bind(this));
},
connect: function (server, onSuccess, onError) {
var timeoutHandle;
if (this.DEBUG) {
console.log('[DEBUG] Connecting to peer ' + server.address + ':' + server.port);
}
if (!this.handle) {
if (this.DEBUG) {
console.log('[DEBUG] Socket not created');
}
if (onError) {
onError('Socket handle is invalid');
}
return;
}
// FIXME for some reason chrome blocks if peer is not reachable
timeoutHandle = setTimeout(function () {
chrome.sockets.tcp.getInfo(this.handle, function (socketInfo) {
if (!socketInfo.connected) {
// let the consumer decide if to close or not?
// this.close();
onError('Could not connect to ' + server.address + ':' + server.port);
}
}.bind(this));
}.bind(this), 500);
chrome.sockets.tcp.connect(this.handle, server.address, server.port, function (result) {
if (this.DEBUG) {
console.log('[DEBUG] Connect result: ' + result);
}
clearTimeout(timeoutHandle);
if (chrome.runtime.lastError) {
if (onError) {
onError('Could not connect to ' + server.address + ':' + server.port);
}
return;
}
if (result !== 0) {
if (onError) {
onError('Could not connect to ' + server.address + ':' + server.port);
}
} else if (onSuccess) {
onSuccess();
}
}.bind(this));
},
close: function (onSuccess, onError) {
if (this.DEBUG) {
console.log('[DEBUG] Closing socket...');
}
if (this.handle) {
chrome.sockets.tcp.close(this.handle, function () {
this.handle = null;
if (onSuccess) {
onSuccess();
}
}.bind(this));
} else {
if (this.DEBUG) {
console.log('[DEBUG] Socket not created');
}
if (onError) {
onError('Socket handle is invalid');
}
}
},
write: function (data, onSuccess, onError) {
var dataToSend = null, dataType = typeof (data);
if (this.DEBUG) {
console.log('[DEBUG] writing to socket...');
}
if (!this.handle) {
if (this.DEBUG) {
console.log('[DEBUG] Socket not created');
}
if (onError) {
onError('Socket handle is invalid');
}
return;
}
this.isConnected(function (connected) {
if (connected) {
if (dataType === 'string') {
if (this.DEBUG) {
console.log('> ' + data);
}
dataToSend = tools.str2ab(data);
} else {
if (this.DEBUG) {
console.log('> ' + tools.ab2hexstr(data));
}
dataToSend = data;
}
chrome.sockets.tcp.send(this.handle, tools.a2ab(dataToSend), function (sendInfo) {
if (this.DEBUG) {
console.log('[DEBUG] Socket write result: ' + sendInfo.resultCode);
}
if (sendInfo.resultCode !== 0) {
onError('Socket write error: ' + sendInfo.resultCode);
} else if (onSuccess) {
onSuccess();
}
}.bind(this));
} else {
if (onError) {
onError('No connection to peer');
}
}
}.bind(this));
},
read: function (onSuccess, onError) {
if (this.DEBUG) {
console.log('[DEBUG] reading from socket...');
}
if (!this.handle) {
if (this.DEBUG) {
console.log('[DEBUG] socket not created');
}
if (onError) {
onError('Socket handle is invalid');
}
return;
}
this.isConnected(function (connected) {
if (!connected) {
this.currentResponseCallback = null;
this.currentErrorCallback = null;
if (onError) {
onError('No connection to peer');
}
}
}.bind(this));
if (onSuccess) {
this.currentResponseCallback = onSuccess;
}
if (onError) {
this.currentErrorCallback = onError;
}
},
/**
* Data receiption callback
* @private
* @param info
*/
onDataReceived: function (info) {
if (this.DEBUG) {
console.log('[DEBUG] received data...');
}
if (info.socketId === this.handle && info.data) {
if (this.readBufferTimerId) {
clearTimeout(this.readBufferTimerId);
}
if (this.readTimeoutTimerId) {
clearTimeout(this.readTimeoutTimerId);
this.readTimeoutTimerId = null;
}
this.inputBuffer.set(new Uint8Array(info.data), this.inputBufferIndex);
this.inputBufferIndex += info.data.byteLength;
if (this.DEBUG) {
console.log('< ' + tools.ab2hexstr(info.data));
}
if (this.currentResponseCallback) {
this.readBufferTimerId = setTimeout(function () {
this.currentResponseCallback(this.inputBuffer.subarray(0, this.inputBufferIndex));
this.inputBufferIndex = 0;
this.currentResponseCallback = null;
}.bind(this), 200);
}
}
},
/**
* Error callback
* @private
* @param info
*/
onError: function (info) {
if (this.DEBUG) {
console.log('[ERROR]: ' + info.resultCode);
}
if (info.socketId === this.handle) {
if (this.currentErrorCallback) {
this.currentErrorCallback(info.resultCode);
this.currentErrorCallback = null;
}
}
}
}, true);
});

@ -0,0 +1,49 @@
/*global define */
define(['lib/stapes'], function (Stapes) {
'use strict';
return Stapes.subclass(/** @lends LocalStorage.prototype */{
/**
* @class LocalStorage
* @classdesc LocalStorage handler using HTML5 localStorage
* @constructs
*
* @fires got
* @fires error
* @fires set
*/
constructor: function () {
},
/**
* Gets stored data
*/
get: function () {
var data;
if (!window.localStorage) {
this.emit('error', 'Local Storage not supported');
return;
}
if (localStorage.data) {
data = JSON.parse(localStorage.data);
this.emit('got', data);
}
},
/**
* Stores settings
* @param {object} data - Data object to store
*/
set: function (data) {
if (!window.localStorage) {
this.emit('error', 'Local Storage not supported');
return;
}
localStorage.data = JSON.stringify(data);
this.emit('set');
}
});
});

@ -0,0 +1,57 @@
/*global define */
define(['lib/stapes'], function (Stapes) {
'use strict';
return Stapes.subclass(/** @lends Network.prototype */{
detectTimerId: null,
/**
* @class Network
* @classdesc Empty network functions handler
* @constructs
*/
constructor: function () {
},
/**
* Returns the list of known local interfaces (ipv4)
* @param {function(string[])} [onSuccess] - Callback to call on success
* @param {function(error:string)} [onError] - Callback to call on error
*/
getLocalInterfaces: function (onSuccess, onError) {
var ips = [], RTCPeerConnection;
// https://developer.mozilla.org/de/docs/Web/API/RTCPeerConnection
RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection || window.msRTCPeerConnection;
var rtc = new RTCPeerConnection({iceServers: []});
rtc.onicecandidate = function (event) {
var parts;
if (this.detectTimerId) {
clearTimeout(this.detectTimerId);
}
if (event.candidate) {
parts = event.candidate.candidate.split(' ');
if (ips.indexOf(parts[4]) === -1) {
console.log(event.candidate);
ips.push(parts[4]);
}
}
this.detectTimerId = setTimeout(function () {
if (onSuccess) {
onSuccess(ips);
}
}, 200);
}.bind(this);
rtc.createDataChannel('');
rtc.createOffer(rtc.setLocalDescription.bind(rtc), onError);
},
canDetectLocalAddress: function () {
return window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection || window.msRTCPeerConnection;
}
}, true);
});

@ -0,0 +1,68 @@
/*global define */
define(['lib/stapes'], function (Stapes) {
'use strict';
return Stapes.subclass(/** @lends Socket.prototype */{
/**
* @class Socket
* @abstract
*/
constructor: function () {
},
/**
* Create the socket
* @param onSuccess
* @param onError
* @abstract
*/
create: function (onSuccess, onError) {
},
/**
* Check if a connection is opened.
* @abstract
*/
isConnected: function () {
return false;
},
/**
* Connect to another peer
* @param {Object} peer Port object
* @param {function} [onSuccess] Callback to call on success
* @param {function(error:string)} [onError] Callback to call on error
* @abstract
*/
connect: function (peer, onSuccess, onError) {
},
/**
* Close the current connection
* @param {function} [onSuccess] Callback to call on success
* @param {function(error:string)} [onError] Callback to call on error
* @abstract
*/
close: function (onSuccess, onError) {
},
/**
* Read data from the socket
* @param {function} [onSuccess] Callback to call on success
* @param {function(error:string)} [onError] Callback to call on error
* @abstract
*/
read: function (onSuccess, onError) {
},
/**
* Writes data to the socket
* @param {string | Array} data Data to send.
* @param {function} [onSuccess] Callback to call if data was sent successfully
* @param {function(error:string)} [onError] Callback to call on error
* @abstract
*/
write: function (data, onSuccess, onError) {
}
}, true);
});

@ -0,0 +1,229 @@
define(['lib/stapes', 'api/Socket', 'utils/Tools'], function (Stapes, Socket, tools) {
'use strict';
return Socket.subclass(/** @lends WebSocket.prototype */{
DEBUG: false,
handle: null,
/**
* @type {function}
*/
currentResponseCallback: null,
/**
* @type {function}
*/
currentErrorCallback: null,
/**
* Temporary buffer for incoming data
* @type {Uint8Array}
*/
inputBuffer: null,
inputBufferIndex: 0,
readBufferTimerId: null,
/**
* @class WebSocket
* @extends Socket
* @constructs
*/
constructor: function () {
this.inputBuffer = new Uint8Array(4096);
this.inputBufferIndex = 0;
},
create: function (onSuccess, onError) {
if (this.DEBUG) {
console.log('[DEBUG] Creating socket...');
}
if (onSuccess) {
onSuccess();
}
},
isConnected: function (resultCallback) {
if (this.DEBUG) {
console.log('[DEBUG] Checking if socket is connected...');
}
if (!this.handle) {
if (this.DEBUG) {
console.log('[DEBUG] Socket not created');
}
if (resultCallback) {
resultCallback(false);
}
return;
}
if (resultCallback) {
if (this.handle.readyState === WebSocket.OPEN) {
resultCallback(true);
} else {
resultCallback(false);
}
}
},
connect: function (server, onSuccess, onError) {
if (this.DEBUG) {
console.log('[DEBUG] Connecting to peer ' + server.address + ':' + server.port);
}
this.currentErrorCallback = onError;
this.handle = new WebSocket('ws://' + server.address + ':' + server.port);
this.handle.onmessage = this.onDataReceived.bind(this);
this.handle.onclose = function () {
if (this.DEBUG) {
console.log('onClose');
}
}.bind(this);
this.handle.onerror = function () {
if (this.DEBUG) {
console.log('[ERROR]: ');
}
if (this.currentErrorCallback) {
this.currentErrorCallback('WebSocket error');
this.currentErrorCallback = null;
}
}.bind(this);
this.handle.onopen = function () {
if (onSuccess) {
onSuccess();
}
};
},
close: function (onSuccess, onError) {
if (this.DEBUG) {
console.log('[DEBUG] Closing socket...');
}
if (this.handle) {
this.handle.close();
if (onSuccess) {
onSuccess();
}
} else {
if (this.DEBUG) {
console.log('[DEBUG] Socket not created');
}
if (onError) {
onError('Socket handle is invalid');
}
}
},
write: function (data, onSuccess, onError) {
var dataToSend = null, dataType = typeof (data);
if (this.DEBUG) {
console.log('[DEBUG] writing to socket...');
}
if (!this.handle) {
if (this.DEBUG) {
console.log('[DEBUG] Socket not created');
}
if (onError) {
onError('Socket handle is invalid');
}
return;
}
this.isConnected(function (connected) {
if (connected) {
if (dataType === 'string') {
if (this.DEBUG) {
console.log('> ' + data);
}
//dataToSend = tools.str2ab(data);
dataToSend = data;
} else {
if (this.DEBUG) {
console.log('> ' + tools.ab2hexstr(data));
}
dataToSend = data;
}
this.currentErrorCallback = onError;
this.handle.send(dataToSend);
if (onSuccess) {
onSuccess();
}
} else {
if (onError) {
onError('No connection to peer');
}
}
}.bind(this));
},
read: function (onSuccess, onError) {
if (this.DEBUG) {
console.log('[DEBUG] reading from socket...');
}
if (!this.handle) {
if (this.DEBUG) {
console.log('[DEBUG] socket not created');
}
if (onError) {
onError('Socket handle is invalid');
}
return;
}
this.isConnected(function (connected) {
if (!connected) {
this.currentResponseCallback = null;
this.currentErrorCallback = null;
if (onError) {
onError('No connection to peer');
}
}
}.bind(this));
if (onSuccess) {
this.currentResponseCallback = onSuccess;
}
if (onError) {
this.currentErrorCallback = onError;
}
},
/**
* Data receiption callback
* @private
* @param event
*/
onDataReceived: function (event) {
if (this.DEBUG) {
console.log('[DEBUG] received data...');
}
if (this.handle && event.data) {
if (this.DEBUG) {
console.log('< ' + event.data);
}
if (this.currentResponseCallback) {
this.currentResponseCallback(tools.str2ab(event.data));
this.currentResponseCallback = null;
}
}
}
}, true);
});

@ -0,0 +1,534 @@
/*global define */
define([
'lib/stapes', 'views/MainView', 'models/Settings', 'views/SettingsView', 'views/EffectsView', 'views/TransformView', 'data/ServerControl', 'api/Socket', 'api/Network'
], function (Stapes, MainView, Settings, SettingsView, EffectsView, TransformView, ServerControl, Socket, Network) {
'use strict';
var network = new Network();
return Stapes.subclass(/** @lends AppController.prototype */{
/**
* @type MainView
*/
mainView: null,
/**
* @type SettingsView
*/
settingsView: null,
/**
* @type EffectsView
*/
effectsView: null,
/**
* @type TransformView
*/
transformView: null,
/**
* @type Settings
*/
settings: null,
/**
* @type ServerControl
*/
serverControl: null,
color: {
r: 25,
g: 25,
b: 25
},
effects: [],
transform: {},
selectedServer: null,
/**
* @class AppController