Files
Zos/Skills/@be/be/node_modules/jibo-tunable/lib/jibo-tunable.js

738 lines
26 KiB
JavaScript

(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.jiboTunable = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
/**
* @fileOverview
*
* Created on 5/21/16.
* @author Siggi Orn <siggi@jibo.com>
*/
"use strict";
exports.ReqType = {
INIT: 'INIT',
NEW_FIELD: 'NEW_FIELD',
PLAY: 'PLAY',
PLAYING: 'PLAYING',
DONE_PLAYING: 'DONE_PLAYING',
VALUE_UPDATE: 'VALUE_UPDATE',
};
var gui;
var Client = (function () {
function Client(port) {
var _this = this;
this.wsHandlers = new Map();
this.folders = new Map();
// Setup gui for first time
if (!gui) {
gui = new dat.GUI({ autoPlace: false });
var container = document.getElementById('gui-container');
container.appendChild(gui.domElement);
gui.width = 400;
}
this.ws = new WebSocket('ws://' + window.location.hostname + ':' + port);
// When the connection is open, send some data to the server
this.ws.onopen = function () {
var msg = {
type: exports.ReqType.INIT,
data: ''
};
_this.sendWS(msg);
};
this.ws.onclose = function (ev) {
console.log('WebSocket closed: ', ev);
_this.cleanup();
};
// Log errors
this.ws.onerror = function (error) {
console.error('WebSocket Error: ', error);
_this.cleanup();
};
// Log messages from the server
this.ws.onmessage = function (incoming) {
console.log('Got WS message: ' + incoming.data);
var msg = JSON.parse(incoming.data);
var handler = _this.wsHandlers.get(msg.type);
if (handler) {
var resp = handler(msg.data);
if (resp) {
_this.sendWS(resp);
}
}
};
this.addWsHandler(exports.ReqType.VALUE_UPDATE, function (data) {
var folder = _this.folders.get(data.folderName);
if (!folder) {
throw new Error("No folder of name '" + data.folderName + "'");
}
folder.obj[data.fieldName] = data.value;
// Update display value of all children of folder
folder.gui.__controllers.forEach(function (c) {
c.updateDisplay();
});
});
/**
* Install handler for new fields
*/
this.addWsHandler(exports.ReqType.NEW_FIELD, function (data) {
var folderName = data.folderName;
var fieldName = data.fieldName;
var folder = _this.folders.get(folderName);
if (!folder) {
folder = {
obj: {},
gui: gui.addFolder(folderName)
};
_this.folders.set(folderName, folder);
}
folder.gui.open();
folder.obj[fieldName] = data.field._current;
var el;
var type = data.field.type;
if (type === 'string') {
el = folder.gui.add(folder.obj, fieldName, data.field.options);
}
else if (type === 'boolean') {
el = folder.gui.add(folder.obj, fieldName);
}
else if (type === 'number') {
el = folder.gui.add(folder.obj, fieldName, data.field.min, data.field.max);
el.step(data.field.step);
}
else if (type === 'button') {
folder.obj[fieldName] = function () {
//let a = arguments;
//debugger;
//this.sendWS({
// type: ReqType.VALUE_UPDATE,
// data: {
// folderName,
// fieldName,
// value: true
// }
//});
};
el = folder.gui.add(folder.obj, fieldName);
}
else {
throw new Error("Can't handle field of type '" + data.field.type + "'");
}
_this.addChangeListener(el, folderName, fieldName);
});
}
Client.prototype.sendWS = function (msg) {
this.ws.send(JSON.stringify(msg));
};
Client.prototype.addWsHandler = function (type, handler) {
this.wsHandlers.set(type, handler);
};
Client.prototype.addChangeListener = function (el, folderName, fieldName) {
var _this = this;
el.onChange(function (value) {
_this.sendWS({
type: exports.ReqType.VALUE_UPDATE,
data: {
folderName: folderName,
fieldName: fieldName,
value: value,
}
});
});
};
Client.prototype.cleanup = function () {
if (gui) {
this.folders.forEach(function (folder, key) {
var clone = folder.gui.__controllers.slice(0);
clone.forEach(function (fCon) { return folder.gui.remove(fCon); });
});
}
this.folders.clear();
gui.destroy();
gui = undefined;
};
return Client;
}());
exports.Client = Client;
var root = window || global;
root.Client = Client;
},{}],2:[function(require,module,exports){
/**
* @fileOverview
*
* Created on 5/21/16.
* @author Siggi Orn <siggi@jibo.com>
*/
"use strict";
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var jibo_typed_events_1 = require("jibo-typed-events");
var Client_1 = require("./Client");
var FieldEvents = (function (_super) {
__extends(FieldEvents, _super);
function FieldEvents() {
var _this = _super.call(this) || this;
_this.change = new jibo_typed_events_1.Event('Value change');
return _this;
}
return FieldEvents;
}(jibo_typed_events_1.EventContainer));
exports.FieldEvents = FieldEvents;
var Field = (function () {
function Field(folder, name, initial, type) {
this.folder = folder;
this.name = name;
this.initial = initial;
this.type = type;
this.events = new FieldEvents();
this._setCurrent(initial, false, true);
}
Object.defineProperty(Field.prototype, "current", {
get: function () {
return this._current;
},
set: function (value) {
this._setCurrent(value, false);
},
enumerable: true,
configurable: true
});
Field.prototype._setCurrent = function (value, fromRemote, noChange) {
if (noChange === void 0) { noChange = false; }
var forceChangeEvent = false;
if (this._limitInput) {
value = this._limitInput(value);
if (value === null) {
forceChangeEvent = true;
}
}
var change = this._current !== value;
this._current = value;
if (!fromRemote) {
this._updateRemote();
}
if ((forceChangeEvent || change) && !noChange) {
this.events.change.emit(this._current);
}
};
Field.prototype._updateRemote = function () {
// Update remote
this.folder.server.sendWS(null, {
type: Client_1.ReqType.VALUE_UPDATE,
data: {
fieldName: this.name,
folderName: this.folder.name,
value: this._current
}
});
};
/**
* Returns a basic representation of this element for serialization
* @returns {any}
*/
Field.prototype.toSimpleObject = function () {
var out = {};
for (var _i = 0, _a = Object.keys(this); _i < _a.length; _i++) {
var attr = _a[_i];
if (attr !== 'events' && attr !== 'folder') {
out[attr] = this[attr];
}
}
return out;
};
return Field;
}());
exports.Field = Field;
var StringField = (function (_super) {
__extends(StringField, _super);
function StringField(folder, name, initial) {
if (initial === void 0) { initial = ''; }
var _this = _super.call(this, folder, name, initial, 'string') || this;
_this.initial = initial;
return _this;
}
return StringField;
}(Field));
exports.StringField = StringField;
var DropdownField = (function (_super) {
__extends(DropdownField, _super);
function DropdownField(folder, name, options, initialIndex) {
if (initialIndex === void 0) { initialIndex = 0; }
var _this = _super.call(this, folder, name, (options[initialIndex] || ''), 'string') || this;
_this.options = options;
if (initialIndex < 0 || initialIndex >= options.length) {
throw Error("Tunable: '" + _this.folder.name + "/" + _this.name + "' initial index " + initialIndex + " out of bounds");
}
_this._limitInput = function (input) {
var index = options.indexOf(input);
if (index === -1) {
console.warn("Tunable: '" + _this.folder.name + "/" + _this.name + "' input " + input + " not a valid option, skipping");
input = _this.current;
}
return input;
};
return _this;
}
return DropdownField;
}(Field));
exports.DropdownField = DropdownField;
var CheckboxField = (function (_super) {
__extends(CheckboxField, _super);
function CheckboxField(folder, name, initial) {
if (initial === void 0) { initial = false; }
var _this = _super.call(this, folder, name, initial, 'boolean') || this;
_this.initial = initial;
return _this;
}
return CheckboxField;
}(Field));
exports.CheckboxField = CheckboxField;
var NumberField = (function (_super) {
__extends(NumberField, _super);
function NumberField(folder, name, initial, min, max, step) {
if (initial === void 0) { initial = 0; }
if (step === void 0) { step = -1; }
var _this = _super.call(this, folder, name, initial, 'number') || this;
_this.initial = initial;
_this.min = min;
_this.max = max;
_this.step = step;
if (min > initial || max < initial) {
throw new Error("Invalid bounds, min: " + min + ", max: " + max + ", initial: " + initial);
}
if (step === -1) {
_this.step = (max - min) / 100;
}
_this._limitInput = function (input) {
if (input < _this.min) {
console.warn("Tunable: '" + _this.folder.name + "/" + _this.name + "' input " + input + " lower than min " + _this.min + ", capping");
input = _this.min;
}
else if (input > _this.max) {
console.warn("Tunable: '" + _this.folder.name + "/" + _this.name + "' input " + input + " higher than max " + _this.max + ", capping");
input = _this.max;
}
return input;
};
return _this;
}
return NumberField;
}(Field));
exports.NumberField = NumberField;
var ButtonField = (function (_super) {
__extends(ButtonField, _super);
function ButtonField(folder, name) {
var _this = _super.call(this, folder, name, false, 'button') || this;
// Sending null to enforce a change event
_this._limitInput = function () {
return null;
};
return _this;
}
ButtonField.prototype.press = function () {
this.current = true;
};
return ButtonField;
}(Field));
exports.ButtonField = ButtonField;
},{"./Client":1,"jibo-typed-events":undefined}],3:[function(require,module,exports){
/**
* @fileOverview
*
* Created on 5/21/16.
* @author Siggi Orn <siggi@jibo.com>
*/
"use strict";
var Folder = (function () {
function Folder(name, server) {
this.name = name;
this.server = server;
this.fields = new Map();
}
Folder.prototype._installField = function (name, creator) {
var el = this.fields.get(name);
if (el) {
return el;
}
el = creator();
this.server.notifyClientOfNewField(null, this.name, name, el);
this.fields.set(name, el);
return el;
};
Folder.prototype.getFieldOrThrow = function (name) {
var el = this.fields.get(name);
if (!el) {
throw new Error("No string field named '" + name + "'");
}
return el;
};
Folder.prototype.getValue = function (name) {
return this.getFieldOrThrow(name).current;
};
return Folder;
}());
exports.Folder = Folder;
},{}],4:[function(require,module,exports){
"use strict";
var path = require("path");
var jibo_cai_utils_1 = require("jibo-cai-utils");
var express = require('express');
var ws = require('nodejs-websocket');
var Folder_1 = require("./Folder");
var Client_1 = require("./Client");
jibo_cai_utils_1.PromiseUtils.catchUnhandledRejection();
var root = jibo_cai_utils_1.FileUtils.getProjectRoot(__dirname);
var shouldLog = false;
function log() {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
if (shouldLog) {
var func = console.log;
func.apply(void 0, args);
}
}
var Server = (function () {
function Server(port) {
var _this = this;
this.port = port;
this.expressApp = express();
this.wsConnections = new Set();
this.wsHandlers = new Map();
this.folders = new Map();
/**
* Set up Webserver
*/
// Handle root
this.expressApp.get('/', function (req, res) {
jibo_cai_utils_1.FileUtils.readFile(root + '/public/index.html')
.then(function (data) {
data = data.replace(RegExp('PORT_TO_USE'), '' + (_this.port + 1));
res.send(data);
})
.catch(function (e) { return res.sendStatus(404); });
});
// Get certain files from transpiled directory
this.expressApp.get('/jibo-tunable-client.js', function (req, res) {
var filename = path.basename(req.path);
log('Request for transpiled file: ' + filename);
jibo_cai_utils_1.FileUtils.readFile(root + '/lib/' + filename)
.then(function (data) { return res.send(data); })
.catch(function (e) { return res.sendStatus(404); });
});
// Host files from public directory
this.expressApp.get('/**.*', function (req, res) {
var filename = path.basename(req.path);
log('Request for regular file: ' + filename);
jibo_cai_utils_1.FileUtils.readFile(root + '/public/' + filename)
.then(function (data) {
res.send(data);
})
.catch(function (e) { return res.sendStatus(404); });
});
/**
* Set up websockets
*/
this.webSocketServer = ws.createServer(function (wsConn) {
_this.wsConnections.add(wsConn);
log('WS Connection opened');
wsConn.on('text', function (str) {
log('Got WS message: ' + str);
var req = JSON.parse(str);
var handler = _this.wsHandlers.get(req.type);
if (handler) {
var resp = handler(wsConn, req.data);
if (resp) {
_this.sendWS(wsConn, resp);
}
}
});
wsConn.on("close", function (code, reason) {
_this.wsConnections.delete(wsConn);
log("WS Connection closed");
});
});
/**
* Here we add some handlers
*/
// On INIT we send all fields to client
this.addWsMessageHandler(Client_1.ReqType.INIT, function (wsConn, data) {
_this.folders.forEach(function (folder, folderName) {
folder.fields.forEach(function (field, fieldName) {
_this.notifyClientOfNewField(wsConn, folderName, fieldName, field);
});
});
});
// On value update we update values
this.addWsMessageHandler(Client_1.ReqType.VALUE_UPDATE, function (wsConn, data) {
var folder = _this.folders.get(data.folderName);
if (!folder) {
throw new Error("No folder of name '" + data.folderName + "'");
}
var field = folder.fields.get(data.fieldName);
if (!field) {
throw new Error("No field of name '" + data.fieldName + "'");
}
field._setCurrent(data.value, true);
});
}
Server.prototype.notifyClientOfNewField = function (wsConn, folderName, fieldName, field) {
var _this = this;
var msg = {
type: Client_1.ReqType.NEW_FIELD,
data: {
folderName: folderName,
fieldName: fieldName,
field: field.toSimpleObject(),
}
};
if (wsConn) {
this.sendWS(wsConn, msg);
}
else {
this.wsConnections.forEach(function (_wsConn) {
_this.sendWS(_wsConn, msg);
});
}
};
Server.prototype.getFolder = function (name) {
var nameToUse = (name === '') ? 'Tunable Parameters' : name;
var folder = this.folders.get(nameToUse);
if (!folder) {
folder = new Folder_1.Folder(nameToUse, this);
this.folders.set(nameToUse, folder);
}
return folder;
};
Server.prototype.start = function () {
var _this = this;
var errHandler = function (e) {
console.error("jibo-tunable error: ", e);
_this.close();
};
// Start servers
this.httpServer = this.expressApp.listen(this.port);
this.httpServer.on('error', errHandler);
this.webSocketServer.listen(this.port + 1);
this.webSocketServer.on('error', errHandler);
log('Tunable server started on port ' + this.port);
};
Server.prototype.close = function () {
if (this.httpServer) {
this.httpServer.close();
this.httpServer = null;
}
if (this.webSocketServer) {
this.webSocketServer.close();
this.webSocketServer = null;
}
};
Server.prototype.broadcastWS = function (msg) {
var _this = this;
this.wsConnections.forEach(function (ws) {
_this.sendWS(ws, msg);
});
};
Server.prototype.sendWS = function (wsConn, msg) {
var json = JSON.stringify(msg);
if (!wsConn) {
this.wsConnections.forEach(function (_wsConn) {
_wsConn.sendText(json);
});
}
else {
wsConn.sendText(json);
}
};
Server.prototype.addWsMessageHandler = function (type, handler) {
this.wsHandlers.set(type, handler);
};
return Server;
}());
exports.Server = Server;
},{"./Client":1,"./Folder":3,"express":undefined,"jibo-cai-utils":undefined,"nodejs-websocket":undefined,"path":undefined}],5:[function(require,module,exports){
/**
* @fileOverview
*
* Created on 5/21/16.
* @author Siggi Orn <siggi@jibo.com>
*/
"use strict";
var Server_1 = require("./Server");
var Field_1 = require("./Field");
function shutDownHandler(message) {
Tunable.close();
}
process.on('exit', function () { return shutDownHandler('process exit'); });
process.on('SIGINT', function () { return shutDownHandler('SIGINT'); });
process.on('SIGTERM', function () { return shutDownHandler('SIGTERM'); });
/**
* Default server port
* @type {number}
*/
var DEFAULT_PORT = 3333;
var server;
var Tunable = (function () {
function Tunable() {
}
/**
* Initialize server at port number
* This method is optional but can be used to start a server
* at a non-default port, it has to be called before any other
* method here.
* @param {number} port Server port
*/
Tunable.initializeServer = function (port) {
if (server) {
server.close();
}
server = new Server_1.Server(port);
server.start();
};
/**
* Stops both the web and websocket servers
*/
Tunable.close = function () {
if (server) {
server.close();
}
};
/**
* Gets or starts the server, and then gets or creates a folder
* with the requested name
* @param name
* @returns {Folder}
*/
Tunable.getFolder = function (name) {
if (name === void 0) { name = ''; }
if (!server) {
Tunable.initializeServer(DEFAULT_PORT);
}
return server.getFolder(name);
};
/**
* Creates or gets the already created string field for name
* @param {string} name
* @param {string} [initial='']
* @param {string} [folderName=''] Name of GUI folder to use (empty by default)
* @returns {StringField}
*/
Tunable.getStringField = function (name, initial, folderName) {
if (initial === void 0) { initial = ''; }
if (folderName === void 0) { folderName = ''; }
var folder = Tunable.getFolder(folderName);
return folder._installField(name, function () {
return new Field_1.StringField(folder, name, initial);
});
};
/**
* Creates or gets the already created dropdown field for name
* @param {string} name
* @param {string[]} options
* @param {number} [initialIndex=0]
* @param {string} [folderName=''] Name of GUI folder to use (empty by default)
* @returns {DropdownField}
*/
Tunable.getDropdownField = function (name, options, initialIndex, folderName) {
if (initialIndex === void 0) { initialIndex = 0; }
if (folderName === void 0) { folderName = ''; }
var folder = Tunable.getFolder(folderName);
return folder._installField(name, function () {
return new Field_1.DropdownField(folder, name, options, initialIndex);
});
};
/**
* Creates (or gets the already created) checkbox field for name
* @param {string} name
* @param {boolean} [initial=false]
* @param {string} [folderName=''] Name of GUI folder to use (empty by default)
* @returns {CheckboxField}
*/
Tunable.getCheckboxField = function (name, initial, folderName) {
if (initial === void 0) { initial = false; }
if (folderName === void 0) { folderName = ''; }
var folder = Tunable.getFolder(folderName);
return folder._installField(name, function () {
return new Field_1.CheckboxField(folder, name, initial);
});
};
/**
* Creates (or gets the already created) number field for name
* @param {string} name
* @param {number} initial Initial value for number
* @param {number} min Minimum value for number
* @param {number} max Maximum value for number
* @param {number} [step=-1] Increments
* @param {string} [folderName=''] Name of GUI folder to use (empty by default)
* @returns {NumberField}
*/
Tunable.getNumberField = function (name, initial, min, max, step, folderName) {
if (step === void 0) { step = -1; }
if (folderName === void 0) { folderName = ''; }
var folder = Tunable.getFolder(folderName);
return folder._installField(name, function () {
return new Field_1.NumberField(folder, name, initial, min, max, step);
});
};
/**
* Creates (or gets the already created) trigger button field for name
* @param {string} name
* @param {string} [folderName=''] Name of GUI folder to use (empty by default)
* @returns {ButtonField}
*/
Tunable.getButtonField = function (name, folderName) {
if (folderName === void 0) { folderName = ''; }
var folder = Tunable.getFolder(folderName);
return folder._installField(name, function () {
return new Field_1.ButtonField(folder, name);
});
};
/**
* Gets the current value for a field of 'name'
* @param {string} name
* @param {string} [folderName=''] Name of GUI folder to use (empty by default)
* @returns {FieldType}
* @throws Error if no such field exists
*/
Tunable.getValue = function (name, folderName) {
if (folderName === void 0) { folderName = ''; }
var folder = Tunable.getFolder(folderName);
return folder.getValue(name);
};
return Tunable;
}());
exports.Tunable = Tunable;
},{"./Field":2,"./Server":4}],6:[function(require,module,exports){
/**
* @fileOverview
*
* Created on 4/11/16.
* @author Siggi Orn <siggi@jibo.com>
*/
"use strict";
var Tunable_1 = require("./Tunable");
exports.Tunable = Tunable_1.Tunable;
},{"./Tunable":5}],7:[function(require,module,exports){
/**
* @fileOverview
*
* Created on 4/11/16.
* @author Siggi Orn <siggi@jibo.com>
*/
var root = global;
if (!root.__tunableSingleton) {
root.__tunableSingleton = require('./index-internal');
}
module.exports = root.__tunableSingleton;
},{"./index-internal":6}]},{},[7])(7)
});
//# sourceMappingURL=jibo-tunable.js.map