mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2025-03-01 10:33:28 +00: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:
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
Reference in New Issue
Block a user