parambox
Version:
Too much parameters to handle ? paramBox is a collection of smart plug and play tools to facilitate the design of javascript projects such as games and cognitive tasks. ParamBox is one of the helper class and allows to modify key variable on the fly witho
696 lines (571 loc) • 22.2 kB
JavaScript
"use strict";
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; };
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var DragBox = function () {
function DragBox() {
var boxElement = arguments.length <= 0 || arguments[0] === undefined ? null : arguments[0];
_classCallCheck(this, DragBox);
// constants
this.MAX_BINDED_PROPERTIES = 15;
this.INIT_WIDTH = 400;
this.INIT_HEIGHT = 300;
this.DEFAULT_BOX_CLASS = "dragbox";
this.DEFAULT_DRAGGABLE = true;
this.DEFAULT_STICKINESS_TYPE = "magnetized";
// ui variables
this.boxElement = boxElement;
this.draggable = this.DEFAULT_DRAGGABLE;
this.boxHTML = null;
// stickness
this.shouldStick = null;
this.shouldMagnetize = null;
this.isStickingX = null;
this.isStickingY = null;
this._stickiness = this.DEFAULT_STICKINESS_TYPE;
this.stickiness = this._stickiness;
// private
this._beingDragged = false;
this._visibility = "hidden";
// keyboard variables
// maps the keys pressed with either true on kedown or false on keyup
this.map = [];
// keep mouse position at all times
this.currentMousePos = { x: -1, y: -1 };
var thisObject = this;
$(document).mousemove(function (event) {
thisObject.currentMousePos.x = event.pageX;
thisObject.currentMousePos.y = event.pageY;
});
// check if the box already exists, else create it
if (!this.boxElement) {
$("#paramBox").remove();
// html for creation
this.boxHTML = '<div id="dragbox" class="' + this.DEFAULT_BOX_CLASS + '" style="visibility:hidden;" draggable="true">' + '<div class="col-xs-12 dragbox-title"><center><h3>Dragbox</h3></center></div>' + '<div class="col-xs-12 dragbox-content"></div>' + '</div>';
$(document.body).append(this.boxHTML);
this.boxElement = $("#dragbox");
}
// keyboard show hide hotkeys events
$(document.body).keydown(function (e) {
thisObject.keyfunction(e);
});
$(document.body).keyup(function (e) {
thisObject.keyfunction(e);
});
$(this.boxElement).find(".dragbox-title").mousedown(function (e) {
thisObject.startDrag(e);
});
$(this.boxElement).find(".dragbox-title").mouseup(function (e) {
thisObject.stopDrag(e);
});
// draggin cleanUp event
$(document).click(function (e) {
thisObject.stopDrag(e);
});
}
// drag methods
_createClass(DragBox, [{
key: "startDrag",
value: function startDrag(e) {
// prevent classic dragging from happening
e.preventDefault();
// check if already being dragged, stop the dragging if so
if (this.beingDragged == "true") {
this.beingDragged = false;
return;
}
// calculate X and Y offset of the mouse compare to the top left corner of the box
var offset = { x: e.clientX - $(this.boxElement).offset().left, y: e.clientY - $(this.boxElement).offset().top };
// set the beingdragged flag to true
this.beingDragged = true;
// start the update loop for smooth dragging
this.loopDrag(offset);
// another way of preventing default, just in case
return false;
}
}, {
key: "stopDrag",
value: function stopDrag() {
this.beingDragged = false;
}
}, {
key: "loopDrag",
value: function loopDrag(offset) {
if (this.beingDragged == true) {
var newPosX = this.currentMousePos.x - offset.x;
var newPosY = this.currentMousePos.y - offset.y;
var element = {
offsetLeft: newPosX,
offsetTop: newPosY,
offsetWidth: $(this.boxElement).width(),
offsetHeigth: $(this.boxElement).height()
};
// maintain box totally visible and check for sticky borders
var constrainedPosition = ParamBox.stayInWindow(element);
var constrainedPositionX = constrainedPosition.x;
var constrainedPositionY = constrainedPosition.y;
// stickiness
// glue
if (this.shouldStick) {
//make the box sticky if collided with x border
if (constrainedPosition.stickyX == -2 || constrainedPosition.stickyX == 2) {
this.isStickingX = true;
}
// for sticky window, check if the box got out of the sticky x aera before authorizing movement in x
if (this.isStickingX && constrainedPosition.stickyX != 0) {
constrainedPositionX = constrainedPosition.leftSticky; //stick to the window
}
// make sure stickiness disapears when out of the sticky zone
if (constrainedPosition.stickyX == 0) {
this.isStickingX = false;
}
// make the box sticky if collided with y border
if (constrainedPosition.stickyY == -2 || constrainedPosition.stickyY == 2) {
this.isStickingY = true;
}
// for sticky window, check if the box got out of the sticky y aera before authorizing movement in y
if (this.isStickingY && constrainedPosition.stickyY != 0) {
constrainedPositionY = constrainedPosition.topSticky; //stick to the window
}
// make sure stickiness disapears when out of the sticky zone
if (constrainedPosition.stickyY == 0) {
this.isStickingY = false;
}
}
// magnet
if (this.shouldMagnetize) {
constrainedPositionX = constrainedPosition.leftSticky;
constrainedPositionY = constrainedPosition.topSticky;
}
var thisObject = this;
$(this.boxElement).animate({
left: constrainedPositionX,
top: constrainedPositionY
}, 25, function () {
thisObject.loopDrag(offset);
});
return false;
}
}
// keyboard functions
}, {
key: "keyfunction",
value: function keyfunction(e) {
// check if shift + P hotkeys were stroke and toggle visibility if so
this.map[e.keyCode] = e.type == 'keydown';
// hide and show parameter box
if (this.map[16] && this.map[80]) {
// 16 == Shift - 80 == P
//make sure to reset value in case keyup event is ignored (keep shift true for rapid toggle)
this.map[80] = false;
// toggle box visibility
this.toggle();
// prevent default action if any
e.preventDefault();
}
}
// visibility functions
}, {
key: "toggle",
value: function toggle() {
// toggle box visibility
if (this.visibility == "hidden") {
this.visibility = "visible";
} else {
this.visibility = "hidden";
}
}
}, {
key: "show",
value: function show() {
this.visibility = "visible";
}
}, {
key: "hide",
value: function hide() {
this.visibility = "hidden";
}
// setters getters
}, {
key: "beingDragged",
set: function set(dragged) {
this._beingDragged = dragged;
$(this.boxElement).attr("beingDragged", dragged);
},
get: function get() {
return this._beingDragged;
}
}, {
key: "visibility",
set: function set(visibility) {
this._visibility = visibility;
$(this.boxElement).css("visibility", visibility);
},
get: function get() {
return this._visibility;
}
}, {
key: "stickiness",
set: function set(type) {
// different type of stickyness for the box : "none", "glue", "magnetized"
switch (type) {
case "none":
this.shouldMagnetize = false;
this.shouldStick = false;
console.log("stickiness set to none");
break;
case "glue":
this.shouldStick = true;
this.shouldMagnetize = false;
console.log("stickiness set to glue");
break;
case "magnetized":
this.shouldStick = false;
this.shouldMagnetize = true;
console.log("stickiness set to magnetized");
break;
default:
throw new Error("this sticky type does not exist. Types are : none, glue, or magnetized.");
}
this._stickiness = type;
}
}, {
key: "title",
set: function set(html) {
if (this.boxElement) {
$(this.boxElement).find(".dragbox-title").html(html);
}
}
// some static helper functions
}], [{
key: "stayInWindow",
value: function stayInWindow(element) {
if (typeof element === 'undefined') {
throw new Error("element is undefined");
}
// constants
var STOP_BEING_STICKY_AFTER = 0.15; // times the size in distance
// coordinates
var left = element.offsetLeft;
var right = element.offsetLeft + element.offsetWidth;
var top = element.offsetTop;
var bottom = element.offsetTop + element.offsetHeigth;
var maxLeft = window.innerWidth - element.offsetWidth;
var maxTop = window.innerHeight - element.offsetHeigth;
return {
x: left < 0 ? 0 : right > window.innerWidth ? maxLeft : left,
y: top < 0 ? 0 : bottom > window.innerHeight ? maxTop : top,
stickyX: left <= 0 ? -2 : left <= STOP_BEING_STICKY_AFTER * element.offsetWidth ? -1 : right >= window.innerWidth ? 2 : right >= window.innerWidth - STOP_BEING_STICKY_AFTER * element.offsetWidth ? 1 : 0,
stickyY: top <= 0 ? -2 : top <= STOP_BEING_STICKY_AFTER * element.offsetHeigth ? -1 : bottom >= window.innerHeight ? 2 : bottom >= window.innerHeight - STOP_BEING_STICKY_AFTER * element.offsetHeigth ? 1 : 0,
leftSticky: left <= 0 ? 0 : left <= STOP_BEING_STICKY_AFTER * element.offsetWidth ? 0 : right >= window.innerWidth ? window.innerWidth - element.offsetWidth : right >= window.innerWidth - STOP_BEING_STICKY_AFTER * element.offsetWidth ? window.innerWidth - element.offsetWidth : left,
topSticky: top <= 0 ? 0 : top <= STOP_BEING_STICKY_AFTER * element.offsetHeigth ? 0 : bottom >= window.innerHeight ? window.innerHeight - element.offsetHeigth : bottom >= window.innerHeight - STOP_BEING_STICKY_AFTER * element.offsetHeigth ? window.innerHeight - element.offsetHeigth : top
};
}
}]);
return DragBox;
}();
var ParamBox = function (_DragBox) {
_inherits(ParamBox, _DragBox);
function ParamBox() {
var boxElement = arguments.length <= 0 || arguments[0] === undefined ? null : arguments[0];
_classCallCheck(this, ParamBox);
// constants
var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(ParamBox).call(this, boxElement));
// call super constructor
_this.DEFAULT_ROW_HTML = '<div class="col-md-12 dragbox-row paramboxtmprow"></div>';
// ui
_this.rowHtml = _this.DEFAULT_ROW_HTML;
// row hold the row object in dom as well as the bindedField object {rowDom: row, bindedField: bindedField}
_this.rows = [];
// set dragbox title
_this.title = '<h5><i class="fa fa-cog fa-1x"></i> Parameter Box</h5>';
return _this;
}
// binding methods
_createClass(ParamBox, [{
key: "bind",
value: function bind(object, properties) {
var constraints = arguments.length <= 2 || arguments[2] === undefined ? null : arguments[2];
if (typeof object == 'undefined') {
throw new Error("object is undefined");
}
if (properties.constructor === Array) {
for (var i = 0; i < properties.length; i++) {
if (typeof object[properties[i]] == 'undefined') {
throw new Error("object property " + properties[i] + " is undefined");
}
var rowDom = this.newRowInDom();
var bindedField = null;
// look for a constrained field
if (constraints != null) {
if (typeof constraints[properties[i]] != 'undefined') {
var bindedField = new BindedField(object, properties[i], rowDom, 'selector', constraints[properties[i]]);
}
}
// if not constrained field found, create the most relevant type of field
if (!bindedField) {
if (object[properties[i]].constructor === Boolean) {
var bindedField = new BindedField(object, properties[i], rowDom, 'selector', ["TRUE", "FALSE"]);
} else {
var bindedField = new BindedField(object, properties[i], rowDom);
}
}
this.rows.push(this.getBindedRow(rowDom, bindedField));
}
} else {
if (typeof object[properties] == 'undefined') {
throw new Error("object property " + properties + " is undefined");
}
var rowDom = this.newRowInDom();
var bindedField = null;
// look for a constrained field
if (constraints != null) {
if (typeof constraints[properties] != 'undefined') {
var bindedField = new BindedField(object, properties, rowDom, 'selector', constraints[properties]);
}
}
// if not constrained field found, create the most relevant type of field
if (!bindedField) {
if (object[property].constructor === Boolean) {
var bindedField = new BindedField(object, properties, rowDom, 'selector', ["TRUE", "FALSE"]);
} else {
var bindedField = new BindedField(object, properties, rowDom);
}
}
this.rows.push(this.getBindedRow(rowDom, bindedField));
}
}
}, {
key: "unbind",
value: function unbind(object, property) {
for (var i = 0; i < this.rows.length; i++) {
var bindedField = this.rows[i].bindedField;
if (bindedField.object === object && bindedField.property == property) {
this.rows.splice(i, 1);
bindedField.delete();
}
}
}
// ui methods
}, {
key: "newRowInDom",
value: function newRowInDom() {
var row = null;
$(this.boxElement).find(".dragbox-content").append(this.rowHtml);
row = this.boxElement.find(".paramboxtmprow");
$(row).removeClass("paramboxtmprow");
return row;
}
}, {
key: "getBindedRow",
value: function getBindedRow(rowDom, bindedField) {
return { rowDom: rowDom, bindedField: bindedField };
}
}, {
key: "refreshView",
value: function refreshView() {
// check if all binded field are displayed in the paramBox
// if not add them
// get rid of unbinded field
}
}]);
return ParamBox;
}(DragBox);
var BindedProperty = function () {
function BindedProperty() {
var object = arguments.length <= 0 || arguments[0] === undefined ? null : arguments[0];
var property = arguments.length <= 1 || arguments[1] === undefined ? null : arguments[1];
_classCallCheck(this, BindedProperty);
// constants
this.HANDLED_VARIABLE_TYPES = ["number", "string", "boolean"];
// data properties
this.property = property;
this.object = object;
this.propagate = false; // to add ? chain propagation? a subscription system maybe...
this.type = null;
if (!this.object) {
// if parent object is not set consider that the binding is with a variable in the global scope
this.object = window;
}
if (property) {
this.bind(object, property);
}
}
// binding function
_createClass(BindedProperty, [{
key: "bind",
value: function bind(object, property) {
var propertyType = _typeof(this.object[this.property]);
if (propertyType === 'undefined') {
throw new Error("The variable you are trying to bind is undefined - either this object or the property is incorrect");
} else {
if (this.HANDLED_VARIABLE_TYPES.indexOf(propertyType) == -1) {
throw new Error("The variable you are trying to bind is of a non-handled type (string, number or boolean");
}
this.property = property;
this.object = object;
this.type = this.object[this.property].constructor;
}
}
}, {
key: "convertToType",
value: function convertToType(value) {
if (this.type) {
if (this.type === Boolean) {
switch (String(value).toUpperCase()) {
case "0":
return false;
break;
case "1":
return true;
break;
case "FALSE":
return false;
break;
case "TRUE":
return true;
break;
}
}
return this.type(value);
} else {
throw new Error("You are trying to convert a value to a the type of the binded property but the object has no property binded to it (or no type)");
}
}
// getters and setters
}, {
key: "value",
set: function set(value) {
if (typeof this.object[this.property] === 'undefined') {
throw new Error("The variable you are trying to bind is undefined - either this object or the property is incorrect");
} else {
this.object[this.property] = this.convertToType(value);
}
},
get: function get() {
if (typeof this.object[this.property] === 'undefined') {
throw new Error("The variable you are trying to bind is undefined - either this object or the property is incorrect");
} else {
return this.object[this.property];
}
}
}]);
return BindedProperty;
}();
var BindedField = function (_BindedProperty) {
_inherits(BindedField, _BindedProperty);
// this class holds an active input field (select, text input, slider component)
// it creates a field from the selected type and bind a binded property to it
function BindedField() {
var object = arguments.length <= 0 || arguments[0] === undefined ? mandatory("object") : arguments[0];
var property = arguments.length <= 1 || arguments[1] === undefined ? mandatory("property") : arguments[1];
var parent = arguments.length <= 2 || arguments[2] === undefined ? null : arguments[2];
var fieldType = arguments.length <= 3 || arguments[3] === undefined ? 'input' : arguments[3];
var allowedValues = arguments.length <= 4 || arguments[4] === undefined ? null : arguments[4];
var addClass = arguments.length <= 5 || arguments[5] === undefined ? null : arguments[5];
_classCallCheck(this, BindedField);
// constant
var _this2 = _possibleConstructorReturn(this, Object.getPrototypeOf(BindedField).call(this, object, property));
_this2.VALID_FIELD_TYPE = ["input", "selector", "slider"];
// field
_this2.field = null;
_this2.fieldType = fieldType;
_this2.fieldHTML = null;
_this2.allowedValues = allowedValues;
_this2.tempClass = "binded-" + (typeof object === "undefined" ? "undefined" : _typeof(object)) + property;
// parent
_this2.parent = parent;
// build the field html
switch (_this2.fieldType) {
case 'input':
_this2.fieldHTML = '<fieldset class="form-group">' + '<label>' + property + '</label>' + '<input type="text" class="form-control ' + _this2.tempClass + '" data-binded="' + property + '">' + '</fieldset>';
break;
case 'selector':
if (!allowedValues) {
throw new Error("fieldType selector needs at least one allowedValues");
}
_this2.fieldHTML = '<fieldset class="form-group">' + '<label>' + property + '</label>' + '<select class="form-control ' + _this2.tempClass + '" data-binded="' + property + '">';
for (var i = 0; i < _this2.allowedValues.length; i++) {
_this2.fieldHTML = _this2.fieldHTML + '<option value="' + _this2.allowedValues[i] + '">' + _this2.allowedValues[i] + '</option>';
}
_this2.fieldHTML = _this2.fieldHTML + '</select></fieldset>';
break;
case 'slider':
break;
default:
throw new Error("fieldType is invalid : input, selector and slider are the only valid type for now");
}
if (parent) {
_this2.placeInParent();
}
return _this2;
}
// ui function
_createClass(BindedField, [{
key: "placeInParent",
value: function placeInParent() {
var parent = arguments.length <= 0 || arguments[0] === undefined ? null : arguments[0];
if (parent) {
this.parent = parent;
}
$(this.parent).append(this.fieldHTML);
this.field = $("." + this.tempClass);
this.field.removeClass(this.tempClass);
this.allowedValues ? this.allowedValues.constructor == Array ? this.field.val(this.allowedValues[0]) : this.field.val(this.value) : this.field.val(this.value);
var thisObject = this;
// add event listener on change
this.field.change(function (e) {
thisObject.update("field");
});
this.field.keyup(function (e) {
thisObject.update("field");
});
}
}, {
key: "delete",
value: function _delete() {
//delete the fieldset
this.field.parent().remove();
this.property = null;
this.object = null;
}
}, {
key: "update",
value: function update() {
var origin = arguments.length <= 0 || arguments[0] === undefined ? "field" : arguments[0];
if (origin == "field") {
this.value = $(this.field).val();
} else {
if ($(this.field).val().toUpperCase() != String(this.value).toUpperCase()) {
$(this.field).val(this.value);
}
}
}
// getters and setters
}, {
key: "value",
set: function set(value) {
if (typeof this.object[this.property] === 'undefined') {
throw new Error("The variable you are trying to bind is undefined - either this object or the property is incorrect");
} else {
this.object[this.property] = this.convertToType(value);
this.update("setter");
}
},
get: function get() {
if (typeof this.object[this.property] === 'undefined') {
throw new Error("The variable you are trying to bind is undefined - either this object or the property is incorrect");
} else {
return this.object[this.property];
}
}
}]);
return BindedField;
}(BindedProperty);
// utilities
function mandatory() {
var param = arguments.length <= 0 || arguments[0] === undefined ? "" : arguments[0];
throw new Error('Missing parameter ' + param);
}