mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2023-10-10 13:36:59 +02:00
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
This commit is contained in:
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}"
|
||||
|
609
assets/webconfig/css/index.css
Normal file
609
assets/webconfig/css/index.css
Normal file
@ -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';
|
||||
}
|
69
assets/webconfig/index.html
Normal file
69
assets/webconfig/index.html
Normal file
@ -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"></div>
|
||||
<input type="text" class="value" autocomplete="off" autocorrect="off" autocapitalize="off"/>
|
||||
<div class="icon" id="clearall_button"></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"></div>
|
||||
<div class="title">Color</div>
|
||||
<div class="touchrect"></div>
|
||||
</div>
|
||||
<div class="button" id="effectsButton" data-area="effects">
|
||||
<div class="icon"></div>
|
||||
<div class="title">Effects</div>
|
||||
<div class="touchrect"></div>
|
||||
</div>
|
||||
<div class="button" id="thresholdButton" data-area="transform">
|
||||
<div class="icon"></div>
|
||||
<div class="title">Transform</div>
|
||||
<div class="touchrect"></div>
|
||||
</div>
|
||||
<div class="button" id="settingsButton" data-area="settings">
|
||||
<div class="icon">n</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>
|
37
assets/webconfig/js/app/api/ChromeLocalStorage.js
Normal file
37
assets/webconfig/js/app/api/ChromeLocalStorage.js
Normal file
@ -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));
|
||||
}
|
||||
});
|
||||
});
|
57
assets/webconfig/js/app/api/ChromeNetwork.js
Normal file
57
assets/webconfig/js/app/api/ChromeNetwork.js
Normal file
@ -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);
|
||||
});
|
307
assets/webconfig/js/app/api/ChromeTcpSocket.js
Normal file
307
assets/webconfig/js/app/api/ChromeTcpSocket.js
Normal file
@ -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);
|
||||
});
|
49
assets/webconfig/js/app/api/LocalStorage.js
Normal file
49
assets/webconfig/js/app/api/LocalStorage.js
Normal file
@ -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');
|
||||
}
|
||||
});
|
||||
});
|
57
assets/webconfig/js/app/api/Network.js
Normal file
57
assets/webconfig/js/app/api/Network.js
Normal file
@ -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);
|
||||
});
|
68
assets/webconfig/js/app/api/Socket.js
Normal file
68
assets/webconfig/js/app/api/Socket.js
Normal file
@ -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);
|
||||
});
|
229
assets/webconfig/js/app/api/WebSocket.js
Normal file
229
assets/webconfig/js/app/api/WebSocket.js
Normal file
@ -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);
|
||||
});
|
534
assets/webconfig/js/app/controllers/AppController.js
Normal file
534
assets/webconfig/js/app/controllers/AppController.js
Normal file
@ -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
|
||||
* @constructs
|
||||
*/
|
||||
constructor: function () {
|
||||
this.mainView = new MainView();
|
||||
this.settingsView = new SettingsView();
|
||||
this.effectsView = new EffectsView();
|
||||
this.transformView = new TransformView();
|
||||
|
||||
this.settings = new Settings();
|
||||
|
||||
this.bindEventHandlers();
|
||||
this.mainView.setColor(this.color);
|
||||
|
||||
if (!network.canDetectLocalAddress()) {
|
||||
this.settingsView.enableDetectButton(false);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Do initialization
|
||||
*/
|
||||
init: function () {
|
||||
this.settings.load();
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
bindEventHandlers: function () {
|
||||
this.settings.on({
|
||||
'loaded': function () {
|
||||
var i;
|
||||
|
||||
for (i = 0; i < this.settings.servers.length; i++) {
|
||||
if (this.settings.servers[i].selected) {
|
||||
this.selectedServer = this.settings.servers[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.settingsView.fillServerList(this.settings.servers);
|
||||
|
||||
if (!this.selectedServer) {
|
||||
this.gotoArea('settings');
|
||||
} else {
|
||||
this.connectToServer(this.selectedServer);
|
||||
}
|
||||
},
|
||||
'error': function (message) {
|
||||
this.showError(message);
|
||||
},
|
||||
'serverAdded': function (server) {
|
||||
var i;
|
||||
for (i = 0; i < this.settings.servers.length; i++) {
|
||||
if (this.settings.servers[i].selected) {
|
||||
this.selectedServer = this.settings.servers[i];
|
||||
this.connectToServer(server);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.settingsView.fillServerList(this.settings.servers);
|
||||
},
|
||||
'serverChanged': function (server) {
|
||||
var i;
|
||||
for (i = 0; i < this.settings.servers.length; i++) {
|
||||
if (this.settings.servers[i].selected) {
|
||||
this.selectedServer = this.settings.servers[i];
|
||||
this.connectToServer(server);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.settingsView.fillServerList(this.settings.servers);
|
||||
this.connectToServer(server);
|
||||
},
|
||||
'serverRemoved': function () {
|
||||
var i, removedSelected = true;
|
||||
this.settingsView.fillServerList(this.settings.servers);
|
||||
|
||||
for (i = 0; i < this.settings.servers.length; i++) {
|
||||
if (this.settings.servers[i].selected) {
|
||||
removedSelected = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (removedSelected) {
|
||||
this.selectedServer = null;
|
||||
if (this.serverControl) {
|
||||
this.serverControl.disconnect();
|
||||
}
|
||||
this.effectsView.clear();
|
||||
this.transformView.clear();
|
||||
}
|
||||
}
|
||||
}, this);
|
||||
|
||||
this.mainView.on({
|
||||
'barClick': function (id) {
|
||||
if (id !== 'settings') {
|
||||
if (!this.selectedServer) {
|
||||
this.showError('No server selected');
|
||||
} else if (!this.serverControl) {
|
||||
this.connectToServer(this.selectedServer);
|
||||
}
|
||||
}
|
||||
this.gotoArea(id);
|
||||
},
|
||||
'colorChange': function (color) {
|
||||
this.color = color;
|
||||
|
||||
if (!this.selectedServer) {
|
||||
this.showError('No server selected');
|
||||
} else if (!this.serverControl) {
|
||||
this.connectToServer(this.selectedServer, function () {
|
||||
this.serverControl.setColor(color, this.selectedServer.duration);
|
||||
}.bind(this));
|
||||
} else {
|
||||
this.serverControl.setColor(color, this.selectedServer.duration);
|
||||
}
|
||||
},
|
||||
'clear': function () {
|
||||
if (!this.selectedServer) {
|
||||
this.showError('No server selected');
|
||||
} else if (!this.serverControl) {
|
||||
this.connectToServer(this.selectedServer, function () {
|
||||
this.serverControl.clear();
|
||||
}.bind(this));
|
||||
} else {
|
||||
this.serverControl.clear();
|
||||
}
|
||||
},
|
||||
'clearall': function () {
|
||||
if (!this.selectedServer) {
|
||||
this.showError('No server selected');
|
||||
} else if (!this.serverControl) {
|
||||
this.connectToServer(this.selectedServer, function () {
|
||||
this.serverControl.clearall();
|
||||
this.mainView.setColor({r: 0, g: 0, b: 0});
|
||||
}.bind(this));
|
||||
} else {
|
||||
this.serverControl.clearall();
|
||||
this.mainView.setColor({r: 0, g: 0, b: 0});
|
||||
}
|
||||
}
|
||||
}, this);
|
||||
|
||||
this.settingsView.on({
|
||||
'serverAdded': function (server) {
|
||||
if (server.address && server.port) {
|
||||
server.priority = server.priority || 50;
|
||||
this.settings.addServer(server);
|
||||
this.lockSettingsView(false);
|
||||
} else {
|
||||
this.showError('Invalid server data');
|
||||
}
|
||||
},
|
||||
'serverAddCanceled': function () {
|
||||
this.lockSettingsView(false);
|
||||
this.settingsView.fillServerList(this.settings.servers);
|
||||
},
|
||||
'serverEditCanceled': function () {
|
||||
this.lockSettingsView(false);
|
||||
this.settingsView.fillServerList(this.settings.servers);
|
||||
},
|
||||
'serverSelected': function (index) {
|
||||
this.lockSettingsView(false);
|
||||
this.settings.setSelectedServer(index);
|
||||
},
|
||||
'serverRemoved': function (index) {
|
||||
this.settings.removeServer(index);
|
||||
},
|
||||
'serverChanged': function (data) {
|
||||
if (data.server.address && data.server.port) {
|
||||
data.server.priority = data.server.priority || 50;
|
||||
this.settings.updateServer(data.index, data.server);
|
||||
this.lockSettingsView(false);
|
||||
} else {
|
||||
this.showError('Invalid server data');
|
||||
}
|
||||
},
|
||||
'editServer': function (index) {
|
||||
var server = this.settings.servers[index];
|
||||
this.settingsView.editServer({index: index, server: server});
|
||||
},
|
||||
'durationChanged': function (value) {
|
||||
this.settings.duration = value;
|
||||
this.settings.save();
|
||||
},
|
||||
'detect': function () {
|
||||
this.lockSettingsView(true);
|
||||
this.settingsView.showWaiting(true);
|
||||
this.searchForServer(function (server) {
|
||||
this.settings.addServer(server);
|
||||
}.bind(this), function () {
|
||||
this.lockSettingsView(false);
|
||||
this.settingsView.showWaiting(false);
|
||||
}.bind(this));
|
||||
}
|
||||
}, this);
|
||||
|
||||
this.effectsView.on({
|
||||
'effectSelected': function (effectId) {
|
||||
if (!this.serverControl) {
|
||||
this.connectToServer(this.selectedServer, function () {
|
||||
this.serverControl.runEffect(this.effects[parseInt(effectId)]);
|
||||
}.bind(this));
|
||||
} else {
|
||||
this.serverControl.runEffect(this.effects[parseInt(effectId)]);
|
||||
}
|
||||
}
|
||||
}, this);
|
||||
|
||||
this.transformView.on({
|
||||
'gamma': function (data) {
|
||||
if (data.r) {
|
||||
this.transform.gamma[0] = data.r;
|
||||
} else if (data.g) {
|
||||
this.transform.gamma[1] = data.g;
|
||||
} else if (data.b) {
|
||||
this.transform.gamma[2] = data.b;
|
||||
}
|
||||
|
||||
if (this.serverControl) {
|
||||
this.serverControl.setTransform(this.transform);
|
||||
}
|
||||
},
|
||||
'whitelevel': function (data) {
|
||||
if (data.r) {
|
||||
this.transform.whitelevel[0] = data.r;
|
||||
} else if (data.g) {
|
||||
this.transform.whitelevel[1] = data.g;
|
||||
} else if (data.b) {
|
||||
this.transform.whitelevel[2] = data.b;
|
||||
}
|
||||
|
||||
if (this.serverControl) {
|
||||
this.serverControl.setTransform(this.transform);
|
||||
}
|
||||
},
|
||||
'blacklevel': function (data) {
|
||||
if (data.r) {
|
||||
this.transform.blacklevel[0] = data.r;
|
||||
} else if (data.g) {
|
||||
this.transform.blacklevel[1] = data.g;
|
||||
} else if (data.b) {
|
||||
this.transform.blacklevel[2] = data.b;
|
||||
}
|
||||
|
||||
if (this.serverControl) {
|
||||
this.serverControl.setTransform(this.transform);
|
||||
}
|
||||
},
|
||||
'threshold': function (data) {
|
||||
if (data.r) {
|
||||
this.transform.threshold[0] = data.r;
|
||||
} else if (data.g) {
|
||||
this.transform.threshold[1] = data.g;
|
||||
} else if (data.b) {
|
||||
this.transform.threshold[2] = data.b;
|
||||
}
|
||||
|
||||
if (this.serverControl) {
|
||||
this.serverControl.setTransform(this.transform);
|
||||
}
|
||||
},
|
||||
'hsv': function (data) {
|
||||
if (data.valueGain) {
|
||||
this.transform.valueGain = data.valueGain;
|
||||
} else if (data.saturationGain) {
|
||||
this.transform.saturationGain = data.saturationGain;
|
||||
}
|
||||
|
||||
if (this.serverControl) {
|
||||
this.serverControl.setTransform(this.transform);
|
||||
}
|
||||
}
|
||||
}, this);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param id
|
||||
*/
|
||||
gotoArea: function (id) {
|
||||
this.mainView.scrollToArea(id);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param server
|
||||
*/
|
||||
connectToServer: function (server, onConnected) {
|
||||
if (this.serverControl) {
|
||||
if (this.serverControl.isConnecting()) {
|
||||
return;
|
||||
}
|
||||
this.serverControl.off();
|
||||
this.serverControl.disconnect();
|
||||
this.transformView.clear();
|
||||
this.effectsView.clear();
|
||||
}
|
||||
|
||||
this.serverControl = new ServerControl(server, Socket);
|
||||
this.serverControl.on({
|
||||
connected: function () {
|
||||
this.serverControl.getServerInfo();
|
||||
},
|
||||
serverInfo: function (info) {
|
||||
var index;
|
||||
if (!this.selectedServer.name || this.selectedServer.name.length === 0) {
|
||||
this.selectedServer.name = info.hostname;
|
||||
index = this.settings.indexOfServer(this.selectedServer);
|
||||
this.settings.updateServer(index, this.selectedServer);
|
||||
this.settingsView.fillServerList(this.settings.servers);
|
||||
}
|
||||
this.effects = info.effects;
|
||||
this.transform = info.transform[0];
|
||||
this.updateView();
|
||||
this.showStatus('Connected to ' + this.selectedServer.name);
|
||||
if (onConnected) {
|
||||
onConnected();
|
||||
}
|
||||
},
|
||||
error: function (message) {
|
||||
this.serverControl = null;
|
||||
this.showError(message);
|
||||
}
|
||||
}, this);
|
||||
this.serverControl.connect();
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
updateView: function () {
|
||||
var i, effects = [];
|
||||
if (this.effects) {
|
||||
for (i = 0; i < this.effects.length; i++) {
|
||||
effects.push({id: i, name: this.effects[i].name});
|
||||
}
|
||||
}
|
||||
|
||||
this.effectsView.clear();
|
||||
this.effectsView.fillList(effects);
|
||||
|
||||
this.transformView.clear();
|
||||
this.transformView.fillList(this.transform);
|
||||
},
|
||||
|
||||
/**
|
||||
* Shows the error text
|
||||
* @param {string} error - Error message
|
||||
*/
|
||||
showError: function (error) {
|
||||
this.mainView.showError(error);
|
||||
},
|
||||
|
||||
/**
|
||||
* Shows a message
|
||||
* @param {string} message - Text to show
|
||||
*/
|
||||
showStatus: function (message) {
|
||||
this.mainView.showStatus(message);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param lock
|
||||
*/
|
||||
lockSettingsView: function (lock) {
|
||||
if (network.canDetectLocalAddress()) {
|
||||
this.settingsView.enableDetectButton(!lock);
|
||||
}
|
||||
this.settingsView.lockList(lock);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param onFound
|
||||
* @param onEnd
|
||||
*/
|
||||
searchForServer: function (onFound, onEnd) {
|
||||
network.getLocalInterfaces(function (ips) {
|
||||
if (ips.length === 0) {
|
||||
onEnd();
|
||||
return;
|
||||
}
|
||||
|
||||
function checkInterface (localInterfaceAddress, ciOnFinished) {
|
||||
var index, ipParts, addr;
|
||||
|
||||
index = 1;
|
||||
ipParts = localInterfaceAddress.split('.');
|
||||
ipParts[3] = index;
|
||||
addr = ipParts.join('.');
|
||||
|
||||
function checkAddressRange (startAddress, count, carOnFinished) {
|
||||
var ipParts, i, addr, cbCounter = 0, last;
|
||||
|
||||
function checkAddress (address, port, caOnFinished) {
|
||||
var server = new ServerControl({'address': address, 'port': port}, Socket);
|
||||
server.on({
|
||||
'error': function () {
|
||||
server.disconnect();
|
||||
caOnFinished();
|
||||
},
|
||||
'connected': function () {
|
||||
server.getServerInfo();
|
||||
},
|
||||
'serverInfo': function (result) {
|
||||
var serverInfo = {
|
||||
'address': address,
|
||||
'port': port,
|
||||
'priority': 50
|
||||
};
|
||||
server.disconnect();
|
||||
|
||||
if (result.hostname) {
|
||||
serverInfo.name = result.hostname;
|
||||
}
|
||||
|
||||
caOnFinished(serverInfo);
|
||||
}
|
||||
});
|
||||
server.connect();
|
||||
}
|
||||
|
||||
function checkAddressDoneCb (serverInfo) {
|
||||
var ipParts, nextAddr;
|
||||
|
||||
if (serverInfo && onFound) {
|
||||
onFound(serverInfo);
|
||||
}
|
||||
|
||||
cbCounter++;
|
||||
if (cbCounter === count) {
|
||||
ipParts = startAddress.split('.');
|
||||
ipParts[3] = parseInt(ipParts[3]) + count;
|
||||
nextAddr = ipParts.join('.');
|
||||
carOnFinished(nextAddr);
|
||||
}
|
||||
}
|
||||
|
||||
ipParts = startAddress.split('.');
|
||||
last = parseInt(ipParts[3]);
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
ipParts[3] = last + i;
|
||||
addr = ipParts.join('.');
|
||||
|
||||
checkAddress(addr, 19444, checkAddressDoneCb);
|
||||
}
|
||||
}
|
||||
|
||||
function checkAddressRangeCb (nextAddr) {
|
||||
var ipParts, count = 64, lastPart;
|
||||
|
||||
ipParts = nextAddr.split('.');
|
||||
lastPart = parseInt(ipParts[3]);
|
||||
if (lastPart === 255) {
|
||||
ciOnFinished();
|
||||
return;
|
||||
} else if (lastPart + 64 > 254) {
|
||||
count = 255 - lastPart;
|
||||
}
|
||||
|
||||
checkAddressRange(nextAddr, count, checkAddressRangeCb);
|
||||
}
|
||||
|
||||
// do search in chunks because the dispatcher used in the ios socket plugin can handle only 64 threads
|
||||
checkAddressRange(addr, 64, checkAddressRangeCb);
|
||||
}
|
||||
|
||||
function checkInterfaceCb () {
|
||||
if (ips.length === 0) {
|
||||
onEnd();
|
||||
} else {
|
||||
checkInterface(ips.pop(), checkInterfaceCb);
|
||||
}
|
||||
}
|
||||
|
||||
checkInterface(ips.pop(), checkInterfaceCb);
|
||||
|
||||
}.bind(this), function (error) {
|
||||
this.showError(error);
|
||||
}.bind(this));
|
||||
}
|
||||
});
|
||||
});
|
201
assets/webconfig/js/app/data/ServerControl.js
Normal file
201
assets/webconfig/js/app/data/ServerControl.js
Normal file
@ -0,0 +1,201 @@
|
||||
/*global define */
|
||||
define(['lib/stapes', 'utils/Tools'], function (Stapes, tools) {
|
||||
'use strict';
|
||||
|
||||
return Stapes.subclass(/** @lends ServerControl.prototype */{
|
||||
/** @type Socket */
|
||||
socket: null,
|
||||
server: null,
|
||||
connecting: false,
|
||||
|
||||
/**
|
||||
* @class ServerControl
|
||||
* @classdesc Interface for the hyperion server control. All commands are sent directly to hyperion's server.
|
||||
* @constructs
|
||||
* @param {object} server - Hyperion server parameter
|
||||
* @param {string} server.address - Server address
|
||||
* @param {number} server.port - Hyperion server port
|
||||
* @param {function} Socket - constructor of the socket to use for communication
|
||||
*
|
||||
* @fires connected
|
||||
* @fires error
|
||||
* @fires serverInfo
|
||||
* @fires cmdSent
|
||||
*/
|
||||
constructor: function (server, Socket) {
|
||||
this.server = server;
|
||||
this.socket = new Socket();
|
||||
this.connecting = false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Try to connect to the server
|
||||
*/
|
||||
connect: function () {
|
||||
if (!this.server) {
|
||||
this.emit('error', 'Missing server info');
|
||||
} else {
|
||||
this.connecting = true;
|
||||
this.socket.create(function () {
|
||||
this.socket.connect(this.server, function () {
|
||||
this.emit('connected');
|
||||
this.connecting = false;
|
||||
}.bind(this), function (error) {
|
||||
this.socket.close();
|
||||
this.emit('error', error);
|
||||
this.connecting = false;
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Disconnect from the server
|
||||
*/
|
||||
disconnect: function () {
|
||||
this.socket.close();
|
||||
},
|
||||
|
||||
/**
|
||||
* Sends the color command to the server
|
||||
* @param {object} color - Color to set
|
||||
* @param {number} color.r - Red value
|
||||
* @param {number} color.g - Green value
|
||||
* @param {number} color.b - Blue value
|
||||
* @param {number} duration - Duration in seconds
|
||||
*/
|
||||
setColor: function (color, duration) {
|
||||
var intColor, cmd;
|
||||
|
||||
intColor = [
|
||||
Math.floor(color.r), Math.floor(color.g), Math.floor(color.b)
|
||||
];
|
||||
cmd = {
|
||||
command: 'color',
|
||||
color: intColor,
|
||||
priority: this.server.priority
|
||||
};
|
||||
|
||||
if (duration) {
|
||||
cmd.duration = duration * 1000;
|
||||
}
|
||||
|
||||
this.sendCommand(cmd);
|
||||
},
|
||||
|
||||
clear: function () {
|
||||
var cmd = {
|
||||
command: 'clear',
|
||||
priority: this.server.priority
|
||||
};
|
||||
this.sendCommand(cmd);
|
||||
},
|
||||
|
||||
clearall: function () {
|
||||
var cmd = {
|
||||
command: 'clearall'
|
||||
};
|
||||
this.sendCommand(cmd);
|
||||
},
|
||||
|
||||
/**
|
||||
* Sends a command to rund specified effect
|
||||
* @param {object} effect - Effect object
|
||||
*/
|
||||
runEffect: function (effect) {
|
||||
var cmd;
|
||||
|
||||
if (!effect) {
|
||||
return;
|
||||
}
|
||||
|
||||
cmd = {
|
||||
command: 'effect',
|
||||
effect: {
|
||||
name: effect.name,
|
||||
args: effect.args
|
||||
},
|
||||
priority: this.server.priority
|
||||
};
|
||||
this.sendCommand(cmd);
|
||||
},
|
||||
|
||||
/**
|
||||
* Sends a command for color transformation
|
||||
* @param {object} transform
|
||||
*/
|
||||
setTransform: function (transform) {
|
||||
var cmd;
|
||||
|
||||
if (!transform) {
|
||||
return;
|
||||
}
|
||||
|
||||
cmd = {
|
||||
'command': 'transform',
|
||||
'transform': transform
|
||||
};
|
||||
|
||||
this.sendCommand(cmd);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param command
|
||||
*/
|
||||
sendCommand: function (command) {
|
||||
var data;
|
||||
|
||||
if (!command) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof command === 'string') {
|
||||
data = command;
|
||||
} else {
|
||||
data = JSON.stringify(command);
|
||||
}
|
||||
|
||||
this.socket.isConnected(function (connected) {
|
||||
if (connected) {
|
||||
this.socket.write(data + '\n', function () {
|
||||
this.emit('cmdSent', command);
|
||||
}.bind(this), function (error) {
|
||||
this.emit('error', error);
|
||||
}.bind(this));
|
||||
} else {
|
||||
this.emit('error', 'No server connection');
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the information about the hyperion server
|
||||
*/
|
||||
getServerInfo: function () {
|
||||
var cmd = {command: 'serverinfo'};
|
||||
|
||||
this.socket.isConnected(function (connected) {
|
||||
if (connected) {
|
||||
this.socket.write(JSON.stringify(cmd) + '\n', function () {
|
||||
this.socket.read(function (result) {
|
||||
var dataobj, str = tools.ab2str(result);
|
||||
dataobj = JSON.parse(str);
|
||||
this.emit('serverInfo', dataobj.info);
|
||||
}.bind(this), function (error) {
|
||||
this.emit('error', error);
|
||||
}.bind(this));
|
||||
}.bind(this), function (error) {
|
||||
this.emit('error', error);
|
||||
}.bind(this));
|
||||
} else {
|
||||
this.emit('error', 'No server connection');
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
isConnecting: function () {
|
||||
return this.connecting;
|
||||
}
|
||||
});
|
||||
});
|
150
assets/webconfig/js/app/main.js
Normal file
150
assets/webconfig/js/app/main.js
Normal file
@ -0,0 +1,150 @@
|
||||
/*global require, requirejs */
|
||||
|
||||
requirejs.config({
|
||||
baseUrl: 'js/app',
|
||||
paths: {
|
||||
'lib': '../vendor'
|
||||
},
|
||||
map: {
|
||||
'controllers/AppController': {
|
||||
'api/Socket': 'api/WebSocket'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} dom
|
||||
* @param {function} handler
|
||||
*/
|
||||
window.addPointerDownHandler = function (dom, handler) {
|
||||
'use strict';
|
||||
dom.addEventListener('touchstart', handler, false);
|
||||
dom.addEventListener('mousedown', handler, false);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} dom
|
||||
* @param {function} handler
|
||||
*/
|
||||
window.removePointerDownHandler = function (dom, handler) {
|
||||
'use strict';
|
||||
dom.removeEventListener('touchstart', handler, false);
|
||||
dom.removeEventListener('mousedown', handler, false);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} dom
|
||||
* @param {function} handler
|
||||
*/
|
||||
window.addPointerUpHandler = function (dom, handler) {
|
||||
'use strict';
|
||||
dom.addEventListener('touchend', handler, false);
|
||||
dom.addEventListener('mouseup', handler, false);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} dom
|
||||
* @param {function} handler
|
||||
*/
|
||||
window.removePointerUpHandler = function (dom, handler) {
|
||||
'use strict';
|
||||
dom.removeEventListener('touchend', handler, false);
|
||||
dom.removeEventListener('mouseup', handler, false);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} dom
|
||||
* @param {function} handler
|
||||
*/
|
||||
window.addPointerMoveHandler = function (dom, handler) {
|
||||
'use strict';
|
||||
dom.addEventListener('touchmove', handler, false);
|
||||
dom.addEventListener('mousemove', handler, false);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} dom
|
||||
* @param {function} handler
|
||||
*/
|
||||
window.removePointerMoveHandler = function (dom, handler) {
|
||||
'use strict';
|
||||
dom.removeEventListener('touchmove', handler, false);
|
||||
dom.removeEventListener('mousemove', handler, false);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {HTMLElement} dom
|
||||
* @param {function} handler
|
||||
*/
|
||||
window.addClickHandler = function (dom, handler) {
|
||||
'use strict';
|
||||
var toFire = false;
|
||||
|
||||
dom.addEventListener('touchstart', function (event) {
|
||||
if (event.touches.length > 1) {
|
||||
return;
|
||||
}
|
||||
toFire = true;
|
||||
}, false);
|
||||
|
||||
dom.addEventListener('touchmove', function () {
|
||||
toFire = false;
|
||||
}, false);
|
||||
|
||||
dom.addEventListener('touchend', function (event) {
|
||||
var focused;
|
||||
if (toFire) {
|
||||
handler.apply(this, arguments);
|
||||
|
||||
focused = document.querySelector(':focus');
|
||||
|
||||
if (focused && event.target !== focused) {
|
||||
focused.blur();
|
||||
}
|
||||
|
||||
if (event.target.tagName !== 'INPUT') {
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
}, false);
|
||||
|
||||
dom.addEventListener('click', function () {
|
||||
handler.apply(this, arguments);
|
||||
}, false);
|
||||
};
|
||||
|
||||
function checkInstallFirefoxOS() {
|
||||
'use strict';
|
||||
var manifest_url, installCheck;
|
||||
|
||||
manifest_url = [location.protocol, '//', location.host, location.pathname.replace('index.html',''), 'manifest.webapp'].join('');
|
||||
installCheck = navigator.mozApps.checkInstalled(manifest_url);
|
||||
|
||||
installCheck.onerror = function() {
|
||||
alert('Error calling checkInstalled: ' + installCheck.error.name);
|
||||
};
|
||||
|
||||
installCheck.onsuccess = function() {
|
||||
var installLoc;
|
||||
if(!installCheck.result) {
|
||||
if (confirm('Do you want to install hyperion remote contorl on your device?')) {
|
||||
installLoc = navigator.mozApps.install(manifest_url);
|
||||
installLoc.onsuccess = function(data) {
|
||||
};
|
||||
installLoc.onerror = function() {
|
||||
alert(installLoc.error.name);
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
require(['controllers/AppController'], function (AppController) {
|
||||
'use strict';
|
||||
var app = new AppController();
|
||||
app.init();
|
||||
if (navigator.mozApps && navigator.userAgent.indexOf('Mozilla/5.0 (Mobile;') !== -1) {
|
||||
checkInstallFirefoxOS();
|
||||
}
|
||||
});
|
108
assets/webconfig/js/app/main_chrome.js
Normal file
108
assets/webconfig/js/app/main_chrome.js
Normal file
@ -0,0 +1,108 @@
|
||||
/*global require, requirejs */
|
||||
|
||||
requirejs.config({
|
||||
baseUrl: '../js/app',
|
||||
paths: {
|
||||
'lib': '../vendor'
|
||||
},
|
||||
map: {
|
||||
'controllers/AppController': {
|
||||
'api/Socket': 'api/ChromeTcpSocket',
|
||||
'api/Network': 'api/ChromeNetwork'
|
||||
},
|
||||
'models/Settings': {
|
||||
'api/LocalStorage': 'api/ChromeLocalStorage'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {HTMLElement} dom
|
||||
* @param {function} handler
|
||||
*/
|
||||
window.addPointerDownHandler = function (dom, handler) {
|
||||
'use strict';
|
||||
dom.addEventListener('touchstart', function (event) {
|
||||
handler.apply(this, arguments);
|
||||
event.preventDefault();
|
||||
}, false);
|
||||
|
||||
dom.addEventListener('mousedown', function () {
|
||||
handler.apply(this, arguments);
|
||||
}, false);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {HTMLElement} dom
|
||||
* @param {function} handler
|
||||
*/
|
||||
window.addPointerUpHandler = function (dom, handler) {
|
||||
'use strict';
|
||||
dom.addEventListener('touchend', function (event) {
|
||||
handler.apply(this, arguments);
|
||||
event.preventDefault();
|
||||
}, false);
|
||||
|
||||
dom.addEventListener('mouseup', function () {
|
||||
handler.apply(this, arguments);
|
||||
}, false);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {HTMLElement} dom
|
||||
* @param {function} handler
|
||||
*/
|
||||
window.addPointerMoveHandler = function (dom, handler) {
|
||||
'use strict';
|
||||
dom.addEventListener('touchmove', function (event) {
|
||||
handler.apply(this, arguments);
|
||||
event.preventDefault();
|
||||
}, false);
|
||||
|
||||
dom.addEventListener('mousemove', function () {
|
||||
handler.apply(this, arguments);
|
||||
}, false);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {HTMLElement} dom
|
||||
* @param {function} handler
|
||||
*/
|
||||
window.addClickHandler = function (dom, handler) {
|
||||
'use strict';
|
||||
var toFire = false;
|
||||
|
||||
dom.addEventListener('touchstart', function (event) {
|
||||
if (event.touches.length > 1) {
|
||||
return;
|
||||
}
|
||||
toFire = true;
|
||||
}, false);
|
||||
|
||||
dom.addEventListener('touchmove', function () {
|
||||
toFire = false;
|
||||
}, false);
|
||||
|
||||
dom.addEventListener('touchend', function (event) {
|
||||
if (toFire) {
|
||||
handler.apply(this, arguments);
|
||||
if (event.target.tagName !== 'INPUT') {
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
}, false);
|
||||
|
||||
dom.addEventListener('click', function () {
|
||||
handler.apply(this, arguments);
|
||||
}, false);
|
||||
};
|
||||
|
||||
require(['controllers/AppController'], function (AppController) {
|
||||
'use strict';
|
||||
var app = new AppController();
|
||||
app.init();
|
||||
});
|
124
assets/webconfig/js/app/models/Settings.js
Normal file
124
assets/webconfig/js/app/models/Settings.js
Normal file
@ -0,0 +1,124 @@
|
||||
/*global define */
|
||||
define(['lib/stapes', 'api/LocalStorage'], function (Stapes, LocalStorage) {
|
||||
'use strict';
|
||||
|
||||
return Stapes.subclass(/** @lends Settings.prototype */{
|
||||
storage: null, servers: [],
|
||||
|
||||
/**
|
||||
* @class Settings
|
||||
* @classdesc Local application settings
|
||||
* @constructs
|
||||
* @fires saved
|
||||
* @fires loaded
|
||||
* @fires error
|
||||
* @fires serverAdded
|
||||
* @fires serverChanged
|
||||
* @fires serverRemoved
|
||||
*/
|
||||
constructor: function () {
|
||||
this.storage = new LocalStorage();
|
||||
this.storage.on({
|
||||
error: function (message) {
|
||||
this.emit('error', message);
|
||||
}, got: function (settings) {
|
||||
if (settings) {
|
||||
this.servers = settings.servers || [];
|
||||
}
|
||||
this.emit('loaded');
|
||||
}, set: function () {
|
||||
this.emit('saved');
|
||||
}
|
||||
}, this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Save current settings
|
||||
*/
|
||||
save: function () {
|
||||
this.storage.set({
|
||||
servers: this.servers
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Loads persistent settings
|
||||
*/
|
||||
load: function () {
|
||||
this.storage.get();
|
||||
},
|
||||
|
||||
/**
|
||||
* Add a server definition
|
||||
* @param {object} server - Server information
|
||||
*/
|
||||
addServer: function (server) {
|
||||
if (this.indexOfServer(server) === -1) {
|
||||
if (this.servers.length === 0) {
|
||||
server.selected = true;
|
||||
}
|
||||
|
||||
this.servers.push(server);
|
||||
this.save();
|
||||
this.emit('serverAdded', server);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets a server as a default server
|
||||
* @param {number} index - Index of the server in the server list to set as default one
|
||||
*/
|
||||
setSelectedServer: function (index) {
|
||||
var i;
|
||||
for (i = 0; i < this.servers.length; i++) {
|
||||
delete this.servers[i].selected;
|
||||
}
|
||||
this.servers[index].selected = true;
|
||||
this.save();
|
||||
this.emit('serverChanged', this.servers[index]);
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove a server from the list
|
||||
* @param {number} index - Index of the server in the list to remove
|
||||
*/
|
||||
removeServer: function (index) {
|
||||
this.servers.splice(index, 1);
|
||||
this.save();
|
||||
this.emit('serverRemoved');
|
||||
},
|
||||
|
||||
/**
|
||||
* Update server information
|
||||
* @param {number} index - Index of the server to update
|
||||
* @param {object} server - New server information
|
||||
*/
|
||||
updateServer: function (index, server) {
|
||||
if (index >= 0 && index < this.servers.length) {
|
||||
this.servers[index] = server;
|
||||
this.save();
|
||||
this.emit('serverChanged', server);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Find the server in the list.
|
||||
* @param {object} server - Server to search index for
|
||||
* @returns {number} - Index of the server in the list. -1 if server not found
|
||||
*/
|
||||
indexOfServer: function (server) {
|
||||
var i, tmp;
|
||||
|
||||
for (i = 0; i < this.servers.length; i++) {
|
||||
tmp = this.servers[i];
|
||||
|
||||
if (tmp.port === server.port && tmp.address === server.address) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
});
|
||||
});
|
56
assets/webconfig/js/app/utils/Tools.js
Normal file
56
assets/webconfig/js/app/utils/Tools.js
Normal file
@ -0,0 +1,56 @@
|
||||
define([], function () {
|
||||
'use strict';
|
||||
|
||||
return {
|
||||
/**
|
||||
* Convert a string to ArrayBuffer
|
||||
* @param {string} str String to convert
|
||||
* @returns {ArrayBuffer} Result
|
||||
*/
|
||||
str2ab: function (str) {
|
||||
var i, buf = new ArrayBuffer(str.length), bufView = new Uint8Array(buf);
|
||||
for (i = 0; i < str.length; i++) {
|
||||
bufView[i] = str.charCodeAt(i);
|
||||
}
|
||||
return buf;
|
||||
},
|
||||
|
||||
/**
|
||||
* Convert an array to ArrayBuffer
|
||||
* @param array
|
||||
* @returns {ArrayBuffer} Result
|
||||
*/
|
||||
a2ab: function (array) {
|
||||
return new Uint8Array(array).buffer;
|
||||
},
|
||||
|
||||
/**
|
||||
* Convert ArrayBuffer to string
|
||||
* @param {ArrayBuffer} buffer Buffer to convert
|
||||
* @returns {string}
|
||||
*/
|
||||
ab2hexstr: function (buffer) {
|
||||
var i, str = '', ua = new Uint8Array(buffer);
|
||||
for (i = 0; i < ua.length; i++) {
|
||||
str += this.b2hexstr(ua[i]);
|
||||
}
|
||||
return str;
|
||||
},
|
||||
|
||||
/**
|
||||
* Convert byte to hexstr.
|
||||
* @param {number} byte Byte to convert
|
||||
*/
|
||||
b2hexstr: function (byte) {
|
||||
return ('00' + byte.toString(16)).substr(-2);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {ArrayBuffer} buffer
|
||||
* @returns {string}
|
||||
*/
|
||||
ab2str: function (buffer) {
|
||||
return String.fromCharCode.apply(null, new Uint8Array(buffer));
|
||||
}
|
||||
};
|
||||
});
|
64
assets/webconfig/js/app/views/EffectsView.js
Normal file
64
assets/webconfig/js/app/views/EffectsView.js
Normal file
@ -0,0 +1,64 @@
|
||||
/**
|
||||
* hyperion remote
|
||||
* MIT License
|
||||
*/
|
||||
|
||||
define(['lib/stapes'], function (Stapes) {
|
||||
'use strict';
|
||||
|
||||
return Stapes.subclass(/** @lends EffectsView.prototype */{
|
||||
/**
|
||||
* @class EffectsView
|
||||
* @constructs
|
||||
*/
|
||||
constructor: function () {
|
||||
this.bindEventHandlers();
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
bindEventHandlers: function () {
|
||||
window.addClickHandler(document.querySelector('#effects ul'), function (event) {
|
||||
var selected = event.target.parentNode.querySelector('.selected');
|
||||
if (selected) {
|
||||
selected.classList.remove('selected');
|
||||
}
|
||||
event.target.classList.add('selected');
|
||||
this.emit('effectSelected', event.target.dataset.id);
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Clear the list
|
||||
*/
|
||||
clear: function () {
|
||||
document.querySelector('#effects ul').innerHTML = '';
|
||||
document.querySelector('#effects .info').classList.add('hidden');
|
||||
},
|
||||
|
||||
/**
|
||||
* Fill the list
|
||||
* @param {object} effects - Object containing effect information
|
||||
*/
|
||||
fillList: function (effects) {
|
||||
var dom, el, i;
|
||||
|
||||
dom = document.createDocumentFragment();
|
||||
|
||||
for (i = 0; i < effects.length; i++) {
|
||||
el = document.createElement('li');
|
||||
el.innerHTML = effects[i].name;
|
||||
el.dataset.id = effects[i].id;
|
||||
dom.appendChild(el);
|
||||
}
|
||||
|
||||
document.querySelector('#effects ul').appendChild(dom);
|
||||
|
||||
if (effects.length === 0) {
|
||||
document.querySelector('#effects .info').classList.remove('hidden');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
396
assets/webconfig/js/app/views/MainView.js
Normal file
396
assets/webconfig/js/app/views/MainView.js
Normal file
@ -0,0 +1,396 @@
|
||||
define([
|
||||
'lib/stapes',
|
||||
'lib/tinycolor',
|
||||
'utils/Tools',
|
||||
'views/Slider'
|
||||
], function (Stapes, Tinycolor, tools, Slider) {
|
||||
'use strict';
|
||||
var timer;
|
||||
|
||||
function showMessageField (text, type) {
|
||||
var dom, wrapper;
|
||||
|
||||
dom = document.querySelector('.work .msg');
|
||||
|
||||
if (!dom) {
|
||||
dom = document.createElement('div');
|
||||
dom.classList.add('msg');
|
||||
dom.classList.add(type);
|
||||
wrapper = document.createElement('div');
|
||||
wrapper.classList.add('wrapper_msg');
|
||||
wrapper.classList.add('invisible');
|
||||
wrapper.appendChild(dom);
|
||||
|
||||
document.querySelector('.work').appendChild(wrapper);
|
||||
setTimeout(function () {
|
||||
wrapper.classList.remove('invisible');
|
||||
}, 0);
|
||||
}
|
||||
|
||||
if (!dom.classList.contains(type)) {
|
||||
dom.className = 'msg';
|
||||
dom.classList.add(type);
|
||||
}
|
||||
dom.innerHTML = text;
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
timer = setTimeout(function () {
|
||||
var error = document.querySelector('.work .wrapper_msg');
|
||||
if (error) {
|
||||
error.parentNode.removeChild(error);
|
||||
}
|
||||
}, 1600);
|
||||
}
|
||||
|
||||
return Stapes.subclass(/** @lends MainView.prototype*/{
|
||||
pointer: null,
|
||||
colorpicker: null,
|
||||
slider: null,
|
||||
sliderinput: null,
|
||||
cpradius: 0,
|
||||
cpcenter: 0,
|
||||
drag: false,
|
||||
color: null,
|
||||
brightness: 1.0,
|
||||
inputbox: null,
|
||||
|
||||
/**
|
||||
* @class MainView
|
||||
* @construct
|
||||
* @fires barClick
|
||||
* @fires colorChange
|
||||
*/
|
||||
constructor: function () {
|
||||
var ev;
|
||||
this.pointer = document.querySelector('#colorpicker #pointer');
|
||||
this.colorpicker = document.querySelector('#colorpicker #colorwheelbg');
|
||||
this.slider = new Slider({
|
||||
element: document.getElementById('brightness'),
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 0.02,
|
||||
value: 1
|
||||
});
|
||||
this.inputbox = document.querySelector('#color input.value');
|
||||
|
||||
this.cpradius = this.colorpicker.offsetWidth / 2;
|
||||
this.cpcenter = this.colorpicker.offsetLeft + this.cpradius;
|
||||
|
||||
this.bindEventHandlers();
|
||||
|
||||
ev = document.createEvent('Event');
|
||||
ev.initEvent('resize', true, true);
|
||||
window.dispatchEvent(ev);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
bindEventHandlers: function () {
|
||||
var cptouchrect;
|
||||
|
||||
window.addEventListener('resize', function () {
|
||||
var attrW, attrH, side, w = this.colorpicker.parentNode.clientWidth, h = this.colorpicker.parentNode.clientHeight;
|
||||
|
||||
attrW = this.colorpicker.getAttribute('width');
|
||||
attrH = this.colorpicker.getAttribute('height');
|
||||
side = attrW === 'auto' ? attrH : attrW;
|
||||
if (w > h) {
|
||||
if (attrH !== side) {
|
||||
this.colorpicker.setAttribute('height', side);
|
||||
this.colorpicker.setAttribute('width', 'auto');
|
||||
}
|
||||
} else if (attrW !== side) {
|
||||
this.colorpicker.setAttribute('height', 'auto');
|
||||
this.colorpicker.setAttribute('width', side);
|
||||
}
|
||||
|
||||
this.cpradius = this.colorpicker.offsetWidth / 2;
|
||||
this.cpcenter = this.colorpicker.offsetLeft + this.cpradius;
|
||||
if (this.color) {
|
||||
this.updatePointer();
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
window.addClickHandler(document.querySelector('.footer'), function (event) {
|
||||
this.emit('barClick', event.target.parentNode.dataset.area);
|
||||
}.bind(this));
|
||||
|
||||
|
||||
this.slider.on('changeValue', function (value) {
|
||||
this.brightness = value.value;
|
||||
this.updateInput();
|
||||
this.fireColorEvent();
|
||||
}, this);
|
||||
|
||||
this.inputbox.addEventListener('input', function (event) {
|
||||
var bright, rgb = new Tinycolor(event.target.value).toRgb();
|
||||
|
||||
if (rgb.r === 0 && rgb.g === 0 && rgb.b === 0) {
|
||||
this.brightness = 0;
|
||||
this.color = new Tinycolor({
|
||||
r: 0xff,
|
||||
g: 0xff,
|
||||
b: 0xff
|
||||
});
|
||||
} else {
|
||||
bright = Math.max(rgb.r, rgb.g, rgb.b) / 256;
|
||||
rgb.r = Math.round(rgb.r / bright);
|
||||
rgb.g = Math.round(rgb.g / bright);
|
||||
rgb.b = Math.round(rgb.b / bright);
|
||||
this.brightness = bright;
|
||||
this.color = new Tinycolor(rgb);
|
||||
}
|
||||
|
||||
this.fireColorEvent();
|
||||
this.updatePointer();
|
||||
this.updateSlider();
|
||||
}.bind(this), false);
|
||||
|
||||
this.inputbox.addEventListener('keydown', function (event) {
|
||||
switch (event.keyCode) {
|
||||
case 8:
|
||||
case 9:
|
||||
case 16:
|
||||
case 37:
|
||||
case 38:
|
||||
case 39:
|
||||
case 40:
|
||||
case 46:
|
||||
break;
|
||||
default:
|
||||
{
|
||||
if (event.target.value.length >= 6 && (event.target.selectionEnd - event.target.selectionStart) === 0) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
} else if (event.keyCode < 48 || event.keyCode > 71) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
cptouchrect = document.querySelector('#colorpicker .touchrect');
|
||||
window.addPointerDownHandler(cptouchrect, function (event) {
|
||||
this.leaveInput();
|
||||
this.drag = true;
|
||||
this.handleEvent(event);
|
||||
}.bind(this));
|
||||
|
||||
window.addPointerMoveHandler(cptouchrect, function (event) {
|
||||
if (this.drag) {
|
||||
this.handleEvent(event);
|
||||
event.preventDefault();
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
window.addPointerUpHandler(cptouchrect, function () {
|
||||
this.drag = false;
|
||||
}.bind(this));
|
||||
|
||||
window.addClickHandler(document.querySelector('#clear_button'), function () {
|
||||
this.emit('clear');
|
||||
}.bind(this));
|
||||
|
||||
window.addClickHandler(document.querySelector('#clearall_button'), function () {
|
||||
this.emit('clearall');
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param event
|
||||
*/
|
||||
handleEvent: function (event) {
|
||||
var point, x, y;
|
||||
|
||||
if (event.touches) {
|
||||
x = event.touches[0].clientX;
|
||||
y = event.touches[0].clientY;
|
||||
} else {
|
||||
x = event.clientX;
|
||||
y = event.clientY;
|
||||
}
|
||||
point = this.getCirclePoint(x, y);
|
||||
this.color = this.getColorFromPoint(point);
|
||||
|
||||
this.updatePointer();
|
||||
this.updateSlider();
|
||||
this.updateInput();
|
||||
|
||||
this.fireColorEvent();
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {number} x
|
||||
* @param {number} y
|
||||
* @returns {{x: number, y: number}}
|
||||
*/
|
||||
getCirclePoint: function (x, y) {
|
||||
var p = {
|
||||
x: x,
|
||||
y: y
|
||||
}, c = {
|
||||
x: this.colorpicker.offsetLeft + this.cpradius,
|
||||
y: this.colorpicker.offsetTop + this.cpradius
|
||||
}, n;
|
||||
|
||||
n = Math.sqrt(Math.pow((x - c.x), 2) + Math.pow((y - c.y), 2));
|
||||
|
||||
if (n > this.cpradius) {
|
||||
p.x = (c.x) + this.cpradius * ((x - c.x) / n);
|
||||
p.y = (c.y) + this.cpradius * ((y - c.y) / n);
|
||||
}
|
||||
|
||||
return p;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {{x: number, y: number}} p
|
||||
* @returns {Tinycolor}
|
||||
*/
|
||||
getColorFromPoint: function (p) {
|
||||
var h, t, s, x, y;
|
||||
x = p.x - this.colorpicker.offsetLeft - this.cpradius;
|
||||
y = this.cpradius - p.y + this.colorpicker.offsetTop;
|
||||
t = Math.atan2(y, x);
|
||||
h = (t * (180 / Math.PI) + 360) % 360;
|
||||
s = Math.min(Math.sqrt(x * x + y * y) / this.cpradius, 1);
|
||||
|
||||
return new Tinycolor({
|
||||
h: h,
|
||||
s: s,
|
||||
v: 1
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param color
|
||||
* @returns {{x: number, y: number}}
|
||||
*/
|
||||
getPointFromColor: function (color) {
|
||||
var t, x, y, p = {};
|
||||
|
||||
t = color.h * (Math.PI / 180);
|
||||
y = Math.sin(t) * this.cpradius * color.s;
|
||||
x = Math.cos(t) * this.cpradius * color.s;
|
||||
|
||||
p.x = Math.round(x + this.colorpicker.offsetLeft + this.cpradius);
|
||||
p.y = Math.round(this.cpradius - y + this.colorpicker.offsetTop);
|
||||
|
||||
return p;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
fireColorEvent: function () {
|
||||
var rgb = this.color.toRgb();
|
||||
rgb.r = Math.round(rgb.r * this.brightness);
|
||||
rgb.g = Math.round(rgb.g * this.brightness);
|
||||
rgb.b = Math.round(rgb.b * this.brightness);
|
||||
this.emit('colorChange', rgb);
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param rgb
|
||||
*/
|
||||
setColor: function (rgb) {
|
||||
var bright;
|
||||
|
||||
if (rgb.r === 0 && rgb.g === 0 && rgb.b === 0) {
|
||||
this.brightness = 0;
|
||||
this.color = new Tinycolor({
|
||||
r: 0xff,
|
||||
g: 0xff,
|
||||
b: 0xff
|
||||
});
|
||||
} else {
|
||||
bright = Math.max(rgb.r, rgb.g, rgb.b) / 256;
|
||||
rgb.r = Math.round(rgb.r / bright);
|
||||
rgb.g = Math.round(rgb.g / bright);
|
||||
rgb.b = Math.round(rgb.b / bright);
|
||||
this.brightness = bright;
|
||||
this.color = new Tinycolor(rgb);
|
||||
}
|
||||
|
||||
this.updatePointer();
|
||||
this.updateSlider();
|
||||
this.updateInput();
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
updateSlider: function () {
|
||||
this.slider.setValue(this.brightness);
|
||||
this.slider.dom.style.backgroundImage = '-webkit-linear-gradient(left, #000000 0%, ' + this.color.toHexString() + ' 100%)';
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
updatePointer: function () {
|
||||
var point = this.getPointFromColor(this.color.toHsv());
|
||||
|
||||
this.pointer.style.left = (point.x - this.pointer.offsetWidth / 2) + 'px';
|
||||
this.pointer.style.top = (point.y - this.pointer.offsetHeight / 2) + 'px';
|
||||
this.pointer.style.backgroundColor = this.color.toHexString();
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
updateInput: function () {
|
||||
var rgb = this.color.toRgb();
|
||||
rgb.r = Math.round(rgb.r * this.brightness);
|
||||
rgb.g = Math.round(rgb.g * this.brightness);
|
||||
rgb.b = Math.round(rgb.b * this.brightness);
|
||||
|
||||
this.inputbox.value = tools.b2hexstr(rgb.r) + tools.b2hexstr(rgb.g) + tools.b2hexstr(rgb.b);
|
||||
},
|
||||
|
||||
/**
|
||||
* Scroll to the specific tab content
|
||||
* @param {string} id - Id of the tab to scroll to
|
||||
*/
|
||||
scrollToArea: function (id) {
|
||||
var area, index;
|
||||
document.querySelector('.footer .selected').classList.remove('selected');
|
||||
document.querySelector('.footer .button[data-area =' + id + ']').classList.add('selected');
|
||||
|
||||
area = document.getElementById(id);
|
||||
index = area.offsetLeft / area.clientWidth;
|
||||
area.parentNode.style.left = (-index * 100) + '%';
|
||||
},
|
||||
|
||||
/**
|
||||
* Shows a status message
|
||||
* @param {string} message - Text to show
|
||||
*/
|
||||
showStatus: function (message) {
|
||||
showMessageField(message, 'status');
|
||||
},
|
||||
|
||||
/**
|
||||
* Shows the error text
|
||||
* @param {string} error - Error message
|
||||
*/
|
||||
showError: function (error) {
|
||||
showMessageField(error, 'error');
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
leaveInput: function () {
|
||||
this.inputbox.blur();
|
||||
}
|
||||
});
|
||||
});
|
199
assets/webconfig/js/app/views/ServerList.js
Normal file
199
assets/webconfig/js/app/views/ServerList.js
Normal file
@ -0,0 +1,199 @@
|
||||
define(['lib/stapes'], function (Stapes) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
*
|
||||
* @param params
|
||||
* @returns {HTMLElement}
|
||||
*/
|
||||
function buildDom (params) {
|
||||
var dom, label, ul, li, i, header;
|
||||
|
||||
dom = document.createElement('div');
|
||||
dom.classList.add('grouplist');
|
||||
dom.id = params.id;
|
||||
|
||||
header = document.createElement('div');
|
||||
header.classList.add('header');
|
||||
dom.appendChild(header);
|
||||
label = document.createElement('div');
|
||||
label.innerHTML = params.label;
|
||||
label.classList.add('title');
|
||||
header.appendChild(label);
|
||||
label = document.createElement('div');
|
||||
label.innerHTML = '';
|
||||
label.classList.add('callout');
|
||||
header.appendChild(label);
|
||||
|
||||
ul = document.createElement('ul');
|
||||
dom.appendChild(ul);
|
||||
if (params.list) {
|
||||
for (i = 0; i < params.list.length; i++) {
|
||||
li = createLine(params.list[i]);
|
||||
ul.appendChild(li);
|
||||
}
|
||||
}
|
||||
|
||||
return dom;
|
||||
}
|
||||
|
||||
function createLine (params) {
|
||||
var dom, el, horiz, box, touch;
|
||||
|
||||
dom = document.createDocumentFragment();
|
||||
|
||||
horiz = document.createElement('div');
|
||||
horiz.classList.add('horizontal');
|
||||
dom.appendChild(horiz);
|
||||
|
||||
touch = document.createElement('div');
|
||||
touch.classList.add('horizontal');
|
||||
horiz.appendChild(touch);
|
||||
|
||||
el = document.createElement('div');
|
||||
el.classList.add('checkbox');
|
||||
touch.appendChild(el);
|
||||
|
||||
box = document.createElement('div');
|
||||
box.classList.add('titlebox');
|
||||
touch.appendChild(box);
|
||||
|
||||
el = document.createElement('label');
|
||||
el.classList.add('title');
|
||||
el.innerHTML = params.title || '';
|
||||
box.appendChild(el);
|
||||
|
||||
el = document.createElement('label');
|
||||
el.classList.add('subtitle');
|
||||
el.innerHTML = params.subtitle;
|
||||
box.appendChild(el);
|
||||
|
||||
el = document.createElement('div');
|
||||
el.classList.add('touchrect');
|
||||
touch.appendChild(el);
|
||||
|
||||
el = document.createElement('div');
|
||||
el.classList.add('edit_icon');
|
||||
el.innerHTML = '';
|
||||
horiz.appendChild(el);
|
||||
|
||||
el = document.createElement('div');
|
||||
el.classList.add('delete_icon');
|
||||
el.innerHTML = '';
|
||||
horiz.appendChild(el);
|
||||
|
||||
return dom;
|
||||
}
|
||||
|
||||
return Stapes.subclass(/** @lends ServerList.prototype */{
|
||||
/**
|
||||
* @private
|
||||
* @type {HTMLElement}
|
||||
*/
|
||||
dom: null,
|
||||
|
||||
/**
|
||||
* @class ServerList
|
||||
* @constructs
|
||||
* @param {object} params - List parameter
|
||||
* @param {string} params.id - List id
|
||||
* @param {string} params.label - List title
|
||||
* @param {{title: string, subtitle: string, c}[]} [params.list] - List elements
|
||||
*
|
||||
* @fires add
|
||||
* @fires remove
|
||||
* @fires select
|
||||
* @fires edit
|
||||
*/
|
||||
constructor: function (params) {
|
||||
this.dom = buildDom(params || {});
|
||||
this.bindEventHandlers();
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
bindEventHandlers: function () {
|
||||
window.addClickHandler(this.dom.querySelector('.callout'), function () {
|
||||
this.emit('add');
|
||||
}.bind(this));
|
||||
|
||||
window.addClickHandler(this.dom.querySelector('ul'), function (event) {
|
||||
if (event.target.classList.contains('delete_icon')) {
|
||||
this.emit('remove', event.target.parentNode.parentNode.id);
|
||||
} else if (event.target.classList.contains('edit_icon')) {
|
||||
this.emit('edit', event.target.parentNode.parentNode.id);
|
||||
} else if (event.target.classList.contains('touchrect')) {
|
||||
this.emit('select', event.target.parentNode.parentNode.parentNode.id);
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the DOM of the list
|
||||
* @returns {HTMLElement}
|
||||
*/
|
||||
getDom: function () {
|
||||
return this.dom;
|
||||
},
|
||||
|
||||
/**
|
||||
* Append a line
|
||||
* @param id
|
||||
* @param selected
|
||||
* @param element
|
||||
*/
|
||||
append: function (id, selected, element) {
|
||||
var li = document.createElement('li');
|
||||
li.id = id;
|
||||
if (selected) {
|
||||
li.classList.add('selected');
|
||||
}
|
||||
li.appendChild(element);
|
||||
this.dom.querySelector('ul').appendChild(li);
|
||||
},
|
||||
|
||||
/**
|
||||
* Replace a line
|
||||
* @param index
|
||||
* @param element
|
||||
*/
|
||||
replace: function (index, element) {
|
||||
var child = this.dom.querySelector('ul li:nth-child(' + (index + 1) + ')'), li;
|
||||
if (child) {
|
||||
li = document.createElement('li');
|
||||
li.appendChild(element);
|
||||
this.dom.querySelector('ul').replaceChild(li, child);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Add a line
|
||||
* @param lineParam
|
||||
*/
|
||||
addLine: function (lineParam) {
|
||||
var line = createLine(lineParam);
|
||||
this.append(lineParam.id, lineParam.selected, line);
|
||||
},
|
||||
|
||||
/**
|
||||
* Clear the list
|
||||
*/
|
||||
clear: function () {
|
||||
this.dom.querySelector('ul').innerHTML = '';
|
||||
},
|
||||
|
||||
/**
|
||||
* Hide or show the Add button in the header
|
||||
* @param {boolean} show - True to show, false to hide
|
||||
*/
|
||||
showAddButton: function (show) {
|
||||
if (show) {
|
||||
this.dom.querySelector('.callout').classList.remove('invisible');
|
||||
} else {
|
||||
this.dom.querySelector('.callout').classList.add('invisible');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
259
assets/webconfig/js/app/views/SettingsView.js
Normal file
259
assets/webconfig/js/app/views/SettingsView.js
Normal file
@ -0,0 +1,259 @@
|
||||
define(['lib/stapes', 'views/ServerList'], function (Stapes, ServerList) {
|
||||
'use strict';
|
||||
|
||||
function createLabelInputLine (params) {
|
||||
var dom, el;
|
||||
|
||||
dom = document.createElement('div');
|
||||
dom.classList.add('inputline');
|
||||
dom.id = params.id;
|
||||
|
||||
el = document.createElement('label');
|
||||
el.innerHTML = params.label;
|
||||
dom.appendChild(el);
|
||||
|
||||
el = document.createElement('input');
|
||||
if (typeof params.value === 'number') {
|
||||
el.type = 'number';
|
||||
}
|
||||
el.value = params.value || '';
|
||||
el.autocomplete='off';
|
||||
el.autocorrect='off';
|
||||
el.autocapitalize='off';
|
||||
dom.appendChild(el);
|
||||
|
||||
return dom;
|
||||
}
|
||||
|
||||
function createButtonLine (params) {
|
||||
var dom, el;
|
||||
|
||||
dom = document.createElement('div');
|
||||
dom.classList.add('inputline');
|
||||
|
||||
el = document.createElement('button');
|
||||
el.innerHTML = params.label;
|
||||
el.classList.add('OK');
|
||||
dom.appendChild(el);
|
||||
|
||||
el = document.createElement('button');
|
||||
el.innerHTML = 'Cancel';
|
||||
el.classList.add('CANCEL');
|
||||
dom.appendChild(el);
|
||||
|
||||
return dom;
|
||||
}
|
||||
|
||||
function createDetectLine () {
|
||||
var dom, el;
|
||||
|
||||
dom = document.createElement('div');
|
||||
dom.classList.add('line');
|
||||
|
||||
el = document.createElement('button');
|
||||
el.id = 'detect_button';
|
||||
el.innerHTML = 'Detect';
|
||||
dom.appendChild(el);
|
||||
|
||||
el = document.createElement('div');
|
||||
el.classList.add('spinner');
|
||||
el.classList.add('hidden');
|
||||
dom.appendChild(el);
|
||||
|
||||
return dom;
|
||||
}
|
||||
|
||||
return Stapes.subclass(/** @lends SettingsView.prototype */{
|
||||
dom: null,
|
||||
serverList: null,
|
||||
|
||||
/**
|
||||
* @class SettingsView
|
||||
* @classdesc View for the settings
|
||||
* @constructs
|
||||
*/
|
||||
constructor: function () {
|
||||
var list = [], el;
|
||||
|
||||
this.dom = document.querySelector('#settings');
|
||||
|
||||
this.serverList = new ServerList({
|
||||
id: 'serverList',
|
||||
label: 'Server',
|
||||
list: list
|
||||
});
|
||||
|
||||
this.serverList.on({
|
||||
add: function () {
|
||||
var line, box;
|
||||
|
||||
this.enableDetectButton(false);
|
||||
this.lockList(true);
|
||||
|
||||
box = document.createDocumentFragment();
|
||||
|
||||
line = createLabelInputLine({id: 'name', label: 'Name:'});
|
||||
box.appendChild(line);
|
||||
line = createLabelInputLine({id: 'address', label: 'Address:'});
|
||||
box.appendChild(line);
|
||||
line = createLabelInputLine({id: 'port', label: 'Port:', value: 19444});
|
||||
box.appendChild(line);
|
||||
line = createLabelInputLine({id: 'priority', label: 'Priority:', value: 50});
|
||||
box.appendChild(line);
|
||||
line = createLabelInputLine({id: 'duration', label: 'Duration (sec):', value: 0});
|
||||
box.appendChild(line);
|
||||
|
||||
line = createButtonLine({label: 'Add'});
|
||||
|
||||
window.addClickHandler(line.firstChild, function (event) {
|
||||
var server = {}, i, inputs = event.target.parentNode.parentNode.querySelectorAll('input');
|
||||
|
||||
for (i = 0; i < inputs.length; i++) {
|
||||
server[inputs[i].parentNode.id] = inputs[i].value;
|
||||
}
|
||||
|
||||
server.port = parseInt(server.port);
|
||||
server.priority = parseInt(server.priority);
|
||||
server.duration = parseInt(server.duration);
|
||||
|
||||
this.emit('serverAdded', server);
|
||||
}.bind(this));
|
||||
|
||||
window.addClickHandler(line.lastChild, function () {
|
||||
this.emit('serverAddCanceled');
|
||||
}.bind(this));
|
||||
box.appendChild(line);
|
||||
this.serverList.append(null, false, box);
|
||||
},
|
||||
select: function (id) {
|
||||
if (!this.dom.classList.contains('locked')) {
|
||||
this.emit('serverSelected', parseInt(id.replace('server_', '')));
|
||||
}
|
||||
},
|
||||
remove: function (id) {
|
||||
this.emit('serverRemoved', parseInt(id.replace('server_', '')));
|
||||
},
|
||||
edit: function (id) {
|
||||
this.emit('editServer', parseInt(id.replace('server_', '')));
|
||||
}
|
||||
}, this);
|
||||
|
||||
this.dom.appendChild(this.serverList.getDom());
|
||||
|
||||
el = createDetectLine();
|
||||
|
||||
window.addClickHandler(el.querySelector('button'), function () {
|
||||
this.emit('detect');
|
||||
}.bind(this));
|
||||
|
||||
this.dom.appendChild(el);
|
||||
},
|
||||
|
||||
/**
|
||||
* Fills the list of known servers
|
||||
* @param {Array} servers
|
||||
*/
|
||||
fillServerList: function (servers) {
|
||||
var i, server, params;
|
||||
this.serverList.clear();
|
||||
for (i = 0; i < servers.length; i++) {
|
||||
server = servers[i];
|
||||
params = {id: 'server_' + i, title: server.name, subtitle: server.address + ':' + server.port};
|
||||
if (server.selected) {
|
||||
params.selected = true;
|
||||
}
|
||||
this.serverList.addLine(params);
|
||||
}
|
||||
this.serverList.showAddButton(true);
|
||||
},
|
||||
|
||||
/**
|
||||
* Shows or hides the spinner as progress indicator
|
||||
* @param {boolean} show True to show, false to hide
|
||||
*/
|
||||
showWaiting: function (show) {
|
||||
if (show) {
|
||||
this.dom.querySelector('.spinner').classList.remove('hidden');
|
||||
} else {
|
||||
this.dom.querySelector('.spinner').classList.add('hidden');
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Enables or disables the detect button
|
||||
* @param {Boolean} enabled True to enable, false to disable
|
||||
*/
|
||||
enableDetectButton: function (enabled) {
|
||||
if (enabled) {
|
||||
this.dom.querySelector('#detect_button').classList.remove('hidden');
|
||||
} else {
|
||||
this.dom.querySelector('#detect_button').classList.add('hidden');
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Locks the list for editing/deleting
|
||||
* @param {Boolean} lock True to lock, false to unlock
|
||||
*/
|
||||
lockList: function (lock) {
|
||||
if (!lock) {
|
||||
this.dom.classList.remove('locked');
|
||||
this.serverList.showAddButton(true);
|
||||
} else {
|
||||
this.dom.classList.add('locked');
|
||||
this.serverList.showAddButton(false);
|
||||
}
|
||||
},
|
||||
|
||||
editServer: function (serverInfo) {
|
||||
var line, box;
|
||||
|
||||
this.lockList(true);
|
||||
this.enableDetectButton(false);
|
||||
|
||||
box = document.createDocumentFragment();
|
||||
|
||||
line = createLabelInputLine({id: 'name', label: 'Name:', value: serverInfo.server.name});
|
||||
box.appendChild(line);
|
||||
line = createLabelInputLine({id: 'address', label: 'Address:', value: serverInfo.server.address});
|
||||
box.appendChild(line);
|
||||
line = createLabelInputLine({id: 'port', label: 'Port:', value: serverInfo.server.port});
|
||||
box.appendChild(line);
|
||||
line = createLabelInputLine({id: 'priority', label: 'Priority:', value: serverInfo.server.priority});
|
||||
box.appendChild(line);
|
||||
line = createLabelInputLine({id: 'duration', label: 'Duration (sec):', value: serverInfo.server.duration});
|
||||
box.appendChild(line);
|
||||
|
||||
line = createButtonLine({label: 'Done'});
|
||||
|
||||
window.addClickHandler(line.querySelector('button.OK'), function (event) {
|
||||
var server = {}, i, inputs = event.target.parentNode.parentNode.querySelectorAll('input');
|
||||
|
||||
for (i = 0; i < inputs.length; i++) {
|
||||
server[inputs[i].parentNode.id] = inputs[i].value;
|
||||
}
|
||||
|
||||
server.port = parseInt(server.port);
|
||||
server.priority = parseInt(server.priority);
|
||||
server.duration = parseInt(server.duration);
|
||||
if (serverInfo.server.selected) {
|
||||
server.selected = true;
|
||||
}
|
||||
|
||||
this.emit('serverChanged', {index: serverInfo.index, server: server});
|
||||
}.bind(this));
|
||||
|
||||
window.addClickHandler(line.querySelector('button.CANCEL'), function () {
|
||||
this.emit('serverEditCanceled');
|
||||
}.bind(this));
|
||||
box.appendChild(line);
|
||||
|
||||
window.addClickHandler(box.querySelector('input'), function (event) {
|
||||
event.stopPropagation();
|
||||
});
|
||||
|
||||
this.serverList.replace(serverInfo.index, box);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
134
assets/webconfig/js/app/views/Slider.js
Normal file
134
assets/webconfig/js/app/views/Slider.js
Normal file
@ -0,0 +1,134 @@
|
||||
define(['lib/stapes'], function (Stapes) {
|
||||
'use strict';
|
||||
|
||||
function syncView (slider) {
|
||||
var left = (slider.value - slider.min) * 100 / (slider.max - slider.min);
|
||||
slider.dom.lastElementChild.style.left = left + '%';
|
||||
}
|
||||
|
||||
function handleEvent(event, slider) {
|
||||
var x, left, ratio, value, steppedValue;
|
||||
if (event.touches) {
|
||||
x = event.touches[0].clientX;
|
||||
} else {
|
||||
x = event.clientX;
|
||||
}
|
||||
|
||||
left = x - slider.dom.getBoundingClientRect().left;
|
||||
ratio = left / slider.dom.offsetWidth;
|
||||
value = (slider.max - slider.min) * ratio;
|
||||
|
||||
steppedValue = (value - slider.min) % slider.step;
|
||||
if (steppedValue <= slider.step / 2) {
|
||||
value = value - steppedValue;
|
||||
} else {
|
||||
value = value + (slider.step - steppedValue);
|
||||
}
|
||||
|
||||
value = Math.max(value, slider.min);
|
||||
value = Math.min(value, slider.max);
|
||||
slider.value = value;
|
||||
slider.emit('changeValue', {
|
||||
'value': value,
|
||||
'target': slider.dom
|
||||
});
|
||||
}
|
||||
|
||||
return Stapes.subclass(/** @lends Slider.prototype */{
|
||||
/**
|
||||
* @private
|
||||
* @type {Element}
|
||||
*/
|
||||
dom: null,
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {Number}
|
||||
*/
|
||||
min: 0,
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {Number}
|
||||
*/
|
||||
max: 100,
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {Number}
|
||||
*/
|
||||
value: 0,
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {Number}
|
||||
*/
|
||||
step: 1,
|
||||
|
||||
/**
|
||||
* @class Slider
|
||||
* @constructs
|
||||
* @fires change
|
||||
*/
|
||||
constructor: function (params) {
|
||||
this.dom = params.element;
|
||||
this.setValue(params.value);
|
||||
this.setMin(params.min);
|
||||
this.setMax(params.max);
|
||||
this.setStep(params.step);
|
||||
|
||||
this.bindEventHandlers();
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
bindEventHandlers: function () {
|
||||
var that = this;
|
||||
|
||||
function pointerMoveEventHandler(event) {
|
||||
if (that.drag) {
|
||||
handleEvent(event, that);
|
||||
syncView(that);
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
function pointerUpEventHandler() {
|
||||
that.drag = false;
|
||||
syncView(that);
|
||||
window.removePointerMoveHandler(document, pointerMoveEventHandler);
|
||||
window.removePointerUpHandler(document, pointerUpEventHandler);
|
||||
}
|
||||
|
||||
window.addPointerDownHandler(this.dom, function (event) {
|
||||
this.drag = true;
|
||||
handleEvent(event, this);
|
||||
syncView(this);
|
||||
window.addPointerMoveHandler(document, pointerMoveEventHandler);
|
||||
window.addPointerUpHandler(document, pointerUpEventHandler);
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
setValue: function(value) {
|
||||
this.value = value || 0;
|
||||
syncView(this);
|
||||
},
|
||||
|
||||
setMin: function(value) {
|
||||
this.min = value || 0;
|
||||
syncView(this);
|
||||
},
|
||||
|
||||
setMax: function(value) {
|
||||
this.max = value || 100;
|
||||
syncView(this);
|
||||
},
|
||||
|
||||
setStep: function(value) {
|
||||
this.step = value || 1;
|
||||
syncView(this);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
375
assets/webconfig/js/app/views/TransformView.js
Normal file
375
assets/webconfig/js/app/views/TransformView.js
Normal file
@ -0,0 +1,375 @@
|
||||
/**
|
||||
* hyperion remote
|
||||
* MIT License
|
||||
*/
|
||||
|
||||
define([
|
||||
'lib/stapes',
|
||||
'views/Slider'
|
||||
], function (Stapes, Slider) {
|
||||
'use strict';
|
||||
|
||||
function onHeaderClick (event) {
|
||||
var list = event.target.parentNode.parentNode.querySelector('ul');
|
||||
|
||||
if (list.clientHeight === 0) {
|
||||
list.style.maxHeight = list.scrollHeight + 'px';
|
||||
event.target.parentNode.parentNode.setAttribute('collapsed', 'false');
|
||||
} else {
|
||||
list.style.maxHeight = 0;
|
||||
event.target.parentNode.parentNode.setAttribute('collapsed', 'true');
|
||||
}
|
||||
}
|
||||
|
||||
function createLine (id, type, icon, caption, value, min, max) {
|
||||
var dom, el, el2, label, wrapper;
|
||||
|
||||
dom = document.createElement('li');
|
||||
dom.className = type;
|
||||
|
||||
label = document.createElement('label');
|
||||
label.innerHTML = caption;
|
||||
dom.appendChild(label);
|
||||
|
||||
wrapper = document.createElement('div');
|
||||
wrapper.classList.add('wrapper');
|
||||
wrapper.id = id;
|
||||
|
||||
el = document.createElement('div');
|
||||
el.classList.add('icon');
|
||||
el.innerHTML = icon;
|
||||
wrapper.appendChild(el);
|
||||
|
||||
el = document.createElement('div');
|
||||
el.classList.add('slider');
|
||||
el2 = document.createElement('div');
|
||||
el2.classList.add('track');
|
||||
el.appendChild(el2);
|
||||
el2 = document.createElement('div');
|
||||
el2.classList.add('thumb');
|
||||
el.appendChild(el2);
|
||||
el.dataset.min = min;
|
||||
el.dataset.max = max;
|
||||
el.dataset.value = value;
|
||||
el.dataset.step = 0.01;
|
||||
wrapper.appendChild(el);
|
||||
|
||||
el = document.createElement('input');
|
||||
el.classList.add('value');
|
||||
el.type = 'number';
|
||||
el.min = min;
|
||||
el.max = max;
|
||||
el.step = 0.01;
|
||||
el.value = parseFloat(Math.round(value * 100) / 100).toFixed(2);
|
||||
wrapper.appendChild(el);
|
||||
|
||||
dom.appendChild(wrapper);
|
||||
|
||||
return dom;
|
||||
}
|
||||
|
||||
function createGroup (groupInfo) {
|
||||
var group, node, subnode, i, member;
|
||||
group = document.createElement('div');
|
||||
group.classList.add('group');
|
||||
if (groupInfo.collapsed) {
|
||||
group.setAttribute('collapsed', 'true');
|
||||
}
|
||||
group.id = groupInfo.id;
|
||||
|
||||
node = document.createElement('div');
|
||||
node.classList.add('header');
|
||||
group.appendChild(node);
|
||||
subnode = document.createElement('label');
|
||||
subnode.innerHTML = groupInfo.title;
|
||||
node.appendChild(subnode);
|
||||
subnode = document.createElement('label');
|
||||
subnode.innerHTML = groupInfo.subtitle;
|
||||
node.appendChild(subnode);
|
||||
|
||||
node = document.createElement('ul');
|
||||
group.appendChild(node);
|
||||
for (i = 0; i < groupInfo.members.length; i++) {
|
||||
member = groupInfo.members[i];
|
||||
subnode = createLine(member.id, member.type, member.icon, member.label, member.value, member.min,
|
||||
member.max);
|
||||
node.appendChild(subnode);
|
||||
}
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
return Stapes.subclass(/** @lends TransformView.prototype */{
|
||||
sliders: {},
|
||||
|
||||
/**
|
||||
* @class TransformView
|
||||
* @constructs
|
||||
*/
|
||||
constructor: function () {
|
||||
},
|
||||
|
||||
/**
|
||||
* Clear the list
|
||||
*/
|
||||
clear: function () {
|
||||
document.querySelector('#transform .values').innerHTML = '';
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param change
|
||||
*/
|
||||
onSliderChange: function (event) {
|
||||
var data = {}, idparts, value;
|
||||
|
||||
idparts = event.target.parentNode.id.split('_');
|
||||
value = parseFloat(Math.round(parseFloat(event.value) * 100) / 100);
|
||||
|
||||
event.target.parentNode.querySelector('.value').value = value.toFixed(2);
|
||||
|
||||
data[idparts[1]] = value;
|
||||
this.emit(idparts[0], data);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param change
|
||||
*/
|
||||
onValueChange: function (event) {
|
||||
var data = {}, idparts, value;
|
||||
|
||||
idparts = event.target.parentNode.id.split('_');
|
||||
value = parseFloat(Math.round(parseFloat(event.target.value) * 100) / 100);
|
||||
|
||||
if (parseFloat(event.target.value) < parseFloat(event.target.min)) {
|
||||
event.target.value = event.target.min;
|
||||
} else if (parseFloat(event.target.value) > parseFloat(event.target.max)) {
|
||||
event.target.value = event.target.max;
|
||||
}
|
||||
this.sliders[event.target.parentNode.id].setValue(value);
|
||||
|
||||
data[idparts[1]] = value;
|
||||
this.emit(idparts[0], data);
|
||||
},
|
||||
|
||||
/**
|
||||
* fill the list
|
||||
* @param {object} transform - Object containing transform information
|
||||
*/
|
||||
fillList: function (transform) {
|
||||
var dom, group, els, i, slider;
|
||||
|
||||
if (!transform) {
|
||||
document.querySelector('#transform .info').classList.remove('hidden');
|
||||
return;
|
||||
}
|
||||
|
||||
dom = document.createDocumentFragment();
|
||||
|
||||
group = createGroup({
|
||||
collapsed: true,
|
||||
id: 'HSV',
|
||||
title: 'HSV',
|
||||
subtitle: 'HSV color corrections',
|
||||
members: [
|
||||
{
|
||||
id: 'hsv_saturationGain',
|
||||
type: 'saturation',
|
||||
icon: '',
|
||||
label: 'Saturation gain',
|
||||
value: transform.saturationGain,
|
||||
min: 0,
|
||||
max: 5
|
||||
},
|
||||
{
|
||||
id: 'hsv_valueGain',
|
||||
type: 'value',
|
||||
icon: '',
|
||||
label: 'Value gain',
|
||||
value: transform.valueGain,
|
||||
min: 0,
|
||||
max: 5
|
||||
}
|
||||
]
|
||||
});
|
||||
dom.appendChild(group);
|
||||
|
||||
group = createGroup({
|
||||
collapsed: true,
|
||||
id: 'Gamma',
|
||||
title: 'Gamma',
|
||||
subtitle: 'Gamma correction',
|
||||
members: [
|
||||
{
|
||||
id: 'gamma_r',
|
||||
type: 'red',
|
||||
icon: '',
|
||||
label: 'Red',
|
||||
value: transform.gamma[0],
|
||||
min: 0,
|
||||
max: 5
|
||||
},
|
||||
{
|
||||
id: 'gamma_g',
|
||||
type: 'green',
|
||||
icon: '',
|
||||
label: 'Green',
|
||||
value: transform.gamma[1],
|
||||
min: 0,
|
||||
max: 5
|
||||
},
|
||||
{
|
||||
id: 'gamma_b',
|
||||
type: 'blue',
|
||||
icon: '',
|
||||
label: 'Blue',
|
||||
value: transform.gamma[2],
|
||||
min: 0,
|
||||
max: 5
|
||||
}
|
||||
]
|
||||
});
|
||||
dom.appendChild(group);
|
||||
|
||||
group = createGroup({
|
||||
collapsed: true,
|
||||
id: 'Whitelevel',
|
||||
title: 'Whitelevel',
|
||||
subtitle: 'Value when RGB channel is fully on',
|
||||
members: [
|
||||
{
|
||||
id: 'whitelevel_r',
|
||||
type: 'red',
|
||||
icon: '',
|
||||
label: 'Red',
|
||||
value: transform.whitelevel[0],
|
||||
min: 0,
|
||||
max: 1
|
||||
},
|
||||
{
|
||||
id: 'whitelevel_g',
|
||||
type: 'green',
|
||||
icon: '',
|
||||
label: 'Green',
|
||||
value: transform.whitelevel[1],
|
||||
min: 0,
|
||||
max: 1
|
||||
},
|
||||
{
|
||||
id: 'whitelevel_b',
|
||||
type: 'blue',
|
||||
icon: '',
|
||||
label: 'Blue',
|
||||
value: transform.whitelevel[2],
|
||||
min: 0,
|
||||
max: 1
|
||||
}
|
||||
]
|
||||
});
|
||||
dom.appendChild(group);
|
||||
|
||||
group = createGroup({
|
||||
collapsed: true,
|
||||
id: 'Blacklevel',
|
||||
title: 'Blacklevel',
|
||||
subtitle: 'Value when RGB channel is fully off',
|
||||
members: [
|
||||
{
|
||||
id: 'blacklevel_r',
|
||||
type: 'red',
|
||||
icon: '',
|
||||
label: 'Red',
|
||||
value: transform.blacklevel[0],
|
||||
min: 0,
|
||||
max: 1
|
||||
},
|
||||
{
|
||||
id: 'blacklevel_g',
|
||||
type: 'green',
|
||||
icon: '',
|
||||
label: 'Green',
|
||||
value: transform.blacklevel[1],
|
||||
min: 0,
|
||||
max: 1
|
||||
},
|
||||
{
|
||||
id: 'blacklevel_b',
|
||||
type: 'blue',
|
||||
icon: '',
|
||||
label: 'Blue',
|
||||
value: transform.blacklevel[2],
|
||||
min: 0,
|
||||
max: 1
|
||||
}
|
||||
]
|
||||
});
|
||||
dom.appendChild(group);
|
||||
|
||||
group = createGroup({
|
||||
collapsed: true,
|
||||
id: 'Threshold',
|
||||
title: 'Threshold',
|
||||
subtitle: 'Threshold for a channel',
|
||||
members: [
|
||||
{
|
||||
id: 'threshold_r',
|
||||
type: 'red',
|
||||
icon: '',
|
||||
label: 'Red',
|
||||
value: transform.threshold[0],
|
||||
min: 0,
|
||||
max: 1
|
||||
},
|
||||
{
|
||||
id: 'threshold_g',
|
||||
type: 'green',
|
||||
icon: '',
|
||||
label: 'Green',
|
||||
value: transform.threshold[1],
|
||||
min: 0,
|
||||
max: 1
|
||||
},
|
||||
{
|
||||
id: 'threshold_b',
|
||||
type: 'blue',
|
||||
icon: '',
|
||||
label: 'Blue',
|
||||
value: transform.threshold[2],
|
||||
min: 0,
|
||||
max: 1
|
||||
}
|
||||
]
|
||||
});
|
||||
dom.appendChild(group);
|
||||
|
||||
els = dom.querySelectorAll('.slider');
|
||||
for (i = 0; i < els.length; i++) {
|
||||
slider = new Slider({
|
||||
element: els[i],
|
||||
min: els[i].dataset.min,
|
||||
max: els[i].dataset.max,
|
||||
step: els[i].dataset.step,
|
||||
value: els[i].dataset.value
|
||||
});
|
||||
slider.on('changeValue', this.onSliderChange, this);
|
||||
this.sliders[els[i].parentNode.id] = slider;
|
||||
}
|
||||
|
||||
els = dom.querySelectorAll('input');
|
||||
for (i = 0; i < els.length; i++) {
|
||||
els[i].addEventListener('input', this.onValueChange.bind(this), false);
|
||||
}
|
||||
|
||||
els = dom.querySelectorAll('.header');
|
||||
for (i = 0; i < els.length; i++) {
|
||||
window.addClickHandler(els[i], onHeaderClick);
|
||||
}
|
||||
|
||||
document.querySelector('#transform .info').classList.add('hidden');
|
||||
document.querySelector('#transform .values').appendChild(dom);
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
|
17
assets/webconfig/js/background.js
Normal file
17
assets/webconfig/js/background.js
Normal file
@ -0,0 +1,17 @@
|
||||
/*global chrome */
|
||||
chrome.app.runtime.onLaunched.addListener(function () {
|
||||
'use strict';
|
||||
chrome.app.window.create('index.html', {
|
||||
'id': 'fakeIdForSingleton',
|
||||
'innerBounds': {
|
||||
'width': 320,
|
||||
'height': 480,
|
||||
'minWidth': 320,
|
||||
'minHeight': 480
|
||||
},
|
||||
resizable: false
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
2054
assets/webconfig/js/vendor/require.js
vendored
Normal file
2054
assets/webconfig/js/vendor/require.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
594
assets/webconfig/js/vendor/stapes.js
vendored
Normal file
594
assets/webconfig/js/vendor/stapes.js
vendored
Normal file
@ -0,0 +1,594 @@
|
||||
//
|
||||
// ____ _ _
|
||||
// / ___|| |_ __ _ _ __ ___ ___ (_)___ (*)
|
||||
// \___ \| __/ _` | '_ \ / _ \/ __| | / __|
|
||||
// ___) | || (_| | |_) | __/\__ \_ | \__ \
|
||||
// |____/ \__\__,_| .__/ \___||___(_)/ |___/
|
||||
// |_| |__/
|
||||
//
|
||||
// (*) a (really) tiny Javascript MVC microframework
|
||||
//
|
||||
// (c) Hay Kranen < hay@bykr.org >
|
||||
// Released under the terms of the MIT license
|
||||
// < http://en.wikipedia.org/wiki/MIT_License >
|
||||
//
|
||||
// Stapes.js : http://hay.github.com/stapes
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
var VERSION = "0.8.0";
|
||||
|
||||
// Global counter for all events in all modules (including mixed in objects)
|
||||
var guid = 1;
|
||||
|
||||
// Makes _.create() faster
|
||||
if (!Object.create) {
|
||||
var CachedFunction = function(){};
|
||||
}
|
||||
|
||||
// So we can use slice.call for arguments later on
|
||||
var slice = Array.prototype.slice;
|
||||
|
||||
// Private attributes and helper functions, stored in an object so they
|
||||
// are overwritable by plugins
|
||||
var _ = {
|
||||
// Properties
|
||||
attributes : {},
|
||||
|
||||
eventHandlers : {
|
||||
"-1" : {} // '-1' is used for the global event handling
|
||||
},
|
||||
|
||||
guid : -1,
|
||||
|
||||
// Methods
|
||||
addEvent : function(event) {
|
||||
// If we don't have any handlers for this type of event, add a new
|
||||
// array we can use to push new handlers
|
||||
if (!_.eventHandlers[event.guid][event.type]) {
|
||||
_.eventHandlers[event.guid][event.type] = [];
|
||||
}
|
||||
|
||||
// Push an event object
|
||||
_.eventHandlers[event.guid][event.type].push({
|
||||
"guid" : event.guid,
|
||||
"handler" : event.handler,
|
||||
"scope" : event.scope,
|
||||
"type" : event.type
|
||||
});
|
||||
},
|
||||
|
||||
addEventHandler : function(argTypeOrMap, argHandlerOrScope, argScope) {
|
||||
var eventMap = {},
|
||||
scope;
|
||||
|
||||
if (typeof argTypeOrMap === "string") {
|
||||
scope = argScope || false;
|
||||
eventMap[ argTypeOrMap ] = argHandlerOrScope;
|
||||
} else {
|
||||
scope = argHandlerOrScope || false;
|
||||
eventMap = argTypeOrMap;
|
||||
}
|
||||
|
||||
for (var eventString in eventMap) {
|
||||
var handler = eventMap[eventString];
|
||||
var events = eventString.split(" ");
|
||||
|
||||
for (var i = 0, l = events.length; i < l; i++) {
|
||||
var eventType = events[i];
|
||||
_.addEvent.call(this, {
|
||||
"guid" : this._guid || this._.guid,
|
||||
"handler" : handler,
|
||||
"scope" : scope,
|
||||
"type" : eventType
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
addGuid : function(object, forceGuid) {
|
||||
if (object._guid && !forceGuid) return;
|
||||
|
||||
object._guid = guid++;
|
||||
|
||||
_.attributes[object._guid] = {};
|
||||
_.eventHandlers[object._guid] = {};
|
||||
},
|
||||
|
||||
// This is a really small utility function to save typing and produce
|
||||
// better optimized code
|
||||
attr : function(guid) {
|
||||
return _.attributes[guid];
|
||||
},
|
||||
|
||||
clone : function(obj) {
|
||||
var type = _.typeOf(obj);
|
||||
|
||||
if (type === 'object') {
|
||||
return _.extend({}, obj);
|
||||
}
|
||||
|
||||
if (type === 'array') {
|
||||
return obj.slice(0);
|
||||
}
|
||||
},
|
||||
|
||||
create : function(proto) {
|
||||
if (Object.create) {
|
||||
return Object.create(proto);
|
||||
} else {
|
||||
CachedFunction.prototype = proto;
|
||||
return new CachedFunction();
|
||||
}
|
||||
},
|
||||
|
||||
createSubclass : function(props, includeEvents) {
|
||||
props = props || {};
|
||||
includeEvents = includeEvents || false;
|
||||
|
||||
var superclass = props.superclass.prototype;
|
||||
|
||||
// Objects always have a constructor, so we need to be sure this is
|
||||
// a property instead of something from the prototype
|
||||
var realConstructor = props.hasOwnProperty('constructor') ? props.constructor : function(){};
|
||||
|
||||
function constructor() {
|
||||
// Be kind to people forgetting new
|
||||
if (!(this instanceof constructor)) {
|
||||
throw new Error("Please use 'new' when initializing Stapes classes");
|
||||
}
|
||||
|
||||
// If this class has events add a GUID as well
|
||||
if (this.on) {
|
||||
_.addGuid( this, true );
|
||||
}
|
||||
|
||||
realConstructor.apply(this, arguments);
|
||||
}
|
||||
|
||||
if (includeEvents) {
|
||||
_.extend(superclass, Events);
|
||||
}
|
||||
|
||||
constructor.prototype = _.create(superclass);
|
||||
constructor.prototype.constructor = constructor;
|
||||
|
||||
_.extend(constructor, {
|
||||
extend : function() {
|
||||
return _.extendThis.apply(this, arguments);
|
||||
},
|
||||
|
||||
// We can't call this 'super' because that's a reserved keyword
|
||||
// and fails in IE8
|
||||
'parent' : superclass,
|
||||
|
||||
proto : function() {
|
||||
return _.extendThis.apply(this.prototype, arguments);
|
||||
},
|
||||
|
||||
subclass : function(obj) {
|
||||
obj = obj || {};
|
||||
obj.superclass = this;
|
||||
return _.createSubclass(obj);
|
||||
}
|
||||
});
|
||||
|
||||
// Copy all props given in the definition to the prototype
|
||||
for (var key in props) {
|
||||
if (key !== 'constructor' && key !== 'superclass') {
|
||||
constructor.prototype[key] = props[key];
|
||||
}
|
||||
}
|
||||
|
||||
return constructor;
|
||||
},
|
||||
|
||||
emitEvents : function(type, data, explicitType, explicitGuid) {
|
||||
explicitType = explicitType || false;
|
||||
explicitGuid = explicitGuid || this._guid;
|
||||
|
||||
// #30: make a local copy of handlers to prevent problems with
|
||||
// unbinding the event while unwinding the loop
|
||||
var handlers = slice.call(_.eventHandlers[explicitGuid][type]);
|
||||
|
||||
for (var i = 0, l = handlers.length; i < l; i++) {
|
||||
// Clone the event to prevent issue #19
|
||||
var event = _.extend({}, handlers[i]);
|
||||
var scope = (event.scope) ? event.scope : this;
|
||||
|
||||
if (explicitType) {
|
||||
event.type = explicitType;
|
||||
}
|
||||
|
||||
event.scope = scope;
|
||||
event.handler.call(event.scope, data, event);
|
||||
}
|
||||
},
|
||||
|
||||
// Extend an object with more objects
|
||||
extend : function() {
|
||||
var args = slice.call(arguments);
|
||||
var object = args.shift();
|
||||
|
||||
for (var i = 0, l = args.length; i < l; i++) {
|
||||
var props = args[i];
|
||||
for (var key in props) {
|
||||
object[key] = props[key];
|
||||
}
|
||||
}
|
||||
|
||||
return object;
|
||||
},
|
||||
|
||||
// The same as extend, but uses the this value as the scope
|
||||
extendThis : function() {
|
||||
var args = slice.call(arguments);
|
||||
args.unshift(this);
|
||||
return _.extend.apply(this, args);
|
||||
},
|
||||
|
||||
// from http://stackoverflow.com/a/2117523/152809
|
||||
makeUuid : function() {
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
||||
var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
|
||||
return v.toString(16);
|
||||
});
|
||||
},
|
||||
|
||||
removeAttribute : function(keys, silent) {
|
||||
silent = silent || false;
|
||||
|
||||
// Split the key, maybe we want to remove more than one item
|
||||
var attributes = _.trim(keys).split(" ");
|
||||
|
||||
// Actually delete the item
|
||||
for (var i = 0, l = attributes.length; i < l; i++) {
|
||||
var key = _.trim(attributes[i]);
|
||||
|
||||
if (key) {
|
||||
delete _.attr(this._guid)[key];
|
||||
|
||||
// If 'silent' is set, do not throw any events
|
||||
if (!silent) {
|
||||
this.emit('change', key);
|
||||
this.emit('change:' + key);
|
||||
this.emit('remove', key);
|
||||
this.emit('remove:' + key);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
removeEventHandler : function(type, handler) {
|
||||
var handlers = _.eventHandlers[this._guid];
|
||||
|
||||
if (type && handler) {
|
||||
// Remove a specific handler
|
||||
handlers = handlers[type];
|
||||
if (!handlers) return;
|
||||
|
||||
for (var i = 0, l = handlers.length, h; i < l; i++) {
|
||||
h = handlers[i].handler;
|
||||
if (h && h === handler) {
|
||||
handlers.splice(i--, 1);
|
||||
l--;
|
||||
}
|
||||
}
|
||||
} else if (type) {
|
||||
// Remove all handlers for a specific type
|
||||
delete handlers[type];
|
||||
} else {
|
||||
// Remove all handlers for this module
|
||||
_.eventHandlers[this._guid] = {};
|
||||
}
|
||||
},
|
||||
|
||||
setAttribute : function(key, value, silent) {
|
||||
silent = silent || false;
|
||||
|
||||
// We need to do this before we actually add the item :)
|
||||
var itemExists = this.has(key);
|
||||
var oldValue = _.attr(this._guid)[key];
|
||||
|
||||
// Is the value different than the oldValue? If not, ignore this call
|
||||
if (value === oldValue) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Actually add the item to the attributes
|
||||
_.attr(this._guid)[key] = value;
|
||||
|
||||
// If 'silent' flag is set, do not throw any events
|
||||
if (silent) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Throw a generic event
|
||||
this.emit('change', key);
|
||||
|
||||
// And a namespaced event as well, NOTE that we pass value instead of
|
||||
// key here!
|
||||
this.emit('change:' + key, value);
|
||||
|
||||
// Throw namespaced and non-namespaced 'mutate' events as well with
|
||||
// the old value data as well and some extra metadata such as the key
|
||||
var mutateData = {
|
||||
"key" : key,
|
||||
"newValue" : value,
|
||||
"oldValue" : oldValue || null
|
||||
};
|
||||
|
||||
this.emit('mutate', mutateData);
|
||||
this.emit('mutate:' + key, mutateData);
|
||||
|
||||
// Also throw a specific event for this type of set
|
||||
var specificEvent = itemExists ? 'update' : 'create';
|
||||
|
||||
this.emit(specificEvent, key);
|
||||
|
||||
// And a namespaced event as well, NOTE that we pass value instead of key
|
||||
this.emit(specificEvent + ':' + key, value);
|
||||
},
|
||||
|
||||
trim : function(str) {
|
||||
return str.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
|
||||
},
|
||||
|
||||
typeOf : function(val) {
|
||||
if (val === null || typeof val === "undefined") {
|
||||
// This is a special exception for IE, in other browsers the
|
||||
// method below works all the time
|
||||
return String(val);
|
||||
} else {
|
||||
return Object.prototype.toString.call(val).replace(/\[object |\]/g, '').toLowerCase();
|
||||
}
|
||||
},
|
||||
|
||||
updateAttribute : function(key, fn, silent) {
|
||||
var item = this.get(key);
|
||||
|
||||
// In previous versions of Stapes we didn't have the check for object,
|
||||
// but still this worked. In 0.7.0 it suddenly doesn't work anymore and
|
||||
// we need the check. Why? I have no clue.
|
||||
var type = _.typeOf(item);
|
||||
|
||||
if (type === 'object' || type === 'array') {
|
||||
item = _.clone(item);
|
||||
}
|
||||
|
||||
var newValue = fn.call(this, item, key);
|
||||
_.setAttribute.call(this, key, newValue, silent || false);
|
||||
}
|
||||
};
|
||||
|
||||
// Can be mixed in later using Stapes.mixinEvents(object);
|
||||
var Events = {
|
||||
emit : function(types, data) {
|
||||
data = (typeof data === "undefined") ? null : data;
|
||||
|
||||
var splittedTypes = types.split(" ");
|
||||
|
||||
for (var i = 0, l = splittedTypes.length; i < l; i++) {
|
||||
var type = splittedTypes[i];
|
||||
|
||||
// First 'all' type events: is there an 'all' handler in the
|
||||
// global stack?
|
||||
if (_.eventHandlers[-1].all) {
|
||||
_.emitEvents.call(this, "all", data, type, -1);
|
||||
}
|
||||
|
||||
// Catch all events for this type?
|
||||
if (_.eventHandlers[-1][type]) {
|
||||
_.emitEvents.call(this, type, data, type, -1);
|
||||
}
|
||||
|
||||
if (typeof this._guid === 'number') {
|
||||
// 'all' event for this specific module?
|
||||
if (_.eventHandlers[this._guid].all) {
|
||||
_.emitEvents.call(this, "all", data, type);
|
||||
}
|
||||
|
||||
// Finally, normal events :)
|
||||
if (_.eventHandlers[this._guid][type]) {
|
||||
_.emitEvents.call(this, type, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
off : function() {
|
||||
_.removeEventHandler.apply(this, arguments);
|
||||
},
|
||||
|
||||
on : function() {
|
||||
_.addEventHandler.apply(this, arguments);
|
||||
}
|
||||
};
|
||||
|
||||
_.Module = function() {
|
||||
|
||||
};
|
||||
|
||||
_.Module.prototype = {
|
||||
each : function(fn, ctx) {
|
||||
var attr = _.attr(this._guid);
|
||||
for (var key in attr) {
|
||||
var value = attr[key];
|
||||
fn.call(ctx || this, value, key);
|
||||
}
|
||||
},
|
||||
|
||||
extend : function() {
|
||||
return _.extendThis.apply(this, arguments);
|
||||
},
|
||||
|
||||
filter : function(fn) {
|
||||
var filtered = [];
|
||||
var attributes = _.attr(this._guid);
|
||||
|
||||
for (var key in attributes) {
|
||||
if ( fn.call(this, attributes[key], key)) {
|
||||
filtered.push( attributes[key] );
|
||||
}
|
||||
}
|
||||
|
||||
return filtered;
|
||||
},
|
||||
|
||||
get : function(input) {
|
||||
if (typeof input === "string") {
|
||||
return this.has(input) ? _.attr(this._guid)[input] : null;
|
||||
} else if (typeof input === "function") {
|
||||
var items = this.filter(input);
|
||||
return (items.length) ? items[0] : null;
|
||||
}
|
||||
},
|
||||
|
||||
getAll : function() {
|
||||
return _.clone( _.attr(this._guid) );
|
||||
},
|
||||
|
||||
getAllAsArray : function() {
|
||||
var arr = [];
|
||||
var attributes = _.attr(this._guid);
|
||||
|
||||
for (var key in attributes) {
|
||||
var value = attributes[key];
|
||||
|
||||
if (_.typeOf(value) === "object" && !value.id) {
|
||||
value.id = key;
|
||||
}
|
||||
|
||||
arr.push(value);
|
||||
}
|
||||
|
||||
return arr;
|
||||
},
|
||||
|
||||
has : function(key) {
|
||||
return (typeof _.attr(this._guid)[key] !== "undefined");
|
||||
},
|
||||
|
||||
map : function(fn, ctx) {
|
||||
var mapped = [];
|
||||
this.each(function(value, key) {
|
||||
mapped.push( fn.call(ctx || this, value, key) );
|
||||
}, ctx || this);
|
||||
return mapped;
|
||||
},
|
||||
|
||||
// Akin to set(), but makes a unique id
|
||||
push : function(input, silent) {
|
||||
if (_.typeOf(input) === "array") {
|
||||
for (var i = 0, l = input.length; i < l; i++) {
|
||||
_.setAttribute.call(this, _.makeUuid(), input[i], silent || false);
|
||||
}
|
||||
} else {
|
||||
_.setAttribute.call(this, _.makeUuid(), input, silent || false);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
remove : function(input, silent) {
|
||||
if (typeof input === 'undefined') {
|
||||
// With no arguments, remove deletes all attributes
|
||||
_.attributes[this._guid] = {};
|
||||
this.emit('change remove');
|
||||
} else if (typeof input === "function") {
|
||||
this.each(function(item, key) {
|
||||
if (input(item)) {
|
||||
_.removeAttribute.call(this, key, silent);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// nb: checking for exists happens in removeAttribute
|
||||
_.removeAttribute.call(this, input, silent || false);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
set : function(objOrKey, valueOrSilent, silent) {
|
||||
if (typeof objOrKey === "object") {
|
||||
for (var key in objOrKey) {
|
||||
_.setAttribute.call(this, key, objOrKey[key], valueOrSilent || false);
|
||||
}
|
||||
} else {
|
||||
_.setAttribute.call(this, objOrKey, valueOrSilent, silent || false);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
size : function() {
|
||||
var size = 0;
|
||||
var attr = _.attr(this._guid);
|
||||
|
||||
for (var key in attr) {
|
||||
size++;
|
||||
}
|
||||
|
||||
return size;
|
||||
},
|
||||
|
||||
update : function(keyOrFn, fn, silent) {
|
||||
if (typeof keyOrFn === "string") {
|
||||
_.updateAttribute.call(this, keyOrFn, fn, silent || false);
|
||||
} else if (typeof keyOrFn === "function") {
|
||||
this.each(function(value, key) {
|
||||
_.updateAttribute.call(this, key, keyOrFn);
|
||||
});
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
var Stapes = {
|
||||
"_" : _, // private helper functions and properties
|
||||
|
||||
"extend" : function() {
|
||||
return _.extendThis.apply(_.Module.prototype, arguments);
|
||||
},
|
||||
|
||||
"mixinEvents" : function(obj) {
|
||||
obj = obj || {};
|
||||
|
||||
_.addGuid(obj);
|
||||
|
||||
return _.extend(obj, Events);
|
||||
},
|
||||
|
||||
"on" : function() {
|
||||
_.addEventHandler.apply(this, arguments);
|
||||
},
|
||||
|
||||
"subclass" : function(obj, classOnly) {
|
||||
classOnly = classOnly || false;
|
||||
obj = obj || {};
|
||||
obj.superclass = classOnly ? function(){} : _.Module;
|
||||
return _.createSubclass(obj, !classOnly);
|
||||
},
|
||||
|
||||
"version" : VERSION
|
||||
};
|
||||
|
||||
// This library can be used as an AMD module, a Node.js module, or an
|
||||
// old fashioned global
|
||||
if (typeof exports !== "undefined") {
|
||||
// Server
|
||||
if (typeof module !== "undefined" && module.exports) {
|
||||
exports = module.exports = Stapes;
|
||||
}
|
||||
exports.Stapes = Stapes;
|
||||
} else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(function() {
|
||||
return Stapes;
|
||||
});
|
||||
} else {
|
||||
// Global scope
|
||||
window.Stapes = Stapes;
|
||||
}
|
||||
})();
|
1107
assets/webconfig/js/vendor/tinycolor.js
vendored
Normal file
1107
assets/webconfig/js/vendor/tinycolor.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
32
assets/webconfig/manifest.json
Normal file
32
assets/webconfig/manifest.json
Normal file
@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "hyperion remote control",
|
||||
"short_name": "hyperion remote",
|
||||
"description": "Client side app for controlling the hyperion server over the local network.",
|
||||
"author": {
|
||||
"name": "Daniel Wiese",
|
||||
"email": "gamadril.dev@gmail.com"
|
||||
},
|
||||
"manifest_version": 2,
|
||||
"version": "0.6.0",
|
||||
"minimum_chrome_version": "36",
|
||||
"permissions": [
|
||||
"storage", "system.network"
|
||||
],
|
||||
"app": {
|
||||
"background": {
|
||||
"scripts": ["js/background.js"]
|
||||
}
|
||||
},
|
||||
"sockets": {
|
||||
"tcp": {
|
||||
"connect": "*"
|
||||
},
|
||||
"udp": {
|
||||
"send": "*",
|
||||
"bind": "*"
|
||||
}
|
||||
},
|
||||
"icons": {
|
||||
"128": "res/icon_128.png"
|
||||
}
|
||||
}
|
14
assets/webconfig/manifest.webapp
Normal file
14
assets/webconfig/manifest.webapp
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "hyperion remote control",
|
||||
"description": "Client side app for controlling the hyperion server over the local network.",
|
||||
"launch_path": "/hyperion-remote/index.html",
|
||||
"icons": {
|
||||
"128": "/hyperion-remote/res/icon_128.png"
|
||||
},
|
||||
"developer": {
|
||||
"name": "Daniel Wiese",
|
||||
"email": "gamadril.dev@gmail.com",
|
||||
"url": "https://github.com/Gamadril"
|
||||
},
|
||||
"default_locale": "en"
|
||||
}
|
BIN
assets/webconfig/res/colorwheel.png
Normal file
BIN
assets/webconfig/res/colorwheel.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 487 KiB |
BIN
assets/webconfig/res/fontello.ttf
Normal file
BIN
assets/webconfig/res/fontello.ttf
Normal file
Binary file not shown.
BIN
assets/webconfig/res/fontello.woff
Normal file
BIN
assets/webconfig/res/fontello.woff
Normal file
Binary file not shown.
BIN
assets/webconfig/res/icon_128.png
Normal file
BIN
assets/webconfig/res/icon_128.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
@ -20,7 +20,7 @@ install_file()
|
||||
echo "--- hyperion ambilight postinstall ---"
|
||||
echo "- install configuration template"
|
||||
mkdir -p /etc/hyperion
|
||||
install_file /usr/share/hyperion/config/hyperion.config.json /etc/hyperion/hyperion.config.json
|
||||
install_file /usr/share/hyperion/config/hyperion.config.json.example /etc/hyperion/hyperion.config.json
|
||||
|
||||
|
||||
HYPERION_RUNNING=false
|
||||
|
@ -42,7 +42,7 @@
|
||||
/// tuning parameters:
|
||||
/// - 'saturationGain' The gain adjustement of the saturation
|
||||
/// - 'luminanceGain' The gain adjustement of the luminance
|
||||
/// - 'luminanceMinimum' The minimum luminance (backlight)
|
||||
/// - 'luminanceMinimum' The minimum luminance (backlight)
|
||||
/// * 'red'/'green'/'blue' : The manipulation in the Red-Green-Blue color domain with the
|
||||
/// following tuning parameters for each channel:
|
||||
/// - 'threshold' The minimum required input value for the channel to be on
|
||||
@ -65,21 +65,21 @@
|
||||
"leds" : "*",
|
||||
"pureRed" :
|
||||
{
|
||||
"redChannel" : 255,
|
||||
"greenChannel" : 0,
|
||||
"blueChannel" : 0
|
||||
"redChannel" : 255,
|
||||
"greenChannel" : 0,
|
||||
"blueChannel" : 0
|
||||
},
|
||||
"pureGreen" :
|
||||
{
|
||||
"redChannel" : 0,
|
||||
"greenChannel" : 255,
|
||||
"blueChannel" : 0
|
||||
"redChannel" : 0,
|
||||
"greenChannel" : 255,
|
||||
"blueChannel" : 0
|
||||
},
|
||||
"pureBlue" :
|
||||
{
|
||||
"redChannel" : 0,
|
||||
"greenChannel" : 0,
|
||||
"blueChannel" : 255
|
||||
"redChannel" : 0,
|
||||
"greenChannel" : 0,
|
||||
"blueChannel" : 255
|
||||
}
|
||||
}
|
||||
],
|
||||
@ -90,9 +90,9 @@
|
||||
"leds" : "*",
|
||||
"temperatureValues" :
|
||||
{
|
||||
"red" : 255,
|
||||
"green" : 255,
|
||||
"blue" : 255
|
||||
"red" : 255,
|
||||
"green" : 255,
|
||||
"blue" : 255
|
||||
}
|
||||
}
|
||||
],
|
||||
@ -103,24 +103,24 @@
|
||||
"leds" : "*",
|
||||
"hsl" :
|
||||
{
|
||||
"saturationGain" : 1.0000,
|
||||
"luminanceGain" : 1.0000,
|
||||
"luminanceMinimum" : 0.0000
|
||||
"saturationGain" : 1.0000,
|
||||
"luminanceGain" : 1.0000,
|
||||
"luminanceMinimum" : 0.0000
|
||||
},
|
||||
"red" :
|
||||
{
|
||||
"threshold" : 0.0000,
|
||||
"gamma" : 2.5000
|
||||
"threshold" : 0.0000,
|
||||
"gamma" : 2.5000
|
||||
},
|
||||
"green" :
|
||||
{
|
||||
"threshold" : 0.0000,
|
||||
"gamma" : 2.5000
|
||||
"threshold" : 0.0000,
|
||||
"gamma" : 2.5000
|
||||
},
|
||||
"blue" :
|
||||
{
|
||||
"threshold" : 0.0000,
|
||||
"gamma" : 2.5000
|
||||
"threshold" : 0.0000,
|
||||
"gamma" : 2.5000
|
||||
}
|
||||
}
|
||||
],
|
||||
@ -135,13 +135,13 @@
|
||||
},
|
||||
|
||||
/// The black border configuration, contains the following items:
|
||||
/// * enable : true if the detector should be activated
|
||||
/// * threshold : Value below which a pixel is regarded as black (value between 0.0 and 1.0)
|
||||
/// * unknownFrameCnt : Number of frames without any detection before the border is set to 0 (default 600)
|
||||
/// * borderFrameCnt : Number of frames before a consistent detected border gets set (default 50)
|
||||
/// * maxInconsistentCnt : Number of inconsistent frames that are ignored before a new border gets a chance to proof consistency
|
||||
/// * blurRemoveCnt : Number of pixels that get removed from the detected border to cut away blur (default 1)
|
||||
/// * mode : Border detection mode (values=default,classic,osd)
|
||||
/// * enable : true if the detector should be activated
|
||||
/// * threshold : Value below which a pixel is regarded as black (value between 0.0 and 1.0)
|
||||
/// * unknownFrameCnt : Number of frames without any detection before the border is set to 0 (default 600)
|
||||
/// * borderFrameCnt : Number of frames before a consistent detected border gets set (default 50)
|
||||
/// * maxInconsistentCnt : Number of inconsistent frames that are ignored before a new border gets a chance to proof consistency
|
||||
/// * blurRemoveCnt : Number of pixels that get removed from the detected border to cut away blur (default 1)
|
||||
/// * mode : Border detection mode (values=default,classic,osd)
|
||||
"blackborderdetector" :
|
||||
{
|
||||
"enable" : true,
|
||||
@ -154,19 +154,23 @@
|
||||
},
|
||||
|
||||
/// The configuration of the effect engine, contains the following items:
|
||||
/// * paths : An array with absolute location(s) of directories with effects
|
||||
/// * color : Set static color after boot -> set effect to "" (empty) and input the values [R,G,B] and set duration_ms NOT to 0 (use 1) instead
|
||||
/// * effect : The effect selected as 'boot sequence'
|
||||
/// * duration_ms : The duration of the selected effect (0=endless)
|
||||
/// * priority : The priority of the selected effect/static color (default=990) HINT: lower value result in HIGHER priority!
|
||||
/// * paths : An array with absolute/relative location(s) of directories with effects
|
||||
"effects" :
|
||||
{
|
||||
"paths" :
|
||||
[
|
||||
"/opt/hyperion/effects"
|
||||
"/usr/share/hyperion/effects"
|
||||
]
|
||||
},
|
||||
|
||||
/// Boot sequence configuration. Start effect / set color at startup of hyperion
|
||||
/// HINT inital background color is not shown, when any other grabber is active
|
||||
/// * color : Set initial background color on startup -> set effect to "" (empty) and input the values [R,G,B] and set duration_ms NOT to 0 (use 1) instead
|
||||
/// * effect : The effect is shown when hyperion starts
|
||||
/// * duration_ms : The duration of the selected effect (0=endless)
|
||||
/// * priority : The priority of the selected effect/initial background color (default=990, if duration is 0)
|
||||
/// when duration > 0 => priority is set to 0, otherwise priority is set to configured value
|
||||
/// HINT: lower value result in HIGHER priority!
|
||||
"bootsequence" :
|
||||
{
|
||||
"color" : [0,0,0],
|
||||
@ -175,12 +179,23 @@
|
||||
"priority" : 990
|
||||
},
|
||||
|
||||
/// Configuration of webserver integrated in hyperion.
|
||||
/// * enable : enable the server or not
|
||||
/// * document_root : path to hyperion webapp files
|
||||
/// * port : the port where hyperion webapp is accasible
|
||||
"webConfig" :
|
||||
{
|
||||
"enable" : true,
|
||||
"document_root" : "/usr/share/hyperion/webconfig",
|
||||
"port" : 8080
|
||||
},
|
||||
|
||||
/// The configuration of the Json/Proto forwarder. Forward messages to multiple instances of Hyperion on same and/or other hosts
|
||||
/// 'proto' is mostly used for video streams and 'json' for effects
|
||||
/// * proto : Proto server adress and port of your target. Syntax:[IP:PORT] -> ["127.0.0.1:19447"] or more instances to forward ["127.0.0.1:19447","192.168.0.24:19449"]
|
||||
/// * json : Json server adress and port of your target. Syntax:[IP:PORT] -> ["127.0.0.1:19446"] or more instances to forward ["127.0.0.1:19446","192.168.0.24:19448"]
|
||||
/// HINT: If you redirect to "127.0.0.1" (localhost) you could start a second hyperion with another device/led config!
|
||||
/// Be sure your client(s) is/are listening on the configured ports. The second Hyperion (if used) also needs to be configured! (HyperCon -> External -> Json Server/Proto Server)
|
||||
/// * proto : Proto server adress and port of your target. Syntax:[IP:PORT] -> ["127.0.0.1:19447"] or more instances to forward ["127.0.0.1:19447","192.168.0.24:19449"]
|
||||
/// * json : Json server adress and port of your target. Syntax:[IP:PORT] -> ["127.0.0.1:19446"] or more instances to forward ["127.0.0.1:19446","192.168.0.24:19448"]
|
||||
/// HINT:If you redirect to "127.0.0.1" (localhost) you could start a second hyperion with another device/led config!
|
||||
/// Be sure your client(s) is/are listening on the configured ports. The second Hyperion (if used) also needs to be configured! (HyperCon -> External -> Json Server/Proto Server)
|
||||
"forwarder" :
|
||||
{
|
||||
"proto" : ["127.0.0.1:19447"],
|
||||
@ -239,8 +254,8 @@
|
||||
},
|
||||
|
||||
/// The configuration of the boblight server which enables the boblight remote interface
|
||||
/// * port : Port at which the boblight server is started
|
||||
/// * priority: Priority of the boblight server (Default=900) HINT: lower value result in HIGHER priority!
|
||||
/// * port : Port at which the boblight server is started
|
||||
/// * priority : Priority of the boblight server (Default=900) HINT: lower value result in HIGHER priority!
|
||||
"boblightServer" :
|
||||
{
|
||||
"port" : 19333,
|
||||
@ -248,9 +263,9 @@
|
||||
},
|
||||
|
||||
/// Configuration for the embedded V4L2 grabber
|
||||
/// * device : V4L2 Device to use [default="/dev/video0"]
|
||||
/// * input : V4L2 input to use [default=0]
|
||||
/// * standard : Video standard (no-change/PAL/NTSC) [default="no-change"]
|
||||
/// * device : V4L2 Device to use [default="/dev/video0"]
|
||||
/// * input : V4L2 input to use [default=0]
|
||||
/// * standard : Video standard (no-change/PAL/NTSC) [default="no-change"]
|
||||
/// * width : V4L2 width to set [default=-1]
|
||||
/// * height : V4L2 height to set [default=-1]
|
||||
/// * frameDecimation : Frame decimation factor [default=2]
|
||||
@ -264,25 +279,25 @@
|
||||
/// * redSignalThreshold : Signal threshold for the red channel between 0.0 and 1.0 [default=0.0]
|
||||
/// * greenSignalThreshold : Signal threshold for the green channel between 0.0 and 1.0 [default=0.0]
|
||||
/// * blueSignalThreshold : Signal threshold for the blue channel between 0.0 and 1.0 [default=0.0]
|
||||
"grabber-v4l2" :
|
||||
{
|
||||
"device" : "/dev/video0",
|
||||
"input" : 0,
|
||||
"standard" : "no-change",
|
||||
"width" : -1,
|
||||
"height" : -1,
|
||||
"frameDecimation" : 2,
|
||||
"sizeDecimation" : 8,
|
||||
"priority" : 900,
|
||||
"mode" : "2D",
|
||||
"cropLeft" : 0,
|
||||
"cropRight" : 0,
|
||||
"cropTop" : 0,
|
||||
"cropBottom" : 0,
|
||||
"redSignalThreshold" : 0.0,
|
||||
"greenSignalThreshold" : 0.0,
|
||||
"blueSignalThreshold" : 0.0
|
||||
},
|
||||
"grabber-v4l2" :
|
||||
{
|
||||
"device" : "/dev/video0",
|
||||
"input" : 0,
|
||||
"standard" : "no-change",
|
||||
"width" : -1,
|
||||
"height" : -1,
|
||||
"frameDecimation" : 2,
|
||||
"sizeDecimation" : 8,
|
||||
"priority" : 900,
|
||||
"mode" : "2D",
|
||||
"cropLeft" : 0,
|
||||
"cropRight" : 0,
|
||||
"cropTop" : 0,
|
||||
"cropBottom" : 0,
|
||||
"redSignalThreshold" : 0.0,
|
||||
"greenSignalThreshold" : 0.0,
|
||||
"blueSignalThreshold" : 0.0
|
||||
},
|
||||
|
||||
/// The configuration for each individual led. This contains the specification of the area
|
||||
/// averaged of an input image for each led to determine its color. Each item in the list
|
||||
|
27
include/webconfig/WebConfig.h
Normal file
27
include/webconfig/WebConfig.h
Normal file
@ -0,0 +1,27 @@
|
||||
#ifndef WEBCONFIG_H
|
||||
#define WEBCONFIG_H
|
||||
|
||||
#include <QObject>
|
||||
#include <string>
|
||||
|
||||
class StaticFileServing;
|
||||
|
||||
class WebConfig : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit WebConfig (std::string baseUrl, quint16 port, QObject * parent = NULL);
|
||||
virtual ~WebConfig (void);
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
private:
|
||||
QObject * _parent;
|
||||
QString _baseUrl;
|
||||
quint16 _port;
|
||||
StaticFileServing * _server;
|
||||
};
|
||||
|
||||
#endif // WEBCONFIG_H
|
||||
|
@ -19,3 +19,7 @@ add_subdirectory(utils)
|
||||
add_subdirectory(xbmcvideochecker)
|
||||
add_subdirectory(effectengine)
|
||||
add_subdirectory(grabber)
|
||||
|
||||
if(ENABLE_QT5)
|
||||
add_subdirectory(webconfig)
|
||||
endif()
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "hyperion/ImageProcessorFactory.h"
|
||||
#include "hyperion/ImageProcessor.h"
|
||||
#include "utils/ColorRgb.h"
|
||||
#include "HyperionConfig.h"
|
||||
|
||||
// project includes
|
||||
#include "BoblightClientConnection.h"
|
||||
|
@ -16,6 +16,7 @@
|
||||
// effect engine includes
|
||||
#include <effectengine/EffectEngine.h>
|
||||
#include "Effect.h"
|
||||
#include "HyperionConfig.h"
|
||||
|
||||
EffectEngine::EffectEngine(Hyperion * hyperion, const Json::Value & jsonEffectConfig) :
|
||||
_hyperion(hyperion),
|
||||
|
53
libsrc/webconfig/CMakeLists.txt
Normal file
53
libsrc/webconfig/CMakeLists.txt
Normal file
@ -0,0 +1,53 @@
|
||||
|
||||
# Define the current source locations
|
||||
set(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/webconfig)
|
||||
set(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/webconfig)
|
||||
|
||||
# Group the headers that go through the MOC compiler
|
||||
set(WebConfig_QT_HEADERS
|
||||
${CURRENT_SOURCE_DIR}/QtHttpClientWrapper.h
|
||||
${CURRENT_SOURCE_DIR}/QtHttpHeader.h
|
||||
${CURRENT_SOURCE_DIR}/QtHttpReply.h
|
||||
${CURRENT_SOURCE_DIR}/QtHttpRequest.h
|
||||
${CURRENT_SOURCE_DIR}/QtHttpServer.h
|
||||
${CURRENT_SOURCE_DIR}/StaticFileServing.h
|
||||
${CURRENT_HEADER_DIR}/WebConfig.h
|
||||
)
|
||||
|
||||
set(WebConfig_HEADERS
|
||||
)
|
||||
|
||||
set(WebConfig_SOURCES
|
||||
${CURRENT_SOURCE_DIR}/QtHttpClientWrapper.cpp
|
||||
${CURRENT_SOURCE_DIR}/QtHttpHeader.cpp
|
||||
${CURRENT_SOURCE_DIR}/QtHttpReply.cpp
|
||||
${CURRENT_SOURCE_DIR}/QtHttpRequest.cpp
|
||||
${CURRENT_SOURCE_DIR}/QtHttpServer.cpp
|
||||
${CURRENT_SOURCE_DIR}/StaticFileServing.cpp
|
||||
${CURRENT_SOURCE_DIR}/WebConfig.cpp
|
||||
)
|
||||
|
||||
if(ENABLE_QT5)
|
||||
qt5_wrap_cpp(WebConfig_HEADERS_MOC ${WebConfig_QT_HEADERS})
|
||||
else()
|
||||
qt4_wrap_cpp(WebConfigr_HEADERS_MOC ${WebConfig_QT_HEADERS})
|
||||
endif()
|
||||
|
||||
add_library(webconfig
|
||||
${WebConfig_HEADERS}
|
||||
${WebConfig_QT_HEADERS}
|
||||
${WebConfig_SOURCES}
|
||||
${WebConfig_HEADERS_MOC}
|
||||
)
|
||||
|
||||
if(ENABLE_QT5)
|
||||
qt5_use_modules(webconfig Widgets Network)
|
||||
endif()
|
||||
|
||||
target_link_libraries(webconfig
|
||||
hyperion
|
||||
hyperion-utils
|
||||
${QT_LIBRARIES}
|
||||
)
|
||||
|
||||
|
221
libsrc/webconfig/QtHttpClientWrapper.cpp
Normal file
221
libsrc/webconfig/QtHttpClientWrapper.cpp
Normal file
@ -0,0 +1,221 @@
|
||||
|
||||
#include "QtHttpClientWrapper.h"
|
||||
#include "QtHttpRequest.h"
|
||||
#include "QtHttpReply.h"
|
||||
#include "QtHttpServer.h"
|
||||
#include "QtHttpHeader.h"
|
||||
|
||||
#include <QCryptographicHash>
|
||||
#include <QTcpSocket>
|
||||
#include <QStringBuilder>
|
||||
#include <QStringList>
|
||||
#include <QDateTime>
|
||||
|
||||
const QByteArray & QtHttpClientWrapper::CRLF = QByteArrayLiteral ("\r\n");
|
||||
|
||||
QtHttpClientWrapper::QtHttpClientWrapper (QTcpSocket * sock, QtHttpServer * parent)
|
||||
: QObject (parent)
|
||||
, m_guid ("")
|
||||
, m_parsingStatus (AwaitingRequest)
|
||||
, m_sockClient (sock)
|
||||
, m_currentRequest (Q_NULLPTR)
|
||||
, m_serverHandle (parent)
|
||||
{
|
||||
connect (m_sockClient, &QTcpSocket::readyRead, this, &QtHttpClientWrapper::onClientDataReceived);
|
||||
}
|
||||
|
||||
QString QtHttpClientWrapper::getGuid (void) {
|
||||
if (m_guid.isEmpty ()) {
|
||||
m_guid = QString::fromLocal8Bit (
|
||||
QCryptographicHash::hash (
|
||||
QByteArray::number ((quint64) (this)),
|
||||
QCryptographicHash::Md5
|
||||
).toHex ()
|
||||
);
|
||||
}
|
||||
return m_guid;
|
||||
}
|
||||
|
||||
void QtHttpClientWrapper::onClientDataReceived (void) {
|
||||
if (m_sockClient != Q_NULLPTR) {
|
||||
while (m_sockClient->bytesAvailable ()) {
|
||||
QByteArray line = m_sockClient->readLine ();
|
||||
switch (m_parsingStatus) { // handle parsing steps
|
||||
case AwaitingRequest: { // "command url version" × 1
|
||||
QString str = QString::fromUtf8 (line).trimmed ();
|
||||
QStringList parts = str.split (SPACE, QString::SkipEmptyParts);
|
||||
if (parts.size () == 3) {
|
||||
QString command = parts.at (0);
|
||||
QString url = parts.at (1);
|
||||
QString version = parts.at (2);
|
||||
if (version == QtHttpServer::HTTP_VERSION) {
|
||||
//qDebug () << "Debug : HTTP"
|
||||
// << "command :" << command
|
||||
// << "url :" << url
|
||||
// << "version :" << version;
|
||||
m_currentRequest = new QtHttpRequest (m_serverHandle);
|
||||
m_currentRequest->setUrl (QUrl (url));
|
||||
m_currentRequest->setCommand (command);
|
||||
m_parsingStatus = AwaitingHeaders;
|
||||
}
|
||||
else {
|
||||
m_parsingStatus = ParsingError;
|
||||
//qWarning () << "Error : unhandled HTTP version :" << version;
|
||||
}
|
||||
}
|
||||
else {
|
||||
m_parsingStatus = ParsingError;
|
||||
//qWarning () << "Error : incorrect HTTP command line :" << line;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AwaitingHeaders: { // "header: value" × N (until empty line)
|
||||
QByteArray raw = line.trimmed ();
|
||||
if (!raw.isEmpty ()) { // parse headers
|
||||
int pos = raw.indexOf (COLON);
|
||||
if (pos > 0) {
|
||||
QByteArray header = raw.left (pos).trimmed ();
|
||||
QByteArray value = raw.mid (pos +1).trimmed ();
|
||||
//qDebug () << "Debug : HTTP"
|
||||
// << "header :" << header
|
||||
// << "value :" << value;
|
||||
m_currentRequest->addHeader (header, value);
|
||||
if (header == QtHttpHeader::ContentLength) {
|
||||
int len = -1;
|
||||
bool ok = false;
|
||||
len = value.toInt (&ok, 10);
|
||||
if (ok) {
|
||||
m_currentRequest->addHeader (QtHttpHeader::ContentLength, QByteArray::number (len));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
m_parsingStatus = ParsingError;
|
||||
qWarning () << "Error : incorrect HTTP headers line :" << line;
|
||||
}
|
||||
}
|
||||
else { // end of headers
|
||||
//qDebug () << "Debug : HTTP end of headers";
|
||||
if (m_currentRequest->getHeader (QtHttpHeader::ContentLength).toInt () > 0) {
|
||||
m_parsingStatus = AwaitingContent;
|
||||
}
|
||||
else {
|
||||
m_parsingStatus = RequestParsed;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AwaitingContent: { // raw data × N (until EOF ??)
|
||||
m_currentRequest->appendRawData (line);
|
||||
//qDebug () << "Debug : HTTP"
|
||||
// << "content :" << m_currentRequest->getRawData ().toHex ()
|
||||
// << "size :" << m_currentRequest->getRawData ().size ();
|
||||
if (m_currentRequest->getRawDataSize () == m_currentRequest->getHeader (QtHttpHeader::ContentLength).toInt ()) {
|
||||
//qDebug () << "Debug : HTTP end of content";
|
||||
m_parsingStatus = RequestParsed;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: { break; }
|
||||
}
|
||||
switch (m_parsingStatus) { // handle parsing status end/error
|
||||
case RequestParsed: { // a valid request has ben fully parsed
|
||||
QtHttpReply reply (m_serverHandle);
|
||||
connect (&reply, &QtHttpReply::requestSendHeaders,
|
||||
this, &QtHttpClientWrapper::onReplySendHeadersRequested);
|
||||
connect (&reply, &QtHttpReply::requestSendData,
|
||||
this, &QtHttpClientWrapper::onReplySendDataRequested);
|
||||
emit m_serverHandle->requestNeedsReply (m_currentRequest, &reply); // allow app to handle request
|
||||
m_parsingStatus = sendReplyToClient (&reply);
|
||||
break;
|
||||
}
|
||||
case ParsingError: { // there was an error durin one of parsing steps
|
||||
m_sockClient->readAll (); // clear remaining buffer to ignore content
|
||||
QtHttpReply reply (m_serverHandle);
|
||||
reply.setStatusCode (QtHttpReply::BadRequest);
|
||||
reply.appendRawData (QByteArrayLiteral ("<h1>Bad Request (HTTP parsing error) !</h1>"));
|
||||
reply.appendRawData (CRLF);
|
||||
m_parsingStatus = sendReplyToClient (&reply);
|
||||
break;
|
||||
}
|
||||
default: { break; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QtHttpClientWrapper::onReplySendHeadersRequested (void) {
|
||||
QtHttpReply * reply = qobject_cast<QtHttpReply *> (sender ());
|
||||
if (reply != Q_NULLPTR) {
|
||||
QByteArray data;
|
||||
// HTTP Version + Status Code + Status Msg
|
||||
data.append (QtHttpServer::HTTP_VERSION);
|
||||
data.append (SPACE);
|
||||
data.append (QByteArray::number (reply->getStatusCode ()));
|
||||
data.append (SPACE);
|
||||
data.append (QtHttpReply::getStatusTextForCode (reply->getStatusCode ()));
|
||||
data.append (CRLF);
|
||||
// Header name: header value
|
||||
if (reply->useChunked ()) {
|
||||
static const QByteArray & CHUNKED = QByteArrayLiteral ("chunked");
|
||||
reply->addHeader (QtHttpHeader::TransferEncoding, CHUNKED);
|
||||
}
|
||||
else {
|
||||
reply->addHeader (QtHttpHeader::ContentLength, QByteArray::number (reply->getRawDataSize ()));
|
||||
}
|
||||
const QList<QByteArray> & headersList = reply->getHeadersList ();
|
||||
foreach (const QByteArray & header, headersList) {
|
||||
data.append (header);
|
||||
data.append (COLON);
|
||||
data.append (SPACE);
|
||||
data.append (reply->getHeader (header));
|
||||
data.append (CRLF);
|
||||
}
|
||||
// empty line
|
||||
data.append (CRLF);
|
||||
m_sockClient->write (data);
|
||||
m_sockClient->flush ();
|
||||
}
|
||||
}
|
||||
|
||||
void QtHttpClientWrapper::onReplySendDataRequested (void) {
|
||||
QtHttpReply * reply = qobject_cast<QtHttpReply *> (sender ());
|
||||
if (reply != Q_NULLPTR) {
|
||||
// content raw data
|
||||
QByteArray data = reply->getRawData ();
|
||||
if (reply->useChunked ()) {
|
||||
data.prepend (QByteArray::number (data.size (), 16) % CRLF);
|
||||
data.append (CRLF);
|
||||
reply->resetRawData ();
|
||||
}
|
||||
// write to socket
|
||||
m_sockClient->write (data);
|
||||
m_sockClient->flush ();
|
||||
}
|
||||
}
|
||||
|
||||
QtHttpClientWrapper::ParsingStatus QtHttpClientWrapper::sendReplyToClient (QtHttpReply * reply) {
|
||||
if (reply != Q_NULLPTR) {
|
||||
if (!reply->useChunked ()) {
|
||||
reply->appendRawData (CRLF);
|
||||
// send all headers and all data in one shot
|
||||
reply->requestSendHeaders ();
|
||||
reply->requestSendData ();
|
||||
}
|
||||
else {
|
||||
// last chunk
|
||||
m_sockClient->write ("0" % CRLF % CRLF);
|
||||
m_sockClient->flush ();
|
||||
}
|
||||
if (m_currentRequest != Q_NULLPTR) {
|
||||
static const QByteArray & CLOSE = QByteArrayLiteral ("close");
|
||||
if (m_currentRequest->getHeader (QtHttpHeader::Connection).toLower () == CLOSE) {
|
||||
// must close connection after this request
|
||||
m_sockClient->close ();
|
||||
}
|
||||
m_currentRequest->deleteLater ();
|
||||
m_currentRequest = Q_NULLPTR;
|
||||
}
|
||||
}
|
||||
return AwaitingRequest;
|
||||
}
|
51
libsrc/webconfig/QtHttpClientWrapper.h
Normal file
51
libsrc/webconfig/QtHttpClientWrapper.h
Normal file
@ -0,0 +1,51 @@
|
||||
#ifndef QTHTTPCLIENTWRAPPER_H
|
||||
#define QTHTTPCLIENTWRAPPER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
class QTcpSocket;
|
||||
|
||||
class QtHttpRequest;
|
||||
class QtHttpReply;
|
||||
class QtHttpServer;
|
||||
|
||||
class QtHttpClientWrapper : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit QtHttpClientWrapper (QTcpSocket * sock, QtHttpServer * parent);
|
||||
|
||||
static const char SPACE = ' ';
|
||||
static const char COLON = ':';
|
||||
static const QByteArray & CRLF;
|
||||
|
||||
enum ParsingStatus {
|
||||
ParsingError = -1,
|
||||
AwaitingRequest = 0,
|
||||
AwaitingHeaders = 1,
|
||||
AwaitingContent = 2,
|
||||
RequestParsed = 3
|
||||
};
|
||||
|
||||
QString getGuid (void);
|
||||
|
||||
private slots:
|
||||
void onClientDataReceived (void);
|
||||
|
||||
protected:
|
||||
ParsingStatus sendReplyToClient (QtHttpReply * reply);
|
||||
|
||||
protected slots:
|
||||
void onReplySendHeadersRequested (void);
|
||||
void onReplySendDataRequested (void);
|
||||
|
||||
private:
|
||||
QString m_guid;
|
||||
ParsingStatus m_parsingStatus;
|
||||
QTcpSocket * m_sockClient;
|
||||
QtHttpRequest * m_currentRequest;
|
||||
QtHttpServer * m_serverHandle;
|
||||
};
|
||||
|
||||
#endif // QTHTTPCLIENTWRAPPER_H
|
32
libsrc/webconfig/QtHttpHeader.cpp
Normal file
32
libsrc/webconfig/QtHttpHeader.cpp
Normal file
@ -0,0 +1,32 @@
|
||||
|
||||
#include "QtHttpHeader.h"
|
||||
|
||||
#include <QByteArray>
|
||||
|
||||
const QByteArray & QtHttpHeader::Server = QByteArrayLiteral ("Server");
|
||||
const QByteArray & QtHttpHeader::Date = QByteArrayLiteral ("Date");
|
||||
const QByteArray & QtHttpHeader::Host = QByteArrayLiteral ("Host");
|
||||
const QByteArray & QtHttpHeader::Accept = QByteArrayLiteral ("Accept");
|
||||
const QByteArray & QtHttpHeader::Cookie = QByteArrayLiteral ("Cookie");
|
||||
const QByteArray & QtHttpHeader::ContentType = QByteArrayLiteral ("Content-Type");
|
||||
const QByteArray & QtHttpHeader::ContentLength = QByteArrayLiteral ("Content-Length");
|
||||
const QByteArray & QtHttpHeader::Connection = QByteArrayLiteral ("Connection");
|
||||
const QByteArray & QtHttpHeader::UserAgent = QByteArrayLiteral ("User-Agent");
|
||||
const QByteArray & QtHttpHeader::AcceptCharset = QByteArrayLiteral ("Accept-Charset");
|
||||
const QByteArray & QtHttpHeader::AcceptEncoding = QByteArrayLiteral ("Accept-Encoding");
|
||||
const QByteArray & QtHttpHeader::AcceptLanguage = QByteArrayLiteral ("Accept-Language");
|
||||
const QByteArray & QtHttpHeader::Authorization = QByteArrayLiteral ("Authorization");
|
||||
const QByteArray & QtHttpHeader::CacheControl = QByteArrayLiteral ("Cache-Control");
|
||||
const QByteArray & QtHttpHeader::ContentMD5 = QByteArrayLiteral ("Content-MD5");
|
||||
const QByteArray & QtHttpHeader::ProxyAuthorization = QByteArrayLiteral ("Proxy-Authorization");
|
||||
const QByteArray & QtHttpHeader::Range = QByteArrayLiteral ("Range");
|
||||
const QByteArray & QtHttpHeader::ContentEncoding = QByteArrayLiteral ("Content-Encoding");
|
||||
const QByteArray & QtHttpHeader::ContentLanguage = QByteArrayLiteral ("Content-Language");
|
||||
const QByteArray & QtHttpHeader::ContentLocation = QByteArrayLiteral ("Content-Location");
|
||||
const QByteArray & QtHttpHeader::ContentRange = QByteArrayLiteral ("Content-Range");
|
||||
const QByteArray & QtHttpHeader::Expires = QByteArrayLiteral ("Expires");
|
||||
const QByteArray & QtHttpHeader::LastModified = QByteArrayLiteral ("Last-Modified");
|
||||
const QByteArray & QtHttpHeader::Location = QByteArrayLiteral ("Location");
|
||||
const QByteArray & QtHttpHeader::SetCookie = QByteArrayLiteral ("Set-Cookie");
|
||||
const QByteArray & QtHttpHeader::TransferEncoding = QByteArrayLiteral ("Transfer-Encoding");
|
||||
const QByteArray & QtHttpHeader::ContentDisposition = QByteArrayLiteral ("Content-Disposition");
|
37
libsrc/webconfig/QtHttpHeader.h
Normal file
37
libsrc/webconfig/QtHttpHeader.h
Normal file
@ -0,0 +1,37 @@
|
||||
#ifndef QTHTTPHEADER_H
|
||||
#define QTHTTPHEADER_H
|
||||
|
||||
class QByteArray;
|
||||
|
||||
class QtHttpHeader {
|
||||
public:
|
||||
static const QByteArray & Server;
|
||||
static const QByteArray & Date;
|
||||
static const QByteArray & Host;
|
||||
static const QByteArray & Accept;
|
||||
static const QByteArray & ContentType;
|
||||
static const QByteArray & ContentLength;
|
||||
static const QByteArray & Connection;
|
||||
static const QByteArray & Cookie;
|
||||
static const QByteArray & UserAgent;
|
||||
static const QByteArray & AcceptCharset;
|
||||
static const QByteArray & AcceptEncoding;
|
||||
static const QByteArray & AcceptLanguage;
|
||||
static const QByteArray & Authorization;
|
||||
static const QByteArray & CacheControl;
|
||||
static const QByteArray & ContentMD5;
|
||||
static const QByteArray & ProxyAuthorization;
|
||||
static const QByteArray & Range;
|
||||
static const QByteArray & ContentEncoding;
|
||||
static const QByteArray & ContentLanguage;
|
||||
static const QByteArray & ContentLocation;
|
||||
static const QByteArray & ContentRange;
|
||||
static const QByteArray & Expires;
|
||||
static const QByteArray & LastModified;
|
||||
static const QByteArray & Location;
|
||||
static const QByteArray & SetCookie;
|
||||
static const QByteArray & TransferEncoding;
|
||||
static const QByteArray & ContentDisposition;
|
||||
};
|
||||
|
||||
#endif // QTHTTPHEADER_H
|
75
libsrc/webconfig/QtHttpReply.cpp
Normal file
75
libsrc/webconfig/QtHttpReply.cpp
Normal file
@ -0,0 +1,75 @@
|
||||
|
||||
#include "QtHttpReply.h"
|
||||
#include "QtHttpHeader.h"
|
||||
#include "QtHttpServer.h"
|
||||
|
||||
#include <QDateTime>
|
||||
|
||||
QtHttpReply::QtHttpReply (QtHttpServer * parent)
|
||||
: QObject (parent)
|
||||
, m_useChunked (false)
|
||||
, m_statusCode (Ok)
|
||||
, m_data (QByteArray ())
|
||||
, m_serverHandle (parent)
|
||||
{
|
||||
// set some additional headers
|
||||
addHeader (QtHttpHeader::Date, QDateTime::currentDateTimeUtc ().toString ("ddd, dd MMM yyyy hh:mm:ss t").toUtf8 ());
|
||||
addHeader (QtHttpHeader::Server, m_serverHandle->getServerName ().toUtf8 ());
|
||||
}
|
||||
|
||||
int QtHttpReply::getRawDataSize (void) const {
|
||||
return m_data.size ();
|
||||
}
|
||||
|
||||
bool QtHttpReply::useChunked (void) const {
|
||||
return m_useChunked;
|
||||
}
|
||||
|
||||
QtHttpReply::StatusCode QtHttpReply::getStatusCode (void) const {
|
||||
return m_statusCode;
|
||||
}
|
||||
|
||||
QByteArray QtHttpReply::getRawData (void) const {
|
||||
return m_data;
|
||||
}
|
||||
|
||||
QList<QByteArray> QtHttpReply::getHeadersList (void) const {
|
||||
return m_headersHash.keys ();
|
||||
}
|
||||
|
||||
QByteArray QtHttpReply::getHeader (const QByteArray & header) const {
|
||||
return m_headersHash.value (header, QByteArray ());
|
||||
}
|
||||
|
||||
const QByteArray QtHttpReply::getStatusTextForCode (QtHttpReply::StatusCode statusCode) {
|
||||
switch (statusCode) {
|
||||
case Ok: return QByteArrayLiteral ("OK.");
|
||||
case BadRequest: return QByteArrayLiteral ("Bad request !");
|
||||
case Forbidden: return QByteArrayLiteral ("Forbidden !");
|
||||
case NotFound: return QByteArrayLiteral ("Not found !");
|
||||
default: return QByteArrayLiteral ("");
|
||||
}
|
||||
}
|
||||
|
||||
void QtHttpReply::setUseChunked (bool chunked){
|
||||
m_useChunked = chunked;
|
||||
}
|
||||
|
||||
void QtHttpReply::setStatusCode (QtHttpReply::StatusCode statusCode) {
|
||||
m_statusCode = statusCode;
|
||||
}
|
||||
|
||||
void QtHttpReply::appendRawData (const QByteArray & data) {
|
||||
m_data.append (data);
|
||||
}
|
||||
|
||||
void QtHttpReply::addHeader (const QByteArray & header, const QByteArray & value) {
|
||||
QByteArray key = header.trimmed ();
|
||||
if (!key.isEmpty ()) {
|
||||
m_headersHash.insert (key, value);
|
||||
}
|
||||
}
|
||||
|
||||
void QtHttpReply::resetRawData (void) {
|
||||
m_data.clear ();
|
||||
}
|
55
libsrc/webconfig/QtHttpReply.h
Normal file
55
libsrc/webconfig/QtHttpReply.h
Normal file
@ -0,0 +1,55 @@
|
||||
#ifndef QTHTTPREPLY_H
|
||||
#define QTHTTPREPLY_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QByteArray>
|
||||
#include <QHash>
|
||||
#include <QList>
|
||||
|
||||
class QtHttpServer;
|
||||
|
||||
class QtHttpReply : public QObject {
|
||||
Q_OBJECT
|
||||
Q_ENUMS (StatusCode)
|
||||
|
||||
public:
|
||||
explicit QtHttpReply (QtHttpServer * parent);
|
||||
|
||||
enum StatusCode {
|
||||
Ok = 200,
|
||||
BadRequest = 400,
|
||||
Forbidden = 403,
|
||||
NotFound = 404,
|
||||
InternalError = 502,
|
||||
};
|
||||
|
||||
int getRawDataSize (void) const;
|
||||
bool useChunked (void) const;
|
||||
StatusCode getStatusCode (void) const;
|
||||
QByteArray getRawData (void) const;
|
||||
QList<QByteArray> getHeadersList (void) const;
|
||||
|
||||
QByteArray getHeader (const QByteArray & header) const;
|
||||
|
||||
static const QByteArray getStatusTextForCode (StatusCode statusCode);
|
||||
|
||||
public slots:
|
||||
void setUseChunked (bool chunked = false);
|
||||
void setStatusCode (StatusCode statusCode);
|
||||
void appendRawData (const QByteArray & data);
|
||||
void addHeader (const QByteArray & header, const QByteArray & value);
|
||||
void resetRawData (void);
|
||||
|
||||
signals:
|
||||
void requestSendHeaders (void);
|
||||
void requestSendData (void);
|
||||
|
||||
private:
|
||||
bool m_useChunked;
|
||||
StatusCode m_statusCode;
|
||||
QByteArray m_data;
|
||||
QtHttpServer * m_serverHandle;
|
||||
QHash<QByteArray, QByteArray> m_headersHash;
|
||||
};
|
||||
|
||||
#endif // QTHTTPREPLY_H
|
60
libsrc/webconfig/QtHttpRequest.cpp
Normal file
60
libsrc/webconfig/QtHttpRequest.cpp
Normal file
@ -0,0 +1,60 @@
|
||||
|
||||
#include "QtHttpRequest.h"
|
||||
#include "QtHttpHeader.h"
|
||||
#include "QtHttpServer.h"
|
||||
|
||||
QtHttpRequest::QtHttpRequest (QtHttpServer * parent)
|
||||
: QObject (parent)
|
||||
, m_url (QUrl ())
|
||||
, m_command (QString ())
|
||||
, m_data (QByteArray ())
|
||||
, m_serverHandle (parent)
|
||||
{
|
||||
// set some additional headers
|
||||
addHeader (QtHttpHeader::ContentLength, QByteArrayLiteral ("0"));
|
||||
addHeader (QtHttpHeader::Connection, QByteArrayLiteral ("Keep-Alive"));
|
||||
}
|
||||
|
||||
QUrl QtHttpRequest::getUrl (void) const {
|
||||
return m_url;
|
||||
}
|
||||
|
||||
QString QtHttpRequest::getCommand (void) const {
|
||||
return m_command;
|
||||
}
|
||||
|
||||
int QtHttpRequest::getRawDataSize (void) const {
|
||||
return m_data.size ();
|
||||
}
|
||||
|
||||
|
||||
QByteArray QtHttpRequest::getRawData (void) const {
|
||||
return m_data;
|
||||
}
|
||||
|
||||
QList<QByteArray> QtHttpRequest::getHeadersList (void) const {
|
||||
return m_headersHash.keys ();
|
||||
}
|
||||
|
||||
QByteArray QtHttpRequest::getHeader (const QByteArray & header) const {
|
||||
return m_headersHash.value (header, QByteArray ());
|
||||
}
|
||||
|
||||
void QtHttpRequest::setUrl (const QUrl & url) {
|
||||
m_url = url;
|
||||
}
|
||||
|
||||
void QtHttpRequest::setCommand (const QString & command) {
|
||||
m_command = command;
|
||||
}
|
||||
|
||||
void QtHttpRequest::addHeader (const QByteArray & header, const QByteArray & value) {
|
||||
QByteArray key = header.trimmed ();
|
||||
if (!key.isEmpty ()) {
|
||||
m_headersHash.insert (key, value);
|
||||
}
|
||||
}
|
||||
|
||||
void QtHttpRequest::appendRawData (const QByteArray & data) {
|
||||
m_data.append (data);
|
||||
}
|
40
libsrc/webconfig/QtHttpRequest.h
Normal file
40
libsrc/webconfig/QtHttpRequest.h
Normal file
@ -0,0 +1,40 @@
|
||||
#ifndef QTHTTPREQUEST_H
|
||||
#define QTHTTPREQUEST_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QByteArray>
|
||||
#include <QHash>
|
||||
#include <QUrl>
|
||||
|
||||
class QtHttpServer;
|
||||
|
||||
class QtHttpRequest : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit QtHttpRequest (QtHttpServer * parent);
|
||||
|
||||
int getRawDataSize (void) const;
|
||||
QUrl getUrl (void) const;
|
||||
QString getCommand (void) const;
|
||||
QByteArray getRawData (void) const;
|
||||
QList<QByteArray> getHeadersList (void) const;
|
||||
|
||||
QByteArray getHeader (const QByteArray & header) const;
|
||||
|
||||
public slots:
|
||||
void setUrl (const QUrl & url);
|
||||
void setCommand (const QString & command);
|
||||
void addHeader (const QByteArray & header, const QByteArray & value);
|
||||
void appendRawData (const QByteArray & data);
|
||||
|
||||
private:
|
||||
QUrl m_url;
|
||||
QString m_command;
|
||||
QByteArray m_data;
|
||||
QtHttpServer * m_serverHandle;
|
||||
QHash<QByteArray, QByteArray> m_headersHash;
|
||||
};
|
||||
|
||||
#endif // QTHTTPREQUEST_H
|
66
libsrc/webconfig/QtHttpServer.cpp
Normal file
66
libsrc/webconfig/QtHttpServer.cpp
Normal file
@ -0,0 +1,66 @@
|
||||
|
||||
#include "QtHttpServer.h"
|
||||
#include "QtHttpRequest.h"
|
||||
#include "QtHttpReply.h"
|
||||
#include "QtHttpClientWrapper.h"
|
||||
|
||||
#include <QTcpServer>
|
||||
#include <QTcpSocket>
|
||||
#include <QHostAddress>
|
||||
#include <QDebug>
|
||||
|
||||
const QString & QtHttpServer::HTTP_VERSION = QStringLiteral ("HTTP/1.1");
|
||||
|
||||
QtHttpServer::QtHttpServer (QObject * parent)
|
||||
: QObject (parent)
|
||||
, m_serverName (QStringLiteral ("The Qt5 HTTP Server"))
|
||||
{
|
||||
m_sockServer = new QTcpServer (this);
|
||||
connect (m_sockServer, &QTcpServer::newConnection, this, &QtHttpServer::onClientConnected);
|
||||
}
|
||||
|
||||
const QString QtHttpServer::getServerName (void) const {
|
||||
return m_serverName;
|
||||
}
|
||||
|
||||
void QtHttpServer::start (quint16 port) {
|
||||
if (m_sockServer->listen (QHostAddress::Any, port)) {
|
||||
emit started (m_sockServer->serverPort ());
|
||||
}
|
||||
else {
|
||||
emit error (m_sockServer->errorString ());
|
||||
}
|
||||
}
|
||||
|
||||
void QtHttpServer::stop (void) {
|
||||
if (m_sockServer->isListening ()) {
|
||||
m_sockServer->close ();
|
||||
emit stopped ();
|
||||
}
|
||||
}
|
||||
|
||||
void QtHttpServer::setServerName (const QString & serverName) {
|
||||
m_serverName = serverName;
|
||||
}
|
||||
|
||||
void QtHttpServer::onClientConnected (void) {
|
||||
while (m_sockServer->hasPendingConnections ()) {
|
||||
QTcpSocket * sockClient = m_sockServer->nextPendingConnection ();
|
||||
QtHttpClientWrapper * wrapper = new QtHttpClientWrapper (sockClient, this);
|
||||
connect (sockClient, &QTcpSocket::disconnected, this, &QtHttpServer::onClientDisconnected);
|
||||
m_socksClientsHash.insert (sockClient, wrapper);
|
||||
emit clientConnected (wrapper->getGuid ());
|
||||
}
|
||||
}
|
||||
|
||||
void QtHttpServer::onClientDisconnected (void) {
|
||||
QTcpSocket * sockClient = qobject_cast<QTcpSocket *> (sender ());
|
||||
if (sockClient) {
|
||||
QtHttpClientWrapper * wrapper = m_socksClientsHash.value (sockClient, Q_NULLPTR);
|
||||
if (wrapper) {
|
||||
emit clientDisconnected (wrapper->getGuid ());
|
||||
wrapper->deleteLater ();
|
||||
m_socksClientsHash.remove (sockClient);
|
||||
}
|
||||
}
|
||||
}
|
48
libsrc/webconfig/QtHttpServer.h
Normal file
48
libsrc/webconfig/QtHttpServer.h
Normal file
@ -0,0 +1,48 @@
|
||||
#ifndef QTHTTPSERVER_H
|
||||
#define QTHTTPSERVER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QHash>
|
||||
|
||||
class QTcpSocket;
|
||||
class QTcpServer;
|
||||
|
||||
class QtHttpRequest;
|
||||
class QtHttpReply;
|
||||
class QtHttpClientWrapper;
|
||||
|
||||
class QtHttpServer : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit QtHttpServer (QObject * parent = Q_NULLPTR);
|
||||
|
||||
static const QString & HTTP_VERSION;
|
||||
|
||||
const QString getServerName (void) const;
|
||||
|
||||
public slots:
|
||||
void start (quint16 port = 0);
|
||||
void stop (void);
|
||||
void setServerName (const QString & serverName);
|
||||
|
||||
signals:
|
||||
void started (quint16 port);
|
||||
void stopped (void);
|
||||
void error (const QString & msg);
|
||||
void clientConnected (const QString & guid);
|
||||
void clientDisconnected (const QString & guid);
|
||||
void requestNeedsReply (QtHttpRequest * request, QtHttpReply * reply);
|
||||
|
||||
private slots:
|
||||
void onClientConnected (void);
|
||||
void onClientDisconnected (void);
|
||||
|
||||
private:
|
||||
QString m_serverName;
|
||||
QTcpServer * m_sockServer;
|
||||
QHash<QTcpSocket *, QtHttpClientWrapper *> m_socksClientsHash;
|
||||
};
|
||||
|
||||
#endif // QTHTTPSERVER_H
|
87
libsrc/webconfig/StaticFileServing.cpp
Normal file
87
libsrc/webconfig/StaticFileServing.cpp
Normal file
@ -0,0 +1,87 @@
|
||||
|
||||
#include "StaticFileServing.h"
|
||||
|
||||
#include <QStringBuilder>
|
||||
#include <QUrlQuery>
|
||||
#include <QDebug>
|
||||
#include <QList>
|
||||
#include <QPair>
|
||||
#include <QFile>
|
||||
|
||||
StaticFileServing::StaticFileServing (QString baseUrl, quint16 port, QObject * parent)
|
||||
: QObject (parent)
|
||||
, m_baseUrl (baseUrl)
|
||||
{
|
||||
m_mimeDb = new QMimeDatabase;
|
||||
|
||||
m_server = new QtHttpServer (this);
|
||||
m_server->setServerName (QStringLiteral ("Qt Static HTTP File Server"));
|
||||
|
||||
connect (m_server, &QtHttpServer::started, this, &StaticFileServing::onServerStarted);
|
||||
connect (m_server, &QtHttpServer::stopped, this, &StaticFileServing::onServerStopped);
|
||||
connect (m_server, &QtHttpServer::error, this, &StaticFileServing::onServerError);
|
||||
connect (m_server, &QtHttpServer::requestNeedsReply, this, &StaticFileServing::onRequestNeedsReply);
|
||||
|
||||
m_server->start (port);
|
||||
}
|
||||
|
||||
StaticFileServing::~StaticFileServing ()
|
||||
{
|
||||
m_server->stop ();
|
||||
}
|
||||
|
||||
void StaticFileServing::onServerStarted (quint16 port)
|
||||
{
|
||||
qDebug () << "QtHttpServer started on port" << port << m_server->getServerName ();
|
||||
}
|
||||
|
||||
void StaticFileServing::onServerStopped () {
|
||||
qDebug () << "QtHttpServer stopped" << m_server->getServerName ();
|
||||
}
|
||||
|
||||
void StaticFileServing::onServerError (QString msg)
|
||||
{
|
||||
qDebug () << "QtHttpServer error :" << msg;
|
||||
}
|
||||
|
||||
static inline void printErrorToReply (QtHttpReply * reply, QString errorMessage)
|
||||
{
|
||||
reply->addHeader ("Content-Type", QByteArrayLiteral ("text/plain"));
|
||||
reply->appendRawData (errorMessage.toLocal8Bit ());
|
||||
}
|
||||
|
||||
void StaticFileServing::onRequestNeedsReply (QtHttpRequest * request, QtHttpReply * reply)
|
||||
{
|
||||
QString command = request->getCommand ();
|
||||
if (command == QStringLiteral ("GET"))
|
||||
{
|
||||
QString path = request->getUrl ().path ();
|
||||
if ( path == "/" || path.isEmpty() || ! QFile::exists(m_baseUrl % "/" % path) )
|
||||
path = "index.html";
|
||||
|
||||
QFile file (m_baseUrl % "/" % path);
|
||||
if (file.exists ())
|
||||
{
|
||||
QMimeType mime = m_mimeDb->mimeTypeForFile (file.fileName ());
|
||||
if (file.open (QFile::ReadOnly)) {
|
||||
QByteArray data = file.readAll ();
|
||||
reply->addHeader ("Content-Type", mime.name ().toLocal8Bit ());
|
||||
reply->appendRawData (data);
|
||||
file.close ();
|
||||
}
|
||||
else
|
||||
{
|
||||
printErrorToReply (reply, "Requested file " % m_baseUrl % "/" % path % " couldn't be open for reading !");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
printErrorToReply (reply, "Requested file " % path % " couldn't be found !");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
printErrorToReply (reply, "Unhandled HTTP/1.1 method " % command % " on static file server !");
|
||||
}
|
||||
}
|
||||
|
31
libsrc/webconfig/StaticFileServing.h
Normal file
31
libsrc/webconfig/StaticFileServing.h
Normal file
@ -0,0 +1,31 @@
|
||||
#ifndef STATICFILESERVING_H
|
||||
#define STATICFILESERVING_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QMimeDatabase>
|
||||
|
||||
#include "QtHttpServer.h"
|
||||
#include "QtHttpRequest.h"
|
||||
#include "QtHttpReply.h"
|
||||
#include "QtHttpHeader.h"
|
||||
|
||||
class StaticFileServing : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit StaticFileServing (QString baseUrl, quint16 port, QObject * parent = NULL);
|
||||
virtual ~StaticFileServing (void);
|
||||
|
||||
public slots:
|
||||
void onServerStopped (void);
|
||||
void onServerStarted (quint16 port);
|
||||
void onServerError (QString msg);
|
||||
void onRequestNeedsReply (QtHttpRequest * request, QtHttpReply * reply);
|
||||
|
||||
private:
|
||||
QString m_baseUrl;
|
||||
QtHttpServer * m_server;
|
||||
QMimeDatabase * m_mimeDb;
|
||||
};
|
||||
|
||||
#endif // STATICFILESERVING_H
|
33
libsrc/webconfig/WebConfig.cpp
Normal file
33
libsrc/webconfig/WebConfig.cpp
Normal file
@ -0,0 +1,33 @@
|
||||
#include "webconfig/webconfig.h"
|
||||
#include "StaticFileServing.h"
|
||||
|
||||
WebConfig::WebConfig(std::string baseUrl, quint16 port, QObject * parent) :
|
||||
_parent(parent),
|
||||
_baseUrl(QString::fromStdString(baseUrl)),
|
||||
_port(port),
|
||||
_server(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
WebConfig::~WebConfig()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
|
||||
void WebConfig::start()
|
||||
{
|
||||
if ( _server == nullptr )
|
||||
_server = new StaticFileServing (_baseUrl, _port, this);
|
||||
}
|
||||
|
||||
void WebConfig::stop()
|
||||
{
|
||||
if ( _server != nullptr )
|
||||
{
|
||||
delete _server;
|
||||
_server = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,8 +9,11 @@ target_link_libraries(hyperiond
|
||||
effectengine
|
||||
jsonserver
|
||||
boblightserver
|
||||
protoserver
|
||||
protoserver
|
||||
)
|
||||
if (ENABLE_QT5)
|
||||
target_link_libraries(hyperiond webconfig)
|
||||
endif ()
|
||||
|
||||
if (ENABLE_DISPMANX)
|
||||
target_link_libraries(hyperiond dispmanx-grabber)
|
||||
@ -40,4 +43,4 @@ install ( TARGETS hyperiond DESTINATION "bin" COMPONENT ambilight )
|
||||
install ( DIRECTORY ${CMAKE_SOURCE_DIR}/effects DESTINATION "share/hyperion/" COMPONENT ambilight )
|
||||
install ( DIRECTORY ${CMAKE_SOURCE_DIR}/bin/service DESTINATION "share/hyperion/" COMPONENT ambilight )
|
||||
install ( DIRECTORY ${CMAKE_SOURCE_DIR}/config DESTINATION "share/hyperion/" COMPONENT ambilight )
|
||||
|
||||
install ( DIRECTORY ${CMAKE_SOURCE_DIR}/assets/webconfig DESTINATION "share/hyperion/" COMPONENT ambilight )
|
||||
|
@ -58,8 +58,16 @@
|
||||
#include <QHostInfo>
|
||||
#endif
|
||||
|
||||
// JsonServer includes
|
||||
// network servers
|
||||
#include <jsonserver/JsonServer.h>
|
||||
#include <protoserver/ProtoServer.h>
|
||||
#include <boblightserver/BoblightServer.h>
|
||||
#include <webconfig/WebConfig.h>
|
||||
|
||||
#include <sys/prctl.h>
|
||||
#include <utils/logger.h>
|
||||
|
||||
using namespace vlofgren;
|
||||
|
||||
// ProtoServer includes
|
||||
#include <protoserver/ProtoServer.h>
|
||||
@ -187,7 +195,11 @@ void startXBMCVideoChecker(const Json::Value &config, XBMCVideoChecker* &xbmcVid
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_QT5
|
||||
void startNetworkServices(const Json::Value &config, Hyperion &hyperion, JsonServer* &jsonServer, ProtoServer* &protoServer, BoblightServer* &boblightServer, WebConfig* &webConfig, XBMCVideoChecker* &xbmcVideoChecker, QObject* parent)
|
||||
#else
|
||||
void startNetworkServices(const Json::Value &config, Hyperion &hyperion, JsonServer* &jsonServer, ProtoServer* &protoServer, BoblightServer* &boblightServer, XBMCVideoChecker* &xbmcVideoChecker)
|
||||
#endif
|
||||
{
|
||||
// Create Json server if configuration is present
|
||||
unsigned int jsonPort = 19444;
|
||||
@ -218,6 +230,24 @@ void startNetworkServices(const Json::Value &config, Hyperion &hyperion, JsonSer
|
||||
}
|
||||
std::cout << "INFO: Proto server created and started on port " << protoServer->getPort() << std::endl;
|
||||
|
||||
#ifdef ENABLE_QT5
|
||||
// webconfig server
|
||||
std::string webconfigPath = "/usr/share/hyperion/webconfig";
|
||||
quint16 webconfigPort = 80;
|
||||
bool webconfigEnable = true;
|
||||
if (config.isMember("webConfig"))
|
||||
{
|
||||
const Json::Value & webconfigConfig = config["webConfig"];
|
||||
webconfigEnable = webconfigConfig.get("enable", true).asBool();
|
||||
webconfigPort = webconfigConfig.get("port", 80).asUInt();
|
||||
webconfigPath = webconfigConfig.get("document_root", "/usr/share/hyperion/webconfig").asString();
|
||||
}
|
||||
|
||||
webConfig = new WebConfig(webconfigPath, webconfigPort, parent);
|
||||
if ( webconfigEnable )
|
||||
webConfig->start();
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_ZEROCONF
|
||||
const Json::Value & deviceConfig = config["device"];
|
||||
const std::string deviceName = deviceConfig.get("name", "").asString();
|
||||
@ -492,7 +522,12 @@ int main(int argc, char** argv)
|
||||
JsonServer * jsonServer = nullptr;
|
||||
ProtoServer * protoServer = nullptr;
|
||||
BoblightServer * boblightServer = nullptr;
|
||||
#ifdef ENABLE_QT5
|
||||
WebConfig * webConfig = nullptr;
|
||||
startNetworkServices(config, hyperion, jsonServer, protoServer, boblightServer, webConfig, xbmcVideoChecker, &app);
|
||||
#else
|
||||
startNetworkServices(config, hyperion, jsonServer, protoServer, boblightServer, xbmcVideoChecker);
|
||||
#endif
|
||||
|
||||
// ---- grabber -----
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user