Initial WebUI design and structure with JSON sample function (#170)

* Initial WebUI with sample functions

* Changed folder structure

* Light Reset Button and Translation fixing in Links

* Indentation fixed

* Reorganized menu and new function for setting effects

* Styling fix
This commit is contained in:
b1rdhous3
2016-08-13 20:05:01 +02:00
committed by redPanther
parent 5a902add81
commit 1ff8528597
570 changed files with 41576 additions and 995 deletions

View File

@@ -1,37 +0,0 @@
/*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));
}
});
});

View File

@@ -1,57 +0,0 @@
/*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);
});

View File

@@ -1,307 +0,0 @@
/*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);
});

View File

@@ -1,49 +0,0 @@
/*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');
}
});
});

View File

@@ -1,57 +0,0 @@
/*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);
});

View File

@@ -1,68 +0,0 @@
/*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);
});

View File

@@ -1,229 +0,0 @@
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);
});

View File

@@ -1,534 +0,0 @@
/*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));
}
});
});

View File

@@ -1,201 +0,0 @@
/*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;
}
});
});

View File

@@ -1,150 +0,0 @@
/*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();
}
});

View File

@@ -1,108 +0,0 @@
/*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();
});

View File

@@ -1,124 +0,0 @@
/*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;
}
});
});

View File

@@ -1,56 +0,0 @@
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));
}
};
});

View File

@@ -1,64 +0,0 @@
/**
* 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');
}
}
});
});

View File

@@ -1,402 +0,0 @@
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 = this.inputbox.style.backgroundColor = (
'#'
+ tools.b2hexstr(rgb.r)
+ tools.b2hexstr(rgb.g)
+ tools.b2hexstr(rgb.b)
);
this.inputbox.style.color = (rgb.r + rgb.g + rgb.b) < 384 ? '#fff' : '#000';
},
/**
* 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();
}
});
});

View File

@@ -1,199 +0,0 @@
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 = '&#xe804;';
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 = '&#xe801;';
horiz.appendChild(el);
el = document.createElement('div');
el.classList.add('delete_icon');
el.innerHTML = '&#xe612;';
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');
}
}
});
});

View File

@@ -1,259 +0,0 @@
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);
}
});
});

View File

@@ -1,134 +0,0 @@
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);
}
});
});

View File

@@ -1,375 +0,0 @@
/**
* 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: '&#xe806;',
label: 'Saturation gain',
value: transform.saturationGain,
min: 0,
max: 5
},
{
id: 'hsv_valueGain',
type: 'value',
icon: '&#xe805;',
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: '&#xe800;',
label: 'Red',
value: transform.gamma[0],
min: 0,
max: 5
},
{
id: 'gamma_g',
type: 'green',
icon: '&#xe800;',
label: 'Green',
value: transform.gamma[1],
min: 0,
max: 5
},
{
id: 'gamma_b',
type: 'blue',
icon: '&#xe800;',
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: '&#xe800;',
label: 'Red',
value: transform.whitelevel[0],
min: 0,
max: 1
},
{
id: 'whitelevel_g',
type: 'green',
icon: '&#xe800;',
label: 'Green',
value: transform.whitelevel[1],
min: 0,
max: 1
},
{
id: 'whitelevel_b',
type: 'blue',
icon: '&#xe800;',
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: '&#xe800;',
label: 'Red',
value: transform.blacklevel[0],
min: 0,
max: 1
},
{
id: 'blacklevel_g',
type: 'green',
icon: '&#xe800;',
label: 'Green',
value: transform.blacklevel[1],
min: 0,
max: 1
},
{
id: 'blacklevel_b',
type: 'blue',
icon: '&#xe800;',
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: '&#xe800;',
label: 'Red',
value: transform.threshold[0],
min: 0,
max: 1
},
{
id: 'threshold_g',
type: 'green',
icon: '&#xe800;',
label: 'Green',
value: transform.threshold[1],
min: 0,
max: 1
},
{
id: 'threshold_b',
type: 'blue',
icon: '&#xe800;',
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);
}
});
});

View File

@@ -1,17 +0,0 @@
/*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
});
});

File diff suppressed because it is too large Load Diff

7
assets/webconfig/js/bootstrap.min.js vendored Normal file

File diff suppressed because one or more lines are too long

675
assets/webconfig/js/jquery-lang.js vendored Normal file
View File

@@ -0,0 +1,675 @@
/*
The MIT License (MIT)
Copyright (c) 2014 Irrelon Software Limited
http://www.irrelon.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice, url and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
Source: https://github.com/irrelon/jquery-lang-js
Changelog: See readme.md
*/
var Lang = (function () {
"use strict";
var Lang = function () {
// Enable firing events
this._fireEvents = true;
// Allow storage of dynamic language pack data
this._dynamic = {};
};
/**
* Initialise the library with the library options.
* @param {Object} options The options to init the library with.
* See the readme.md for the details of the options available.
*/
Lang.prototype.init = function (options) {
var self = this,
cookieLang,
defaultLang,
currentLang,
allowCookieOverride;
options = options || {};
options.cookie = options.cookie || {};
defaultLang = options.defaultLang;
currentLang = options.currentLang;
allowCookieOverride = options.allowCookieOverride;
// Set cookie settings
this.cookieName = options.cookie.name || 'langCookie';
this.cookieExpiry = options.cookie.expiry || 365;
this.cookiePath = options.cookie.path || '/';
// Store existing mutation methods so we can auto-run
// translations when new data is added to the page
this._mutationCopies = {
append: $.fn.append,
appendTo: $.fn.appendTo,
prepend: $.fn.prepend,
before: $.fn.before,
after: $.fn.after,
html: $.fn.html
};
// Now override the existing mutation methods with our own
$.fn.append = function () { return self._mutation(this, 'append', arguments) };
$.fn.appendTo = function () { return self._mutation(this, 'appendTo', arguments) };
$.fn.prepend = function () { return self._mutation(this, 'prepend', arguments) };
$.fn.before = function () { return self._mutation(this, 'before', arguments) };
$.fn.after = function () { return self._mutation(this, 'after', arguments) };
$.fn.html = function () { return self._mutation(this, 'html', arguments) };
// Set default and current language to the default one
// to start with
this.defaultLang = defaultLang || 'en';
this.currentLang = defaultLang || 'en';
// Check for cookie support when no current language is specified
if ((allowCookieOverride || !currentLang) && typeof Cookies !== 'undefined') {
// Check for an existing language cookie
cookieLang = Cookies.get(this.cookieName);
if (cookieLang) {
// We have a cookie language, set the current language
currentLang = cookieLang;
}
}
$(function () {
// Setup data on the language items
self._start();
// Check if the current language is not the same as our default
if (currentLang && currentLang !== self.defaultLang) {
// Switch to the current language
self.change(currentLang);
}
});
};
/**
* Object that holds the language packs.
* @type {{}}
*/
Lang.prototype.pack = {};
/**
* Array of translatable attributes to check for on elements.
* @type {string[]}
*/
Lang.prototype.attrList = [
'title',
'alt',
'placeholder',
'href'
];
/**
* Defines a language pack that can be dynamically loaded and the
* path to use when doing so.
* @param {String} lang The language two-letter iso-code.
* @param {String} path The path to the language pack js file.
*/
Lang.prototype.dynamic = function (lang, path) {
if (lang !== undefined && path !== undefined) {
this._dynamic[lang] = path;
}
};
/**
* Loads a new language pack for the given language.
* @param {string} lang The language to load the pack for.
* @param {Function=} callback Optional callback when the file has loaded.
*/
Lang.prototype.loadPack = function (lang, callback) {
var self = this;
if (lang && self._dynamic[lang]) {
$.ajax({
dataType: "json",
url: self._dynamic[lang],
success: function (data) {
self.pack[lang] = data;
// Process the regex list
if (self.pack[lang].regex) {
var packRegex = self.pack[lang].regex,
regex,
i;
for (i = 0; i < packRegex.length; i++) {
regex = packRegex[i];
if (regex.length === 2) {
// String, value
regex[0] = new RegExp(regex[0]);
} else if (regex.length === 3) {
// String, modifiers, value
regex[0] = new RegExp(regex[0], regex[1]);
// Remove modifier
regex.splice(1, 1);
}
}
}
//console.log('Loaded language pack: ' + self._dynamic[lang]);
if (callback) { callback(false, lang, self._dynamic[lang]); }
},
error: function () {
if (callback) { callback(true, lang, self._dynamic[lang]); }
throw('Error loading language pack' + self._dynamic[lang]);
}
});
} else {
throw('Cannot load language pack, no file path specified!');
}
};
/**
* Scans the DOM for elements with [lang] selector and saves translate data
* for them for later use.
* @private
*/
Lang.prototype._start = function (selector) {
// Get the page HTML
var arr = selector !== undefined ? $(selector).find('[lang]') : $(':not(html)[lang]'),
arrCount = arr.length,
elem;
while (arrCount--) {
elem = $(arr[arrCount]);
this._processElement(elem);
}
};
Lang.prototype._processElement = function (elem) {
// Only store data if the element is set to our default language
if (elem.attr('lang') === this.defaultLang) {
// Store translatable attributes
this._storeAttribs(elem);
// Store translatable content
this._storeContent(elem);
}
};
/**
* Stores the translatable attribute values in their default language.
* @param {object} elem The jQuery selected element.
* @private
*/
Lang.prototype._storeAttribs = function (elem) {
var attrIndex,
attr,
attrObj;
for (attrIndex = 0; attrIndex < this.attrList.length; attrIndex++) {
attr = this.attrList[attrIndex];
if (elem.attr(attr)) {
// Grab the existing attribute store or create a new object
attrObj = elem.data('lang-attr') || {};
// Add the attribute and value to the store
attrObj[attr] = elem.attr(attr);
// Save the attribute data to the store
elem.data('lang-attr', attrObj);
}
}
};
/**
* Reads the existing content from the element and stores it for
* later use in translation.
* @param elem
* @private
*/
Lang.prototype._storeContent = function (elem) {
// Check if the element is an input element
if (elem.is('input')) {
switch (elem.attr('type')) {
case 'button':
case 'submit':
case 'hidden':
case 'reset':
elem.data('lang-val', elem.val());
break;
}
} else if (elem.is('img')) {
elem.data('lang-src', elem.attr('src'));
} else {
// Get the text nodes immediately inside this element
var nodes = this._getTextNodes(elem);
if (nodes) {
elem.data('lang-text', nodes);
}
}
};
/**
* Retrieves the text nodes from an element and returns them in array wrap into
* object with two properties:
* - node - which corresponds to text node,
* - langDefaultText - which remember current data of text node
* @param elem
* @returns {Array|*}
* @private
*/
Lang.prototype._getTextNodes = function (elem) {
var nodes = elem.contents(), nodeObjArray = [], nodeObj = {},
nodeArr, that = this, map = Array.prototype.map;
$.each(nodes, function (index, node) {
if ( node.nodeType !== 3 ) {
return;
}
nodeObj = {
node : node,
langDefaultText : node.data
};
nodeObjArray.push(nodeObj);
});
// If element has only one text node and data-lang-token is defined
// set langContentKey property to use as a token
if(nodes.length == 1){
nodeObjArray[0].langToken = elem.data('langToken');
}
return nodeObjArray;
};
/**
* Sets text nodes of an element translated based on the passed language.
* @param elem
* @param {Array|*} nodes array of objecs with text node and defaultText returned from _getTextNodes
* @param lang
* @private
*/
Lang.prototype._setTextNodes = function (elem, nodes, lang) {
var index,
textNode,
defaultText,
translation,
langNotDefault = lang !== this.defaultLang;
for (index = 0; index < nodes.length; index++) {
textNode = nodes[index];
if (langNotDefault) {
// If langToken is set, use it as a token
defaultText = textNode.langToken || $.trim(textNode.langDefaultText);
if (defaultText) {
// Translate the langDefaultText
translation = this.translate(defaultText, lang);
if (translation) {
try {
// Replace the text with the translated version
textNode.node.data = textNode.node.data.split($.trim(textNode.node.data)).join(translation);
} catch (e) {
}
} else {
if (console && console.log) {
console.log('Translation for "' + defaultText + '" not found!');
}
}
}
} else {
// Replace with original text
try {
textNode.node.data = textNode.langDefaultText;
} catch (e) {
}
}
}
};
/**
* Translates and sets the attributes of an element to the passed language.
* @param elem
* @param lang
* @private
*/
Lang.prototype._translateAttribs = function (elem, lang) {
var attr,
attrObj = elem.data('lang-attr') || {},
translation;
for (attr in attrObj) {
if (attrObj.hasOwnProperty(attr)) {
// Check the element still has the attribute
if (elem.attr(attr)) {
if (lang !== this.defaultLang) {
// Get the translated value
translation = this.translate(attrObj[attr], lang);
// Check we actually HAVE a translation
if (translation) {
// Change the attribute to the translated value
elem.attr(attr, translation);
}
} else {
// Set default language value
elem.attr(attr, attrObj[attr]);
}
}
}
}
};
/**
* Translates and sets the contents of an element to the passed language.
* @param elem
* @param lang
* @private
*/
Lang.prototype._translateContent = function (elem, lang) {
var langNotDefault = lang !== this.defaultLang,
translation,
nodes;
// Check if the element is an input element
if (elem.is('input')) {
switch (elem.attr('type')) {
case 'button':
case 'submit':
case 'hidden':
case 'reset':
if (langNotDefault) {
// Get the translated value
translation = this.translate(elem.data('lang-val'), lang);
// Check we actually HAVE a translation
if (translation) {
// Set translated value
elem.val(translation);
}
} else {
// Set default language value
elem.val(elem.data('lang-val'));
}
break;
}
} else if (elem.is('img')) {
if (langNotDefault) {
// Get the translated value
translation = this.translate(elem.data('lang-src'), lang);
// Check we actually HAVE a translation
if (translation) {
// Set translated value
elem.attr('src', translation);
}
} else {
// Set default language value
elem.attr('src', elem.data('lang-src'));
}
} else {
// Set text node translated text
nodes = elem.data('lang-text');
if (nodes) {
this._setTextNodes(elem, nodes, lang);
}
}
};
/**
* Call this to change the current language on the page.
* @param {String} lang The new two-letter language code to change to.
* @param {String=} selector Optional selector to find language-based
* elements for updating.
* @param {Function=} callback Optional callback function that will be
* called once the language change has been successfully processed. This
* is especially useful if you are using dynamic language pack loading
* since you will get a callback once it has been loaded and changed.
* Your callback will be passed three arguments, a boolean to denote if
* there was an error (true if error), the second will be the language
* you passed in the change call (the lang argument) and the third will
* be the selector used in the change update.
*/
Lang.prototype.change = function (lang, selector, callback) {
var self = this;
if (lang === this.defaultLang || this.pack[lang] || this._dynamic[lang]) {
// Check if the language pack is currently loaded
if (lang !== this.defaultLang) {
if (!this.pack[lang] && this._dynamic[lang]) {
// The language pack needs loading first
//console.log('Loading dynamic language pack: ' + this._dynamic[lang] + '...');
this.loadPack(lang, function (err, loadingLang, fromUrl) {
if (!err) {
// Process the change language request
self.change.call(self, lang, selector, callback);
} else {
// Call the callback with the error
if (callback) { callback('Language pack could not load from: ' + fromUrl, lang, selector); }
}
});
return;
} else if (!this.pack[lang] && !this._dynamic[lang]) {
// Pack not loaded and no dynamic entry
if (callback) { callback('Language pack not defined for: ' + lang, lang, selector); }
throw('Could not change language to ' + lang + ' because no language pack for this language exists!');
}
}
var fireAfterUpdate = false,
currLang = this.currentLang;
if (this.currentLang != lang) {
this.beforeUpdate(currLang, lang);
fireAfterUpdate = true;
}
this.currentLang = lang;
// Get the page HTML
var arr = selector !== undefined ? $(selector).find('[lang]') : $(':not(html)[lang]'),
arrCount = arr.length,
elem;
while (arrCount--) {
elem = $(arr[arrCount]);
if (elem.attr('lang') !== lang) {
this._translateElement(elem, lang);
}
}
if (fireAfterUpdate) {
this.afterUpdate(currLang, lang);
}
// Check for cookie support
if (typeof Cookies !== "undefined") {
// Set a cookie to remember this language setting with 1 year expiry
Cookies.set(self.cookieName, lang, {
expires: self.cookieExpiry,
path: self.cookiePath
});
}
if (callback) { callback(false, lang, selector); }
} else {
if (callback) { callback('No language pack defined for: ' + lang, lang, selector); }
throw('Attempt to change language to "' + lang + '" but no language pack for that language is loaded!');
}
};
Lang.prototype._translateElement = function (elem, lang) {
// Translate attributes
this._translateAttribs(elem, lang);
// Translate content
if (elem.attr('data-lang-content') != 'false') {
this._translateContent(elem, lang);
}
// Update the element's current language
elem.attr('lang', lang);
};
/**
* Translates text from the default language into the passed language.
* @param {String} text The text to translate.
* @param {String} lang The two-letter language code to translate to.
* @returns {*}
*/
Lang.prototype.translate = function (text, lang) {
lang = lang || this.currentLang;
if (this.pack[lang]) {
var translation = '';
if (lang != this.defaultLang) {
// Check for a direct token translation
translation = this.pack[lang].token[text];
if (!translation) {
// No token translation was found, test for regex match
translation = this._regexMatch(text, lang);
}
if (!translation) {
if (console && console.log) {
console.log('Translation for "' + text + '" not found in language pack: ' + lang);
}
}
return translation || text;
} else {
return text;
}
} else {
return text;
}
};
/**
* Checks the regex items for a match against the passed text and
* if a match is made, translates to the given replacement.
* @param {String} text The text to test regex matches against.
* @param {String} lang The two-letter language code to translate to.
* @returns {string}
* @private
*/
Lang.prototype._regexMatch = function (text, lang) {
// Loop the regex array and test them against the text
var arr,
arrCount,
arrIndex,
item,
regex,
expressionResult;
arr = this.pack[lang].regex;
if (arr) {
arrCount = arr.length;
for (arrIndex = 0; arrIndex < arrCount; arrIndex++) {
item = arr[arrIndex];
regex = item[0];
// Test regex
expressionResult = regex.exec(text);
if (expressionResult && expressionResult[0]) {
return text.split(expressionResult[0]).join(item[1]);
}
}
}
return '';
};
Lang.prototype.beforeUpdate = function (currentLang, newLang) {
if (this._fireEvents) {
$(this).triggerHandler('beforeUpdate', [currentLang, newLang, this.pack[currentLang], this.pack[newLang]]);
}
};
Lang.prototype.afterUpdate = function (currentLang, newLang) {
if (this._fireEvents) {
$(this).triggerHandler('afterUpdate', [currentLang, newLang, this.pack[currentLang], this.pack[newLang]]);
}
};
Lang.prototype.refresh = function () {
// Process refresh on the page
this._fireEvents = false;
this.change(this.currentLang);
this._fireEvents = true;
};
////////////////////////////////////////////////////
// Mutation overrides
////////////////////////////////////////////////////
Lang.prototype._mutation = function (context, method, args) {
var result = this._mutationCopies[method].apply(context, args),
currLang = this.currentLang,
rootElem = $(context);
if (rootElem.attr('lang')) {
// Switch off events for the moment
this._fireEvents = false;
// Check if the root element is currently set to another language from current
//if (rootElem.attr('lang') !== this.currentLang) {
this._translateElement(rootElem, this.defaultLang);
this.change(this.defaultLang, rootElem);
// Calling change above sets the global currentLang but this is supposed to be
// an isolated change so reset the global value back to what it was before
this.currentLang = currLang;
// Record data on the default language from the root element
this._processElement(rootElem);
// Translate the root element
this._translateElement(rootElem, this.currentLang);
//}
}
// Record data on the default language from the root's children
this._start(rootElem);
// Process translation on any child elements of this element
this.change(this.currentLang, rootElem);
// Switch events back on
this._fireEvents = true;
return result;
};
return Lang;
})();

5
assets/webconfig/js/jquery.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,145 @@
/*!
* JavaScript Cookie v2.0.4
* https://github.com/js-cookie/js-cookie
*
* Copyright 2006, 2015 Klaus Hartl & Fagner Brack
* Released under the MIT license
*/
(function (factory) {
if (typeof define === 'function' && define.amd) {
define(factory);
} else if (typeof exports === 'object') {
module.exports = factory();
} else {
var _OldCookies = window.Cookies;
var api = window.Cookies = factory();
api.noConflict = function () {
window.Cookies = _OldCookies;
return api;
};
}
}(function () {
function extend () {
var i = 0;
var result = {};
for (; i < arguments.length; i++) {
var attributes = arguments[ i ];
for (var key in attributes) {
result[key] = attributes[key];
}
}
return result;
}
function init (converter) {
function api (key, value, attributes) {
var result;
// Write
if (arguments.length > 1) {
attributes = extend({
path: '/'
}, api.defaults, attributes);
if (typeof attributes.expires === 'number') {
var expires = new Date();
expires.setMilliseconds(expires.getMilliseconds() + attributes.expires * 864e+5);
attributes.expires = expires;
}
try {
result = JSON.stringify(value);
if (/^[\{\[]/.test(result)) {
value = result;
}
} catch (e) {}
if (!converter.write) {
value = encodeURIComponent(String(value))
.replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent);
} else {
value = converter.write(value, key);
}
key = encodeURIComponent(String(key));
key = key.replace(/%(23|24|26|2B|5E|60|7C)/g, decodeURIComponent);
key = key.replace(/[\(\)]/g, escape);
return (document.cookie = [
key, '=', value,
attributes.expires && '; expires=' + attributes.expires.toUTCString(), // use expires attribute, max-age is not supported by IE
attributes.path && '; path=' + attributes.path,
attributes.domain && '; domain=' + attributes.domain,
attributes.secure ? '; secure' : ''
].join(''));
}
// Read
if (!key) {
result = {};
}
// To prevent the for loop in the first place assign an empty array
// in case there are no cookies at all. Also prevents odd result when
// calling "get()"
var cookies = document.cookie ? document.cookie.split('; ') : [];
var rdecode = /(%[0-9A-Z]{2})+/g;
var i = 0;
for (; i < cookies.length; i++) {
var parts = cookies[i].split('=');
var name = parts[0].replace(rdecode, decodeURIComponent);
var cookie = parts.slice(1).join('=');
if (cookie.charAt(0) === '"') {
cookie = cookie.slice(1, -1);
}
try {
cookie = converter.read ?
converter.read(cookie, name) : converter(cookie, name) ||
cookie.replace(rdecode, decodeURIComponent);
if (this.json) {
try {
cookie = JSON.parse(cookie);
} catch (e) {}
}
if (key === name) {
result = cookie;
break;
}
if (!key) {
result[name] = cookie;
}
} catch (e) {}
}
return result;
}
api.get = api.set = api;
api.getJSON = function () {
return api.apply({
json: true
}, [].slice.call(arguments));
};
api.defaults = {};
api.remove = function (key, attributes) {
api(key, '', extend(attributes, {
expires: -1
}));
};
api.withConverter = init;
return api;
}
return init(function () {});
}));

9
assets/webconfig/js/metisMenu.min.js vendored Normal file
View File

@@ -0,0 +1,9 @@
/*
* metismenu - v1.1.3
* Easy menu jQuery plugin for Twitter Bootstrap 3
* https://github.com/onokumus/metisMenu
*
* Made by Osman Nuri Okumus
* Under MIT License
*/
!function(a,b,c){function d(b,c){this.element=a(b),this.settings=a.extend({},f,c),this._defaults=f,this._name=e,this.init()}var e="metisMenu",f={toggle:!0,doubleTapToGo:!1};d.prototype={init:function(){var b=this.element,d=this.settings.toggle,f=this;this.isIE()<=9?(b.find("li.active").has("ul").children("ul").collapse("show"),b.find("li").not(".active").has("ul").children("ul").collapse("hide")):(b.find("li.active").has("ul").children("ul").addClass("collapse in"),b.find("li").not(".active").has("ul").children("ul").addClass("collapse")),f.settings.doubleTapToGo&&b.find("li.active").has("ul").children("a").addClass("doubleTapToGo"),b.find("li").has("ul").children("a").on("click."+e,function(b){return b.preventDefault(),f.settings.doubleTapToGo&&f.doubleTapToGo(a(this))&&"#"!==a(this).attr("href")&&""!==a(this).attr("href")?(b.stopPropagation(),void(c.location=a(this).attr("href"))):(a(this).parent("li").toggleClass("active").children("ul").collapse("toggle"),void(d&&a(this).parent("li").siblings().removeClass("active").children("ul.in").collapse("hide")))})},isIE:function(){for(var a,b=3,d=c.createElement("div"),e=d.getElementsByTagName("i");d.innerHTML="<!--[if gt IE "+ ++b+"]><i></i><![endif]-->",e[0];)return b>4?b:a},doubleTapToGo:function(a){var b=this.element;return a.hasClass("doubleTapToGo")?(a.removeClass("doubleTapToGo"),!0):a.parent().children("ul").length?(b.find(".doubleTapToGo").removeClass("doubleTapToGo"),a.addClass("doubleTapToGo"),!1):void 0},remove:function(){this.element.off("."+e),this.element.removeData(e)}},a.fn[e]=function(b){return this.each(function(){var c=a(this);c.data(e)&&c.data(e).remove(),c.data(e,new d(this,b))}),this}}(jQuery,window,document);

View File

@@ -0,0 +1,44 @@
$(function() {
$('#side-menu').metisMenu();
});
//Loads the correct sidebar on window load,
//collapses the sidebar on window resize.
// Sets the min-height of #page-wrapper to window size
$(function() {
$(window).bind("load resize", function() {
var topOffset = 50;
var width = (this.window.innerWidth > 0) ? this.window.innerWidth : this.screen.width;
if (width < 768) {
$('div.navbar-collapse').addClass('collapse');
topOffset = 100; // 2-row-menu
} else {
$('div.navbar-collapse').removeClass('collapse');
}
var height = ((this.window.innerHeight > 0) ? this.window.innerHeight : this.screen.height) - 1;
height = height - topOffset;
if (height < 1) height = 1;
if (height > topOffset) {
$("#page-wrapper").css("min-height", (height) + "px");
}
});
var url = window.location;
// var element = $('ul.nav a').filter(function() {
// return this.href == url;
// }).addClass('active').parent().parent().addClass('in').parent();
var element = $('ul.nav a').filter(function() {
return this.href == url;
}).addClass('active').parent();
while(true){
if (element.is('li')){
element = element.parent().addClass('in').parent();
} else {
break;
}
}
});

File diff suppressed because it is too large Load Diff

View File

@@ -1,594 +0,0 @@
//
// ____ _ _
// / ___|| |_ __ _ _ __ ___ ___ (_)___ (*)
// \___ \| __/ _` | '_ \ / _ \/ __| | / __|
// ___) | || (_| | |_) | __/\__ \_ | \__ \
// |____/ \__\__,_| .__/ \___||___(_)/ |___/
// |_| |__/
//
// (*) 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;
}
})();

File diff suppressed because it is too large Load Diff