From a30c994e92bc7f7e38b3b2ce87913244bbf3f607 Mon Sep 17 00:00:00 2001 From: Alex de Mulder Date: Fri, 27 Mar 2015 23:01:10 +0100 Subject: [PATCH] finalized a very fancy option configurator with a fancy color picker for color fields! --- dist/vis.css | 117 ++- dist/vis.js | 831 +++++++++++++++--- dist/vis.min.css | 2 +- examples/network/01_basic_usage.html | 1 + lib/network/css/network-colorpicker.css | 116 ++- lib/network/css/network-configuration.css | 1 + lib/network/modules/CanvasRenderer.js | 2 +- lib/network/modules/ConfigurationSystem.js | 213 ++++- lib/network/modules/NodesHandler.js | 8 +- lib/network/modules/components/colorPicker.js | 470 ++++++++-- lib/util.js | 17 +- 11 files changed, 1499 insertions(+), 279 deletions(-) diff --git a/dist/vis.css b/dist/vis.css index 0f7bcce9..78c084bd 100644 --- a/dist/vis.css +++ b/dist/vis.css @@ -858,6 +858,7 @@ div.vis-network-configuration.label.s4{ } div.vis-network-configuration.colorBlock{ + top:1px; width:30px; height:19px; border:1px solid #444444; @@ -1009,13 +1010,16 @@ input.vis-network-configuration.range:focus::-ms-fill-upper { } div.vis-network-colorPicker-frame { - position:relative; - margin-top:40px; - width:310px; - height:310px; + position:absolute; + margin-top:-140px; + margin-left:30px; + width:293px; + height:425px; padding: 10px; - border-radius:20px; + border-radius:15px; background-color:#ffffff; + display:none; + box-shadow: rgba(0,0,0,0.5) 0px 0px 10px 0px; } div.vis-network-colorPicker-color { @@ -1025,20 +1029,16 @@ div.vis-network-colorPicker-color { cursor: pointer; } + + div.vis-network-colorPicker-brightness { - position:absolute; - top:300px; - /*background-color:#00ff00;*/ + position: absolute; + top:313px; } -div.vis-network-colorPicker-saturation { +div.vis-network-colorPicker-opacity { position:absolute; - top:145px; - left:160px; - /*background-color:#ff0000;*/ - -ms-transform: rotate(270deg); /* IE 9 */ - -webkit-transform: rotate(270deg); /* Chrome, Safari, Opera */ - transform: rotate(270deg); + top:350px; } div.vis-network-colorPicker-selector{ @@ -1048,6 +1048,7 @@ div.vis-network-colorPicker-selector{ width:15px; height:15px; border-radius:15px; + border:1px solid #ffffff; background: #4c4c4c; /* Old browsers */ background: -moz-linear-gradient(top, #4c4c4c 0%, #595959 12%, #666666 25%, #474747 39%, #2c2c2c 50%, #000000 51%, #111111 60%, #2b2b2b 76%, #1c1c1c 91%, #131313 100%); /* FF3.6+ */ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#4c4c4c), color-stop(12%,#595959), color-stop(25%,#666666), color-stop(39%,#474747), color-stop(50%,#2c2c2c), color-stop(51%,#000000), color-stop(60%,#111111), color-stop(76%,#2b2b2b), color-stop(91%,#1c1c1c), color-stop(100%,#131313)); /* Chrome,Safari4+ */ @@ -1058,6 +1059,91 @@ div.vis-network-colorPicker-selector{ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#4c4c4c', endColorstr='#131313',GradientType=0 ); /* IE6-9 */ } + + +div.vis-network-colorPicker-newColor { + position:absolute; + width:140px; + height:20px; + border:1px solid rgba(0,0,0,0.1); + border-radius:5px; + top:380px; + left:159px; + text-align:right; + padding-right:2px; + font-size:10px; + color:rgba(0,0,0,0.4); + vertical-align:middle; + line-height:20px; + +} + +div.vis-network-colorPicker-initialColor { + position:absolute; + width:140px; + height:20px; + border:1px solid rgba(0,0,0,0.1); + border-radius:5px; + top:380px; + left:10px; + text-align:left; + padding-left:2px; + font-size:10px; + color:rgba(0,0,0,0.4); + vertical-align:middle; + line-height:20px; +} + +div.vis-network-colorPicker-label { + position:absolute; + width:300px; + left:10px; +} + +div.vis-network-colorPicker-label.brightness { + top:300px; +} + +div.vis-network-colorPicker-label.opacity { + top:338px; +} + +div.vis-network-colorPicker-button { + position:absolute; + width:68px; + height:25px; + border-radius:10px; + vertical-align: middle; + text-align:center; + line-height: 25px; + top:410px; + border:2px solid #d9d9d9; + background-color: #f7f7f7; + cursor:pointer; +} + +div.vis-network-colorPicker-button.cancel { + border:2px solid #ff4e33; + background-color: #ff7761; + left:5px; +} +div.vis-network-colorPicker-button.load { + border:2px solid #a153e6; + background-color: #cb8dff; + left:82px; +} +div.vis-network-colorPicker-button.apply { + border:2px solid #4588e6; + background-color: #82b6ff; + left:159px; +} +div.vis-network-colorPicker-button.save { + border:2px solid #45e655; + background-color: #6dff7c; + left:236px; +} + + input.vis-network-configuration.range.colorPicker{ width: 290px; height:20px; @@ -1070,5 +1156,4 @@ input.vis-network-brightnessRange { input.vis-network-saturationRange { width: 289px !important; - } \ No newline at end of file diff --git a/dist/vis.js b/dist/vis.js index d3530497..4b6ac342 100644 --- a/dist/vis.js +++ b/dist/vis.js @@ -1008,8 +1008,8 @@ return /******/ (function(modules) { // webpackBootstrap } } else { c = {}; - c.background = color.background || "white"; - c.border = color.border || c.background; + c.background = color.background || undefined; + c.border = color.border || undefined; if (exports.isString(color.highlight)) { c.highlight = { @@ -1018,8 +1018,8 @@ return /******/ (function(modules) { // webpackBootstrap }; } else { c.highlight = {}; - c.highlight.background = color.highlight && color.highlight.background || c.background; - c.highlight.border = color.highlight && color.highlight.border || c.border; + c.highlight.background = color.highlight && color.highlight.background || undefined; + c.highlight.border = color.highlight && color.highlight.border || undefined; } if (exports.isString(color.hover)) { @@ -1029,8 +1029,8 @@ return /******/ (function(modules) { // webpackBootstrap }; } else { c.hover = {}; - c.hover.background = color.hover && color.hover.background || c.background; - c.hover.border = color.hover && color.hover.border || c.border; + c.hover.background = color.hover && color.hover.background || undefined; + c.hover.border = color.hover && color.hover.border || undefined; } } @@ -1176,6 +1176,11 @@ return /******/ (function(modules) { // webpackBootstrap var isOk = /rgb\((\d{1,3}),(\d{1,3}),(\d{1,3})\)/i.test(rgb); return isOk; }; + exports.isValidRGBA = function (rgba) { + rgba = rgba.replace(" ", ""); + var isOk = /rgba\((\d{1,3}),(\d{1,3}),(\d{1,3}),(.{1,3})\)/i.test(rgba); + return isOk; + }; /** * This recursively redirects the prototype of JSON objects to the referenceObject @@ -25322,7 +25327,25 @@ return /******/ (function(modules) { // webpackBootstrap if (options) { util.selectiveNotDeepExtend(["color"], this.options, options); if (options.color) { - this.options.color = util.parseColor(options.color); + var parsedColor = util.parseColor(options.color); + if (parsedColor.border !== undefined) { + this.options.color.border = parsedColor.border; + } + if (parsedColor.background !== undefined) { + this.options.color.background = parsedColor.background; + } + if (parsedColor.highlight.border !== undefined) { + this.options.color.highlight.border = parsedColor.highlight.border; + } + if (parsedColor.highlight.background !== undefined) { + this.options.color.highlight.background = parsedColor.highlight.background; + } + if (parsedColor.hover.border !== undefined) { + this.options.color.hover.border = parsedColor.hover.border; + } + if (parsedColor.hover.background !== undefined) { + this.options.color.hover.background = parsedColor.hover.background; + } } } }, @@ -32086,7 +32109,7 @@ return /******/ (function(modules) { // webpackBootstrap * chart will be resized too. */ value: function redraw() { - this.setSize(this.constants.width, this.constants.height); + this._setSize(this.constants.width, this.constants.height); this._redraw(); }, writable: true, @@ -36889,40 +36912,70 @@ return /******/ (function(modules) { // webpackBootstrap } }; - this.actualOptions = {}; + this.actualOptions = { + nodes: {}, + edges: {}, + layout: {}, + interaction: {}, + manipulation: {}, + physics: {}, + selection: {}, + renderer: {}, + configure: false, + configureContainer: undefined + }; this.domElements = []; - this.colorPicker = new ColorPicker(); + this.colorPicker = new ColorPicker(this.network.canvas.pixelRatio); } _prototypeProperties(ConfigurationSystem, null, { setOptions: { + + + /** + * refresh all options. + * Because all modules parse their options by themselves, we just use their options. We copy them here. + * + * @param options + */ value: function setOptions(options) { if (options !== undefined) { - this._clean(); - util.deepExtend(this.actualOptions, options); + util.extend(this.actualOptions, options); + } + + this._clean(); + + if (this.actualOptions.configure !== undefined && this.actualOptions.configure !== false) { + util.deepExtend(this.actualOptions.nodes, this.network.nodesHandler.options, true); + util.deepExtend(this.actualOptions.edges, this.network.edgesHandler.options, true); + util.deepExtend(this.actualOptions.layout, this.network.layoutEngine.options, true); + util.deepExtend(this.actualOptions.interaction, this.network.interactionHandler.options, true); + util.deepExtend(this.actualOptions.manipulation, this.network.manipulation.options, true); + util.deepExtend(this.actualOptions.physics, this.network.nodesHandler.physics, true); + util.deepExtend(this.actualOptions.selection, this.network.selectionHandler.selection, true); + util.deepExtend(this.actualOptions.renderer, this.network.renderer.selection, true); + if (this.actualOptions.configurationContainer !== undefined) { this.container = this.actualOptions.configurationContainer; } else { this.container = this.network.body.container; } - if (this.actualOptions.configure !== undefined && this.actualOptions.configure !== false) { - var config = undefined; - if (this.actualOptions.configure instanceof Array) { - config = this.actualOptions.configure.join(); - } else if (typeof this.actualOptions.configure === "string") { - config = this.actualOptions.configure; - } else if (typeof this.actualOptions.configure === "boolean") { - config = this.actualOptions.configure; - } else { - this._clean(); - throw new Error("the option for configure has to be either a string, boolean or an array. Supplied:" + this.options.configure); - return; - } - this._create(config); - } + var config = undefined; + if (this.actualOptions.configure instanceof Array) { + config = this.actualOptions.configure.join(); + } else if (typeof this.actualOptions.configure === "string") { + config = this.actualOptions.configure; + } else if (typeof this.actualOptions.configure === "boolean") { + config = this.actualOptions.configure; + } else { + this._clean(); + throw new Error("the option for configure has to be either a string, boolean or an array. Supplied:" + this.options.configure); + return; + } + this._create(config); } }, writable: true, @@ -36931,7 +36984,7 @@ return /******/ (function(modules) { // webpackBootstrap _create: { /** - * + * Create all DOM elements * @param {Boolean | String} config * @private */ @@ -36959,12 +37012,18 @@ return /******/ (function(modules) { // webpackBootstrap } this._push(); - this.colorPicker.generate(); + this.colorPicker.insertTo(this.container); }, writable: true, configurable: true }, _push: { + + + /** + * draw all DOM elements on the screen + * @private + */ value: function _push() { for (var i = 0; i < this.domElements.length; i++) { this.container.appendChild(this.domElements[i]); @@ -36974,6 +37033,11 @@ return /******/ (function(modules) { // webpackBootstrap configurable: true }, _clean: { + + /** + * delete all DOM elements + * @private + */ value: function _clean() { for (var i = 0; i < this.domElements.length; i++) { this.container.removeChild(this.domElements[i]); @@ -36984,6 +37048,13 @@ return /******/ (function(modules) { // webpackBootstrap configurable: true }, _getValue: { + + /** + * get the value from the actualOptions if it exists + * @param {array} path | where to look for the actual option + * @returns {*} + * @private + */ value: function _getValue(path) { var base = this.actualOptions; for (var i = 0; i < path.length; i++) { @@ -37000,6 +37071,15 @@ return /******/ (function(modules) { // webpackBootstrap configurable: true }, _addToPath: { + + + /** + * Copy the path and add a step. It needs to copy because the path will keep stacking otherwise. + * @param path + * @param newValue + * @returns {Array} + * @private + */ value: function _addToPath(path, newValue) { var newPath = []; for (var i = 0; i < path.length; i++) { @@ -37012,6 +37092,13 @@ return /******/ (function(modules) { // webpackBootstrap configurable: true }, _makeEntree: { + + /** + * all option elements are wrapped in an entree + * @param path + * @param domElements + * @private + */ value: function _makeEntree(path) { for (var _len = arguments.length, domElements = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { domElements[_key - 1] = arguments[_key]; @@ -37028,6 +37115,12 @@ return /******/ (function(modules) { // webpackBootstrap configurable: true }, _makeHeader: { + + /** + * header for major subjects + * @param name + * @private + */ value: function _makeHeader(name) { var div = document.createElement("div"); div.className = "vis-network-configuration header"; @@ -37038,6 +37131,15 @@ return /******/ (function(modules) { // webpackBootstrap configurable: true }, _makeLabel: { + + /** + * make a label, if it is an object label, it gets different styling. + * @param name + * @param path + * @param objectLabel + * @returns {HTMLElement} + * @private + */ value: function _makeLabel(name, path) { var objectLabel = arguments[2] === undefined ? false : arguments[2]; var div = document.createElement("div"); @@ -37053,6 +37155,15 @@ return /******/ (function(modules) { // webpackBootstrap configurable: true }, _makeDropdown: { + + + /** + * make a dropdown list for multiple possible string optoins + * @param arr + * @param value + * @param path + * @private + */ value: function _makeDropdown(arr, value, path) { var select = document.createElement("select"); select.className = "vis-network-configuration select"; @@ -37085,6 +37196,15 @@ return /******/ (function(modules) { // webpackBootstrap configurable: true }, _makeRange: { + + + /** + * make a range object for numeric options + * @param arr + * @param value + * @param path + * @private + */ value: function _makeRange(arr, value, path) { var defaultValue = arr[0]; var min = arr[1]; @@ -37127,6 +37247,15 @@ return /******/ (function(modules) { // webpackBootstrap configurable: true }, _makeCheckbox: { + + + /** + * make a checkbox for boolean options. + * @param defaultValue + * @param value + * @param path + * @private + */ value: function _makeCheckbox(defaultValue, value, path) { var checkbox = document.createElement("input"); checkbox.type = "checkbox"; @@ -37148,21 +37277,31 @@ return /******/ (function(modules) { // webpackBootstrap configurable: true }, _makeColorField: { + + + /** + * make a color field with a color picker for color fields + * @param arr + * @param value + * @param path + * @private + */ value: function _makeColorField(arr, value, path) { var _this = this; var defaultColor = arr[1]; var div = document.createElement("div"); + value = value === undefined ? defaultColor : value; - if (defaultColor !== "none") { + if (value !== "none") { div.className = "vis-network-configuration colorBlock"; - div.style.backgroundColor = defaultColor; + div.style.backgroundColor = value; } else { div.className = "vis-network-configuration colorBlock none"; } value = value === undefined ? defaultColor : value; - div.onclick = function () { - _this._showColorPicker(value, div); + div.onclick = function (event) { + _this._showColorPicker(event, value, div, path); }; var label = this._makeLabel(path[path.length - 1], path); @@ -37172,13 +37311,38 @@ return /******/ (function(modules) { // webpackBootstrap configurable: true }, _showColorPicker: { - value: function _showColorPicker(value, div) { - this.colorPicker.show(div); + + + /** + * used by the color buttons to call the color picker. + * @param event + * @param value + * @param div + * @param path + * @private + */ + value: function _showColorPicker(event, value, div, path) { + var _this = this; + this.colorPicker.show(event.pageX, event.pageY); + this.colorPicker.setColor(value); + this.colorPicker.setCallback(function (color) { + var colorString = "rgba(" + color.r + "," + color.g + "," + color.b + "," + color.a + ")"; + div.style.backgroundColor = colorString; + _this._update(colorString, path); + }); }, writable: true, configurable: true }, _handleObject: { + + + /** + * parse an object and draw the correct entrees + * @param obj + * @param path + * @private + */ value: function _handleObject(obj) { var path = arguments[1] === undefined ? [] : arguments[1]; for (var subObj in obj) { @@ -37207,6 +37371,16 @@ return /******/ (function(modules) { // webpackBootstrap configurable: true }, _handleArray: { + + + /** + * handle the array type of option + * @param optionName + * @param arr + * @param value + * @param path + * @private + */ value: function _handleArray(optionName, arr, value, path) { if (typeof arr[0] === "string" && arr[0] === "color") { this._makeColorField(arr, value, path); @@ -37220,6 +37394,17 @@ return /******/ (function(modules) { // webpackBootstrap configurable: true }, _handleString: { + + + /** + * handle the string type of option. + * TODO: Not sure what to do with this + * @param optionName + * @param string + * @param value + * @param path + * @private + */ value: function _handleString(optionName, string, value, path) { if (string === "string") {} else {} }, @@ -37227,6 +37412,14 @@ return /******/ (function(modules) { // webpackBootstrap configurable: true }, _update: { + + + /** + * called to update the network with the new settings. + * @param value + * @param path + * @private + */ value: function _update(value, path) { var options = {}; var pointer = options; @@ -37238,6 +37431,7 @@ return /******/ (function(modules) { // webpackBootstrap pointer[path[i]] = value; } } + console.log("options", options); this.network.setOptions(options); }, writable: true, @@ -37272,19 +37466,338 @@ return /******/ (function(modules) { // webpackBootstrap var ColorPicker = (function () { function ColorPicker() { + var pixelRatio = arguments[0] === undefined ? 1 : arguments[0]; _classCallCheck(this, ColorPicker); - this.touchTime = 0; - this.pixelRatio = 1; + this.pixelRatio = pixelRatio; this.generated = false; - this.color = undefined; + this.centerCoordinates = { x: 289 / 2, y: 289 / 2 }; + this.r = 289 * 0.49; + this.color = { r: 255, g: 255, b: 255, a: 1 }; + this.hueCircle = undefined; + this.initialColor = { r: 255, g: 255, b: 255, a: 1 }; + this.previousColor = undefined; + this.applied = false; + + // bound by + this.updateCallback = function () {}; - this.create(); + // create all DOM elements + this._create(); } _prototypeProperties(ColorPicker, null, { - create: { - value: function create() { + insertTo: { + + + /** + * this inserts the colorPicker into a div from the DOM + * @param container + */ + value: function insertTo(container) { + if (this.hammer !== undefined) { + this.hammer.destroy(); + this.hammer = undefined; + } + this.container = container; + this.container.appendChild(this.frame); + this._bindHammer(); + + this._setSize(); + }, + writable: true, + configurable: true + }, + setCallback: { + + /** + * the callback is executed on apply and save. Bind it to the application + * @param callback + */ + value: function setCallback(callback) { + if (typeof callback === "function") { + this.updateCallback = callback; + } else { + throw new Error("Function attempted to set as colorPicker callback is not a function."); + } + }, + writable: true, + configurable: true + }, + setColor: { + + + /** + * Set the color of the colorPicker + * Supported formats: + * '#ffffff' --> hex string + * 'rbg(255,255,255)' --> rgb string + * 'rgba(255,255,255,1.0)' --> rgba string + * {r:255,g:255,b:255} --> rgb object + * {r:255,g:255,b:255,a:1.0} --> rgba object + * @param color + * @param setInitial + */ + value: function setColor(color) { + var setInitial = arguments[1] === undefined ? true : arguments[1]; + if (color === "none") { + return; + } + + var rgba = undefined; + + // check format + if (util.isString(color) === true) { + if (util.isValidRGB(color) === true) { + var rgbaArray = color.substr(4).substr(0, color.length - 5).split(","); + rgba = { r: rgbaArray[0], g: rgbaArray[1], b: rgbaArray[2], a: 1 }; + } else if (util.isValidRGBA(color) === true) { + var rgbaArray = color.substr(5).substr(0, color.length - 6).split(","); + rgba = { r: rgbaArray[0], g: rgbaArray[1], b: rgbaArray[2], a: rgbaArray[3] }; + } else if (util.isValidHex(color) === true) { + var rgbObj = util.hexToRGB(color); + rgba = { r: rgbObj.r, g: rgbObj.g, b: rgbObj.b, a: 1 }; + } + } else { + if (color instanceof Object) { + if (color.r !== undefined && color.g !== undefined && color.b !== undefined) { + var alpha = color.a !== undefined ? color.a : "1.0"; + rgba = { r: color.r, g: color.g, b: color.b, a: alpha }; + } + } + } + + // set color + if (rgba === undefined) { + throw new Error("Unknown color passed to the colorPicker. Supported are strings: rgb, hex, rgba. Object: rgb ({r:r,g:g,b:b,[a:a]}). Supplied: " + JSON.stringify(color)); + } else { + this._setColor(rgba, setInitial); + } + }, + writable: true, + configurable: true + }, + show: { + + + /** + * this shows the color picker at a location. The hue circle is constructed once and stored. + * @param x + * @param y + */ + value: function show(x, y) { + this.applied = false; + this.frame.style.display = "block"; + this.frame.style.top = y + "px"; + this.frame.style.left = x + "px"; + this._generateHueCircle(); + }, + writable: true, + configurable: true + }, + _hide: { + + + // ------------------------------------------ PRIVATE ----------------------------- // + + /** + * Hide the picker. Is called by the cancel button. + * Optional boolean to store the previous color for easy access later on. + * @param storePrevious + * @private + */ + value: function _hide() { + var storePrevious = arguments[0] === undefined ? true : arguments[0]; + // store the previous color for next time; + if (storePrevious === true) { + this.previousColor = util.extend({}, this.color); + } + + if (this.applied === true) { + this.updateCallback(this.initialColor); + } + + this.frame.style.display = "none"; + }, + writable: true, + configurable: true + }, + _save: { + + + /** + * bound to the save button. Saves and hides. + * @private + */ + value: function _save() { + this.updateCallback(this.color); + this.applied = false; + this.hide(); + }, + writable: true, + configurable: true + }, + _apply: { + + + /** + * Bound to apply button. Saves but does not close. Is undone by the cancel button. + * @private + */ + value: function _apply() { + this.applied = true; + this.updateCallback(this.color); + this._updatePicker(this.color); + }, + writable: true, + configurable: true + }, + _loadLast: { + + + /** + * load the color from the previous session. + * @private + */ + value: function _loadLast() { + if (this.previousColor !== undefined) { + this.setColor(this.previousColor, false); + } else { + alert("There is no last color to load..."); + } + }, + writable: true, + configurable: true + }, + _setColor: { + + + /** + * set the color, place the picker + * @param rgba + * @param setInitial + * @private + */ + value: function _setColor(rgba) { + var setInitial = arguments[1] === undefined ? true : arguments[1]; + // store the initial color + if (setInitial === true) { + console.log("here"); + this.initialColor = util.extend({}, rgba); + } + + this.color = rgba; + var hsv = util.RGBToHSV(rgba.r, rgba.g, rgba.b); + + var angleConvert = 2 * Math.PI; + var radius = this.r * hsv.s; + var x = this.centerCoordinates.x + radius * Math.sin(angleConvert * hsv.h); + var y = this.centerCoordinates.y + radius * Math.cos(angleConvert * hsv.h); + + this.colorPickerSelector.style.left = x - 0.5 * this.colorPickerSelector.clientWidth + "px"; + this.colorPickerSelector.style.top = y - 0.5 * this.colorPickerSelector.clientHeight + "px"; + + this._updatePicker(rgba); + }, + writable: true, + configurable: true + }, + _setOpacity: { + + + /** + * bound to opacity control + * @param value + * @private + */ + value: function _setOpacity(value) { + this.color.a = value / 100; + this._updatePicker(this.color); + }, + writable: true, + configurable: true + }, + _setBrightness: { + + + /** + * bound to brightness control + * @param value + * @private + */ + value: function _setBrightness(value) { + var hsv = util.RGBToHSV(this.color.r, this.color.g, this.color.b); + hsv.v = value / 100; + var rgba = util.HSVToRGB(hsv.h, hsv.s, hsv.v); + rgba.a = this.color.a; + this.color = rgba; + this._updatePicker(); + }, + writable: true, + configurable: true + }, + _updatePicker: { + + + /** + * update the colorpicker. A black circle overlays the hue circle to mimic the brightness decreasing. + * @param rgba + * @private + */ + value: function _updatePicker() { + var rgba = arguments[0] === undefined ? this.color : arguments[0]; + var hsv = util.RGBToHSV(rgba.r, rgba.g, rgba.b); + var ctx = this.colorPickerCanvas.getContext("2d"); + if (this.pixelRation === undefined) { + this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1); + } + ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); + + // clear the canvas + var w = this.colorPickerCanvas.clientWidth; + var h = this.colorPickerCanvas.clientHeight; + ctx.clearRect(0, 0, w, h); + + ctx.putImageData(this.hueCircle, 0, 0); + ctx.fillStyle = "rgba(0,0,0," + (1 - hsv.v) + ")"; + ctx.circle(this.centerCoordinates.x, this.centerCoordinates.y, this.r); + ctx.fill(); + + this.brightnessRange.value = 100 * hsv.v; + this.opacityRange.value = 100 * rgba.a; + + this.initialColorDiv.style.backgroundColor = "rgba(" + this.initialColor.r + "," + this.initialColor.g + "," + this.initialColor.b + "," + this.initialColor.a + ")"; + this.newColorDiv.style.backgroundColor = "rgba(" + this.color.r + "," + this.color.g + "," + this.color.b + "," + this.color.a + ")"; + }, + writable: true, + configurable: true + }, + _setSize: { + + + /** + * used by create to set the size of the canvas. + * @private + */ + value: function _setSize() { + this.colorPickerCanvas.style.width = "100%"; + this.colorPickerCanvas.style.height = "100%"; + + this.colorPickerCanvas.width = 289 * this.pixelRatio; + this.colorPickerCanvas.height = 289 * this.pixelRatio; + }, + writable: true, + configurable: true + }, + _create: { + + + /** + * create all dom elements + * TODO: cleanup, lots of similar dom elements + * @private + */ + value: function _create() { var visPrefix = "vis-network-"; this.frame = document.createElement("div"); @@ -37314,65 +37827,136 @@ return /******/ (function(modules) { // webpackBootstrap this.colorPickerDiv.className = visPrefix + "colorPicker-color"; + this.opacityDiv = document.createElement("div"); + this.opacityDiv.className = visPrefix + "colorPicker-opacity"; + this.brightnessDiv = document.createElement("div"); this.brightnessDiv.className = visPrefix + "colorPicker-brightness"; - this.saturationDiv = document.createElement("div"); - this.saturationDiv.className = visPrefix + "colorPicker-saturation"; + this.opacityRange = document.createElement("input"); + this.opacityRange.type = "range"; + this.opacityRange.min = "0"; + this.opacityRange.max = "100"; + this.opacityRange.value = "100"; + this.opacityRange.className = visPrefix + "configuration range colorPicker"; this.brightnessRange = document.createElement("input"); this.brightnessRange.type = "range"; this.brightnessRange.min = "0"; this.brightnessRange.max = "100"; this.brightnessRange.value = "100"; - this.brightnessRange.className = "vis-network-configuration range colorPicker"; - - this.saturationRange = document.createElement("input"); - this.saturationRange.type = "range"; - this.saturationRange.min = "0"; - this.saturationRange.max = "100"; - this.saturationRange.value = "100"; - this.saturationRange.className = "vis-network-configuration range colorPicker"; + this.brightnessRange.className = visPrefix + "configuration range colorPicker"; + this.opacityDiv.appendChild(this.opacityRange); this.brightnessDiv.appendChild(this.brightnessRange); - this.saturationDiv.appendChild(this.saturationRange); + + var me = this; + this.opacityRange.onchange = function () { + me._setOpacity(this.value); + }; + this.opacityRange.oninput = function () { + me._setOpacity(this.value); + }; + this.brightnessRange.onchange = function () { + me._setBrightness(this.value); + }; + this.brightnessRange.oninput = function () { + me._setBrightness(this.value); + }; + + this.brightnessLabel = document.createElement("div"); + this.brightnessLabel.className = visPrefix + "colorPicker-label brightness"; + this.brightnessLabel.innerHTML = "brightness:"; + + this.opacityLabel = document.createElement("div"); + this.opacityLabel.className = visPrefix + "colorPicker-label opacity"; + this.opacityLabel.innerHTML = "opacity:"; + + this.newColorDiv = document.createElement("div"); + this.newColorDiv.className = visPrefix + "colorPicker-newColor"; + this.newColorDiv.innerHTML = "new"; + + this.initialColorDiv = document.createElement("div"); + this.initialColorDiv.className = visPrefix + "colorPicker-initialColor"; + this.initialColorDiv.innerHTML = "initial"; + + this.cancelButton = document.createElement("div"); + this.cancelButton.className = visPrefix + "colorPicker-button cancel"; + this.cancelButton.innerHTML = "cancel"; + this.cancelButton.onclick = this._hide.bind(this, false); + + this.applyButton = document.createElement("div"); + this.applyButton.className = visPrefix + "colorPicker-button apply"; + this.applyButton.innerHTML = "apply"; + this.applyButton.onclick = this._apply.bind(this); + + this.saveButton = document.createElement("div"); + this.saveButton.className = visPrefix + "colorPicker-button save"; + this.saveButton.innerHTML = "save"; + this.saveButton.onclick = this._save.bind(this); + + this.loadButton = document.createElement("div"); + this.loadButton.className = visPrefix + "colorPicker-button load"; + this.loadButton.innerHTML = "load last"; + this.loadButton.onclick = this._loadLast.bind(this); this.frame.appendChild(this.colorPickerDiv); - this.frame.appendChild(this.saturationDiv); + this.frame.appendChild(this.brightnessLabel); this.frame.appendChild(this.brightnessDiv); - }, - writable: true, - configurable: true - }, - show: { - value: function show(container) { - this.container = container; - this.container.appendChild(this.frame); - this.bindHammer(); + this.frame.appendChild(this.opacityLabel); + this.frame.appendChild(this.opacityDiv); + this.frame.appendChild(this.newColorDiv); + this.frame.appendChild(this.initialColorDiv); - this.setSize(); + this.frame.appendChild(this.cancelButton); + this.frame.appendChild(this.applyButton); + this.frame.appendChild(this.saveButton); + this.frame.appendChild(this.loadButton); }, writable: true, configurable: true }, - setColor: { - value: function setColor(color) {}, - writable: true, - configurable: true - }, - setSize: { - value: function setSize() { - this.colorPickerCanvas.style.width = "100%"; - this.colorPickerCanvas.style.height = "100%"; + _bindHammer: { + - this.colorPickerCanvas.width = this.colorPickerDiv.clientWidth * this.pixelRatio; - this.colorPickerCanvas.height = this.colorPickerDiv.clientHeight * this.pixelRatio; + /** + * bind hammer to the color picker + * @private + */ + value: function _bindHammer() { + var _this = this; + this.drag = {}; + this.pinch = {}; + this.hammer = new Hammer(this.colorPickerCanvas); + this.hammer.get("pinch").set({ enable: true }); + + hammerUtil.onTouch(this.hammer, function (event) { + _this._moveSelector(event); + }); + this.hammer.on("tap", function (event) { + _this._moveSelector(event); + }); + this.hammer.on("panstart", function (event) { + _this._moveSelector(event); + }); + this.hammer.on("panmove", function (event) { + _this._moveSelector(event); + }); + this.hammer.on("panend", function (event) { + _this._moveSelector(event); + }); }, writable: true, configurable: true }, - generate: { - value: function generate() { + _generateHueCircle: { + + + /** + * generate the hue circle. This is relatively heavy (200ms) and is done only once on the first time it is shown. + * @private + */ + value: function _generateHueCircle() { if (this.generated === false) { var ctx = this.colorPickerCanvas.getContext("2d"); if (this.pixelRation === undefined) { @@ -37385,63 +37969,48 @@ return /******/ (function(modules) { // webpackBootstrap var h = this.colorPickerCanvas.clientHeight; ctx.clearRect(0, 0, w, h); + + // draw hue circle var x = undefined, y = undefined, hue = undefined, sat = undefined; - var center = { x: w * 0.5, y: h * 0.5 }; - var r = 0.49 * w; + this.centerCoordinates = { x: w * 0.5, y: h * 0.5 }; + this.r = 0.49 * w; var angleConvert = 2 * Math.PI / 360; var hfac = 1 / 360; - var sfac = 1 / r; + var sfac = 1 / this.r; var rgb = undefined; for (hue = 0; hue < 360; hue++) { - for (sat = 0; sat < r; sat++) { - x = center.x + sat * Math.sin(angleConvert * hue); - y = center.y + sat * Math.cos(angleConvert * hue); + for (sat = 0; sat < this.r; sat++) { + x = this.centerCoordinates.x + sat * Math.sin(angleConvert * hue); + y = this.centerCoordinates.y + sat * Math.cos(angleConvert * hue); rgb = util.HSVToRGB(hue * hfac, sat * sfac, 1); ctx.fillStyle = "rgb(" + rgb.r + "," + rgb.g + "," + rgb.b + ")"; ctx.fillRect(x - 0.5, y - 0.5, 2, 2); } } + ctx.strokeStyle = "rgba(0,0,0,1)"; + ctx.circle(this.centerCoordinates.x, this.centerCoordinates.y, this.r); + ctx.stroke(); + + this.hueCircle = ctx.getImageData(0, 0, w, h); } this.generated = true; }, writable: true, configurable: true }, - bindHammer: { - value: function bindHammer() { - var _this = this; - this.drag = {}; - this.pinch = {}; - this.hammer = new Hammer(this.colorPickerCanvas); - this.hammer.get("pinch").set({ enable: true }); + _moveSelector: { - hammerUtil.onTouch(this.hammer, function (event) { - _this.moveSelector(event); - }); - this.hammer.on("tap", function (event) { - _this.moveSelector(event); - }); - //this.hammer.on('doubletap', (event) => {this.moveSelector(event)}); - //this.hammer.on('press', (event) => {this.moveSelector(event)}); - this.hammer.on("panstart", function (event) { - _this.moveSelector(event); - }); - this.hammer.on("panmove", function (event) { - _this.moveSelector(event); - }); - this.hammer.on("panend", function (event) { - _this.moveSelector(event); - }); - //this.hammer.on('pinch', (event) => {this.moveSelector(event)}); - }, - writable: true, - configurable: true - }, - moveSelector: { - value: function moveSelector(event) { + + /** + * move the selector. This is called by hammer functions. + * + * @param event + * @private + */ + value: function _moveSelector(event) { var rect = this.colorPickerDiv.getBoundingClientRect(); var left = event.center.x - rect.left; var top = event.center.y - rect.top; @@ -37452,30 +38021,29 @@ return /******/ (function(modules) { // webpackBootstrap var x = left - centerX; var y = top - centerY; - var angle = Math.atan(y / x); - if (x < 0) { - angle += Math.PI; - } + var angle = Math.atan2(x, y); var radius = 0.98 * Math.min(Math.sqrt(x * x + y * y), centerX); - var newTop = Math.sin(angle) * radius + centerY; - var newLeft = Math.cos(angle) * radius + centerX; + var newTop = Math.cos(angle) * radius + centerY; + var newLeft = Math.sin(angle) * radius + centerX; this.colorPickerSelector.style.top = newTop - 0.5 * this.colorPickerSelector.clientHeight + "px"; this.colorPickerSelector.style.left = newLeft - 0.5 * this.colorPickerSelector.clientWidth + "px"; - }, - writable: true, - configurable: true - }, - redraw: { - value: function redraw(roomController) { - if (this.frame === undefined) { - this._create(); - } - var pos = roomController.canvasToDOM({ x: 0, y: 0 }); - this.frame.style.top = "50px"; - this.frame.style.left = pos.x - 350 + "px"; + // set color + var h = angle / (2 * Math.PI); + h = h < 0 ? h + 1 : h; + var s = radius / this.r; + var hsv = util.RGBToHSV(this.color.r, this.color.g, this.color.b); + hsv.h = h; + hsv.s = s; + var rgba = util.HSVToRGB(hsv.h, hsv.s, hsv.v); + rgba.a = this.color.a; + this.color = rgba; + + // update previews + this.initialColorDiv.style.backgroundColor = "rgba(" + this.initialColor.r + "," + this.initialColor.g + "," + this.initialColor.b + "," + this.initialColor.a + ")"; + this.newColorDiv.style.backgroundColor = "rgba(" + this.color.r + "," + this.color.g + "," + this.color.b + "," + this.color.a + ")"; }, writable: true, configurable: true @@ -37486,7 +38054,6 @@ return /******/ (function(modules) { // webpackBootstrap })(); module.exports = ColorPicker; - //todo make /***/ } /******/ ]) diff --git a/dist/vis.min.css b/dist/vis.min.css index 8dd1a07e..77194175 100644 --- a/dist/vis.min.css +++ b/dist/vis.min.css @@ -1 +1 @@ -div.network-manipulation-closeDiv,div.network-manipulationUI,div.network-navigation{-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none;-khtml-user-select:none}div.network-manipulation-closeDiv,div.network-manipulationUI{background-repeat:no-repeat;cursor:pointer;-webkit-touch-callout:none}.vis .overlay{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10}.vis-active{box-shadow:0 0 10px #86d5f8}.vis [class*=span]{min-height:0;width:auto}.vis.timeline.root{position:relative;border:1px solid #bfbfbf;overflow:hidden;padding:0;margin:0;box-sizing:border-box}.vis.timeline .vispanel{position:absolute;padding:0;margin:0;box-sizing:border-box}.vis.timeline .vispanel.bottom,.vis.timeline .vispanel.center,.vis.timeline .vispanel.left,.vis.timeline .vispanel.right,.vis.timeline .vispanel.top{border:1px #bfbfbf}.vis.timeline .vispanel.center,.vis.timeline .vispanel.left,.vis.timeline .vispanel.right{border-top-style:solid;border-bottom-style:solid;overflow:hidden}.vis.timeline .vispanel.bottom,.vis.timeline .vispanel.center,.vis.timeline .vispanel.top{border-left-style:solid;border-right-style:solid}.vis.timeline .background{overflow:hidden}.vis.timeline .vispanel>.content{position:relative}.vis.timeline .vispanel .shadow{position:absolute;width:100%;height:1px;box-shadow:0 0 10px rgba(0,0,0,.8)}.vis.timeline .vispanel .shadow.top{top:-1px;left:0}.vis.timeline .vispanel .shadow.bottom{bottom:-1px;left:0}.vis.timeline .labelset{position:relative;overflow:hidden;box-sizing:border-box}.vis.timeline .labelset .vlabel{position:relative;left:0;top:0;width:100%;color:#4d4d4d;box-sizing:border-box;border-bottom:1px solid #bfbfbf}.vis.timeline .labelset .vlabel:last-child{border-bottom:none}.vis.timeline .labelset .vlabel .inner{display:inline-block;padding:5px}.vis.timeline .labelset .vlabel .inner.hidden{padding:0}.vis.timeline .itemset{position:relative;padding:0;margin:0;box-sizing:border-box}.vis.timeline .itemset .background,.vis.timeline .itemset .foreground{position:absolute;width:100%;height:100%;overflow:visible}.vis.timeline .axis{position:absolute;width:100%;height:0;left:0;z-index:1}.vis.timeline .foreground .group{position:relative;box-sizing:border-box;border-bottom:1px solid #bfbfbf}.vis.timeline .foreground .group:last-child{border-bottom:none}.vis.timeline .item{position:absolute;color:#1A1A1A;border-color:#97B0F8;border-width:1px;background-color:#D5DDF6;display:inline-block;overflow:hidden}.vis.timeline .item.selected{border-color:#FFC200;background-color:#FFF785;z-index:2}.vis.timeline .editable .item.selected{cursor:move}.vis.timeline .item.point.selected{background-color:#FFF785}.vis.timeline .item.box{text-align:center;border-style:solid;border-radius:2px}.vis.timeline .item.point{background:0 0}.vis.timeline .item.dot{position:absolute;padding:0;border-width:4px;border-style:solid;border-radius:4px}.vis.timeline .item.range{border-style:solid;border-radius:2px;box-sizing:border-box}.vis.timeline .item.background{border:none;background-color:rgba(213,221,246,.4);box-sizing:border-box;padding:0;margin:0}.vis.timeline .item.range .content{position:relative;display:inline-block;max-width:100%}.vis.timeline .item.background .content{position:absolute;display:inline-block;max-width:100%;margin:5px}.vis.timeline .item.line{padding:0;position:absolute;width:0;border-left-width:1px;border-left-style:solid}.vis.timeline .item .content{white-space:nowrap;box-sizing:border-box;padding:5px}.vis.timeline .item .delete{background:url(img/timeline/delete.png)top center no-repeat;position:absolute;width:24px;height:24px;top:0;right:-24px;cursor:pointer}.vis.timeline .item.range .drag-left{position:absolute;width:24px;height:100%;top:0;left:-4px;cursor:w-resize}.vis.timeline .item.range .drag-right{position:absolute;width:24px;height:100%;top:0;right:-4px;cursor:e-resize}.vis.timeline .timeaxis{position:relative;overflow:hidden}.vis.timeline .timeaxis.foreground{top:0;left:0;width:100%}.vis.timeline .timeaxis.background{position:absolute;top:0;left:0;width:100%;height:100%}.vis.timeline .timeaxis .text{position:absolute;color:#4d4d4d;padding:3px;white-space:nowrap}.vis.timeline .timeaxis .text.measure{position:absolute;padding-left:0;padding-right:0;margin-left:0;margin-right:0;visibility:hidden}.vis.timeline .timeaxis .grid.vertical{position:absolute;border-left:1px solid}.vis.timeline .timeaxis .grid.minor{border-color:#e5e5e5}.vis.timeline .timeaxis .grid.major{border-color:#bfbfbf}.vis.timeline .currenttime{background-color:#FF7F6E;width:2px;z-index:1}.vis.timeline .customtime{background-color:#6E94FF;width:2px;cursor:move;z-index:1}.vis.timeline .vispanel.background.horizontal .grid.horizontal{position:absolute;width:100%;height:0;border-bottom:1px solid}.vis.timeline .vispanel.background.horizontal .grid.minor{border-color:#e5e5e5}.vis.timeline .vispanel.background.horizontal .grid.major{border-color:#bfbfbf}.vis.timeline .dataaxis .yAxis.major{width:100%;position:absolute;color:#4d4d4d;white-space:nowrap}.vis.timeline .dataaxis .yAxis.major.measure{padding:0;margin:0;border:0;visibility:hidden;width:auto}.vis.timeline .dataaxis .yAxis.minor{position:absolute;width:100%;color:#bebebe;white-space:nowrap}.vis.timeline .dataaxis .yAxis.minor.measure{padding:0;margin:0;border:0;visibility:hidden;width:auto}.vis.timeline .dataaxis .yAxis.title{position:absolute;color:#4d4d4d;white-space:nowrap;bottom:20px;text-align:center}.vis.timeline .dataaxis .yAxis.title.measure{padding:0;margin:0;visibility:hidden;width:auto}.vis.timeline .dataaxis .yAxis.title.left{bottom:0;-webkit-transform-origin:left top;-moz-transform-origin:left top;-ms-transform-origin:left top;-o-transform-origin:left top;transform-origin:left bottom;-webkit-transform:rotate(-90deg);-moz-transform:rotate(-90deg);-ms-transform:rotate(-90deg);-o-transform:rotate(-90deg);transform:rotate(-90deg)}.vis.timeline .dataaxis .yAxis.title.right{bottom:0;-webkit-transform-origin:right bottom;-moz-transform-origin:right bottom;-ms-transform-origin:right bottom;-o-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.vis.timeline .legend{background-color:rgba(247,252,255,.65);padding:5px;border-color:#b3b3b3;border-style:solid;border-width:1px;box-shadow:2px 2px 10px rgba(154,154,154,.55)}.vis.timeline .legendText{white-space:nowrap;display:inline-block}.vis.timeline .graphGroup0{fill:#4f81bd;fill-opacity:0;stroke-width:2px;stroke:#4f81bd}.vis.timeline .graphGroup1{fill:#f79646;fill-opacity:0;stroke-width:2px;stroke:#f79646}.vis.timeline .graphGroup2{fill:#8c51cf;fill-opacity:0;stroke-width:2px;stroke:#8c51cf}.vis.timeline .graphGroup3{fill:#75c841;fill-opacity:0;stroke-width:2px;stroke:#75c841}.vis.timeline .graphGroup4{fill:#ff0100;fill-opacity:0;stroke-width:2px;stroke:#ff0100}.vis.timeline .graphGroup5{fill:#37d8e6;fill-opacity:0;stroke-width:2px;stroke:#37d8e6}.vis.timeline .graphGroup6{fill:#042662;fill-opacity:0;stroke-width:2px;stroke:#042662}.vis.timeline .graphGroup7{fill:#00ff26;fill-opacity:0;stroke-width:2px;stroke:#00ff26}.vis.timeline .graphGroup8{fill:#f0f;fill-opacity:0;stroke-width:2px;stroke:#f0f}.vis.timeline .graphGroup9{fill:#8f3938;fill-opacity:0;stroke-width:2px;stroke:#8f3938}.vis.timeline .fill{fill-opacity:.1;stroke:none}.vis.timeline .bar{fill-opacity:.5;stroke-width:1px}.vis.timeline .point{stroke-width:2px;fill-opacity:1}.vis.timeline .legendBackground{stroke-width:1px;fill-opacity:.9;fill:#fff;stroke:#c2c2c2}.vis.timeline .outline{stroke-width:1px;fill-opacity:1;fill:#fff;stroke:#e5e5e5}.vis.timeline .iconFill{fill-opacity:.3;stroke:none}div.network-manipulationDiv{border-width:0;border-bottom:1px;border-style:solid;border-color:#d6d9d8;background:#fff;background:-moz-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fff),color-stop(48%,#fcfcfc),color-stop(50%,#fafafa),color-stop(100%,#fcfcfc));background:-webkit-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-o-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-ms-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:linear-gradient(to bottom,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#fcfcfc', GradientType=0);position:absolute;left:0;top:0;width:100%;height:30px}div.network-manipulation-editMode{position:absolute;left:0;top:15px;height:30px}div.network-manipulation-closeDiv{position:absolute;right:0;top:0;width:30px;height:30px;background-position:20px 3px;background-image:url(img/network/cross.png);user-select:none}div.network-manipulation-closeDiv:hover{opacity:.6}div.network-manipulationUI{position:relative;top:-7px;font-family:verdana;font-size:12px;-moz-border-radius:15px;border-radius:15px;display:inline-block;background-position:0 0;height:24px;margin:0 0 0 10px;vertical-align:middle;padding:0 8px;user-select:none}div.network-manipulationUI:hover{box-shadow:1px 1px 8px rgba(0,0,0,.2)}div.network-manipulationUI:active{box-shadow:1px 1px 8px rgba(0,0,0,.5)}div.network-manipulationUI.back{background-image:url(img/network/backIcon.png)}div.network-manipulationUI.none:hover{box-shadow:1px 1px 8px transparent;cursor:default}div.network-manipulationUI.none:active{box-shadow:1px 1px 8px transparent}div.network-manipulationUI.none{padding:0}div.network-manipulationUI.notification{margin:2px;font-weight:700}div.network-manipulationUI.add{background-image:url(img/network/addNodeIcon.png)}div.network-manipulationUI.edit{background-image:url(img/network/editIcon.png)}div.network-manipulationUI.edit.editmode{background-color:#fcfcfc;border-style:solid;border-width:1px;border-color:#ccc}div.network-manipulationUI.connect{background-image:url(img/network/connectIcon.png)}div.network-manipulationUI.delete{background-image:url(img/network/deleteIcon.png)}div.network-manipulationLabel{margin:0 0 0 23px;line-height:25px}div.network-seperatorLine{display:inline-block;width:1px;height:20px;background-color:#bdbdbd;margin:5px 7px 0 15px}div.network-navigation_wrapper{position:absolute;left:0;top:0;width:100%;height:100%}div.network-navigation{width:34px;height:34px;-moz-border-radius:17px;border-radius:17px;position:absolute;display:inline-block;background-position:2px 2px;background-repeat:no-repeat;cursor:pointer;-webkit-touch-callout:none;user-select:none}div.network-navigation:hover{box-shadow:0 0 3px 3px rgba(56,207,21,.3)}div.network-navigation:active{box-shadow:0 0 1px 3px rgba(56,207,21,.95)}div.network-navigation.up{background-image:url(img/network/upArrow.png);bottom:50px;left:55px}div.network-navigation.down{background-image:url(img/network/downArrow.png);bottom:10px;left:55px}div.network-navigation.left{background-image:url(img/network/leftArrow.png);bottom:10px;left:15px}div.network-navigation.right{background-image:url(img/network/rightArrow.png);bottom:10px;left:95px}div.network-navigation.zoomIn{background-image:url(img/network/plus.png);bottom:10px;right:15px}div.network-navigation.zoomOut{background-image:url(img/network/minus.png);bottom:10px;right:55px}div.network-navigation.zoomExtends{background-image:url(img/network/zoomExtends.png);bottom:50px;right:15px}div.network-tooltip{position:absolute;visibility:hidden;padding:5px;white-space:nowrap;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;border:1px solid;box-shadow:3px 3px 10px rgba(128,128,128,.5)}div.vis-network-configuration{position:relative;display:block;float:left;font-size:12px}div.vis-network-configuration.entree{display:block;width:495px;height:25px;vertical-align:middle;line-height:25px}div.vis-network-configuration.entree.s2{left:10px;background-color:#f7f8fa;padding-left:5px;border-radius:3px}div.vis-network-configuration.entree.s3{left:20px;background-color:#e4e9f0;padding-left:5px;border-radius:3px}div.vis-network-configuration.entree.s4{left:30px;background-color:#cfd8e6;padding-left:5px;border-radius:3px}div.vis-network-configuration.header{font-size:18px;font-weight:700}div.vis-network-configuration.label{width:120px;height:25px;line-height:25px}div.vis-network-configuration.label.s3{width:110px}div.vis-network-configuration.label.s4{width:100px}div.vis-network-configuration.colorBlock{width:30px;height:19px;border:1px solid #444;border-radius:2px;padding:0;margin:0;cursor:pointer}input.vis-network-configuration.checkbox{left:-5px}input.vis-network-configuration.rangeinput{position:relative;top:-5px;width:60px;height:13px;padding:1px;margin:0;pointer-events:none}input.vis-network-configuration.range{-webkit-appearance:none;border:0 solid #fff;background-color:transparent;width:300px;height:20px}input.vis-network-configuration.range::-webkit-slider-runnable-track{width:300px;height:5px;background:#dedede;background:-moz-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#dedede),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-o-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:linear-gradient(to bottom,#dedede 0,#c8c8c8 99%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#dedede', endColorstr='#c8c8c8', GradientType=0);border:1px solid #999;box-shadow:#aaa 0 0 3px 0;border-radius:3px}input.vis-network-configuration.range::-webkit-slider-thumb{-webkit-appearance:none;border:1px solid #14334b;height:17px;width:17px;border-radius:50%;background:#3876c2;background:-moz-linear-gradient(top,#3876c2 0,#385380 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#3876c2),color-stop(100%,#385380));background:-webkit-linear-gradient(top,#3876c2 0,#385380 100%);background:-o-linear-gradient(top,#3876c2 0,#385380 100%);background:-ms-linear-gradient(top,#3876c2 0,#385380 100%);background:linear-gradient(to bottom,#3876c2 0,#385380 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#3876c2', endColorstr='#385380', GradientType=0);box-shadow:#111927 0 0 1px 0;margin-top:-7px}input.vis-network-configuration.range:focus{outline:0}input.vis-network-configuration.range:focus::-webkit-slider-runnable-track{background:#9d9d9d;background:-moz-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#9d9d9d),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:-o-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:linear-gradient(to bottom,#9d9d9d 0,#c8c8c8 99%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#9d9d9d', endColorstr='#c8c8c8', GradientType=0)}input.vis-network-configuration.range::-moz-range-track{width:300px;height:10px;background:#dedede;background:-moz-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#dedede),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-o-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:linear-gradient(to bottom,#dedede 0,#c8c8c8 99%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#dedede', endColorstr='#c8c8c8', GradientType=0);border:1px solid #999;box-shadow:#aaa 0 0 3px 0;border-radius:3px}input.vis-network-configuration.range::-moz-range-thumb{border:none;height:16px;width:16px;border-radius:50%;background:#385380}input.vis-network-configuration.range:-moz-focusring{outline:#fff solid 1px;outline-offset:-1px}input.vis-network-configuration.range::-ms-track{width:300px;height:5x;background:0 0;border-color:transparent;border-width:6px 0;color:transparent}input.vis-network-configuration.range::-ms-fill-lower{background:#777;border-radius:10px}input.vis-network-configuration.range::-ms-fill-upper{background:#ddd;border-radius:10px}input.vis-network-configuration.range::-ms-thumb{border:none;height:16px;width:16px;border-radius:50%;background:#385380}input.vis-network-configuration.range:focus::-ms-fill-lower{background:#888}input.vis-network-configuration.range:focus::-ms-fill-upper{background:#ccc}div.vis-network-colorPicker-frame{position:relative;margin-top:40px;width:310px;height:310px;padding:10px;border-radius:20px;background-color:#fff}div.vis-network-colorPicker-color{position:absolute;width:289px;height:289px;cursor:pointer}div.vis-network-colorPicker-brightness{position:absolute;top:300px}div.vis-network-colorPicker-saturation{position:absolute;top:145px;left:160px;-ms-transform:rotate(270deg);-webkit-transform:rotate(270deg);transform:rotate(270deg)}div.vis-network-colorPicker-selector{position:absolute;top:137px;left:137px;width:15px;height:15px;border-radius:15px;background:#4c4c4c;background:-moz-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#4c4c4c),color-stop(12%,#595959),color-stop(25%,#666),color-stop(39%,#474747),color-stop(50%,#2c2c2c),color-stop(51%,#000),color-stop(60%,#111),color-stop(76%,#2b2b2b),color-stop(91%,#1c1c1c),color-stop(100%,#131313));background:-webkit-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:-o-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:-ms-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:linear-gradient(to bottom,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#4c4c4c', endColorstr='#131313', GradientType=0)}input.vis-network-configuration.range.colorPicker{width:290px;height:20px}input.vis-network-brightnessRange,input.vis-network-saturationRange{width:289px!important} \ No newline at end of file +div.network-manipulation-closeDiv,div.network-manipulationUI,div.network-navigation{-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none;-khtml-user-select:none}div.network-manipulation-closeDiv,div.network-manipulationUI{background-repeat:no-repeat;cursor:pointer;-webkit-touch-callout:none}.vis .overlay{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10}.vis-active{box-shadow:0 0 10px #86d5f8}.vis [class*=span]{min-height:0;width:auto}.vis.timeline.root{position:relative;border:1px solid #bfbfbf;overflow:hidden;padding:0;margin:0;box-sizing:border-box}.vis.timeline .vispanel{position:absolute;padding:0;margin:0;box-sizing:border-box}.vis.timeline .vispanel.bottom,.vis.timeline .vispanel.center,.vis.timeline .vispanel.left,.vis.timeline .vispanel.right,.vis.timeline .vispanel.top{border:1px #bfbfbf}.vis.timeline .vispanel.center,.vis.timeline .vispanel.left,.vis.timeline .vispanel.right{border-top-style:solid;border-bottom-style:solid;overflow:hidden}.vis.timeline .vispanel.bottom,.vis.timeline .vispanel.center,.vis.timeline .vispanel.top{border-left-style:solid;border-right-style:solid}.vis.timeline .background{overflow:hidden}.vis.timeline .vispanel>.content{position:relative}.vis.timeline .vispanel .shadow{position:absolute;width:100%;height:1px;box-shadow:0 0 10px rgba(0,0,0,.8)}.vis.timeline .vispanel .shadow.top{top:-1px;left:0}.vis.timeline .vispanel .shadow.bottom{bottom:-1px;left:0}.vis.timeline .labelset{position:relative;overflow:hidden;box-sizing:border-box}.vis.timeline .labelset .vlabel{position:relative;left:0;top:0;width:100%;color:#4d4d4d;box-sizing:border-box;border-bottom:1px solid #bfbfbf}.vis.timeline .labelset .vlabel:last-child{border-bottom:none}.vis.timeline .labelset .vlabel .inner{display:inline-block;padding:5px}.vis.timeline .labelset .vlabel .inner.hidden{padding:0}.vis.timeline .itemset{position:relative;padding:0;margin:0;box-sizing:border-box}.vis.timeline .itemset .background,.vis.timeline .itemset .foreground{position:absolute;width:100%;height:100%;overflow:visible}.vis.timeline .axis{position:absolute;width:100%;height:0;left:0;z-index:1}.vis.timeline .foreground .group{position:relative;box-sizing:border-box;border-bottom:1px solid #bfbfbf}.vis.timeline .foreground .group:last-child{border-bottom:none}.vis.timeline .item{position:absolute;color:#1A1A1A;border-color:#97B0F8;border-width:1px;background-color:#D5DDF6;display:inline-block;overflow:hidden}.vis.timeline .item.selected{border-color:#FFC200;background-color:#FFF785;z-index:2}.vis.timeline .editable .item.selected{cursor:move}.vis.timeline .item.point.selected{background-color:#FFF785}.vis.timeline .item.box{text-align:center;border-style:solid;border-radius:2px}.vis.timeline .item.point{background:0 0}.vis.timeline .item.dot{position:absolute;padding:0;border-width:4px;border-style:solid;border-radius:4px}.vis.timeline .item.range{border-style:solid;border-radius:2px;box-sizing:border-box}.vis.timeline .item.background{border:none;background-color:rgba(213,221,246,.4);box-sizing:border-box;padding:0;margin:0}.vis.timeline .item.range .content{position:relative;display:inline-block;max-width:100%}.vis.timeline .item.background .content{position:absolute;display:inline-block;max-width:100%;margin:5px}.vis.timeline .item.line{padding:0;position:absolute;width:0;border-left-width:1px;border-left-style:solid}.vis.timeline .item .content{white-space:nowrap;box-sizing:border-box;padding:5px}.vis.timeline .item .delete{background:url(img/timeline/delete.png)top center no-repeat;position:absolute;width:24px;height:24px;top:0;right:-24px;cursor:pointer}.vis.timeline .item.range .drag-left{position:absolute;width:24px;height:100%;top:0;left:-4px;cursor:w-resize}.vis.timeline .item.range .drag-right{position:absolute;width:24px;height:100%;top:0;right:-4px;cursor:e-resize}.vis.timeline .timeaxis{position:relative;overflow:hidden}.vis.timeline .timeaxis.foreground{top:0;left:0;width:100%}.vis.timeline .timeaxis.background{position:absolute;top:0;left:0;width:100%;height:100%}.vis.timeline .timeaxis .text{position:absolute;color:#4d4d4d;padding:3px;white-space:nowrap}.vis.timeline .timeaxis .text.measure{position:absolute;padding-left:0;padding-right:0;margin-left:0;margin-right:0;visibility:hidden}.vis.timeline .timeaxis .grid.vertical{position:absolute;border-left:1px solid}.vis.timeline .timeaxis .grid.minor{border-color:#e5e5e5}.vis.timeline .timeaxis .grid.major{border-color:#bfbfbf}.vis.timeline .currenttime{background-color:#FF7F6E;width:2px;z-index:1}.vis.timeline .customtime{background-color:#6E94FF;width:2px;cursor:move;z-index:1}.vis.timeline .vispanel.background.horizontal .grid.horizontal{position:absolute;width:100%;height:0;border-bottom:1px solid}.vis.timeline .vispanel.background.horizontal .grid.minor{border-color:#e5e5e5}.vis.timeline .vispanel.background.horizontal .grid.major{border-color:#bfbfbf}.vis.timeline .dataaxis .yAxis.major{width:100%;position:absolute;color:#4d4d4d;white-space:nowrap}.vis.timeline .dataaxis .yAxis.major.measure{padding:0;margin:0;border:0;visibility:hidden;width:auto}.vis.timeline .dataaxis .yAxis.minor{position:absolute;width:100%;color:#bebebe;white-space:nowrap}.vis.timeline .dataaxis .yAxis.minor.measure{padding:0;margin:0;border:0;visibility:hidden;width:auto}.vis.timeline .dataaxis .yAxis.title{position:absolute;color:#4d4d4d;white-space:nowrap;bottom:20px;text-align:center}.vis.timeline .dataaxis .yAxis.title.measure{padding:0;margin:0;visibility:hidden;width:auto}.vis.timeline .dataaxis .yAxis.title.left{bottom:0;-webkit-transform-origin:left top;-moz-transform-origin:left top;-ms-transform-origin:left top;-o-transform-origin:left top;transform-origin:left bottom;-webkit-transform:rotate(-90deg);-moz-transform:rotate(-90deg);-ms-transform:rotate(-90deg);-o-transform:rotate(-90deg);transform:rotate(-90deg)}.vis.timeline .dataaxis .yAxis.title.right{bottom:0;-webkit-transform-origin:right bottom;-moz-transform-origin:right bottom;-ms-transform-origin:right bottom;-o-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.vis.timeline .legend{background-color:rgba(247,252,255,.65);padding:5px;border-color:#b3b3b3;border-style:solid;border-width:1px;box-shadow:2px 2px 10px rgba(154,154,154,.55)}.vis.timeline .legendText{white-space:nowrap;display:inline-block}.vis.timeline .graphGroup0{fill:#4f81bd;fill-opacity:0;stroke-width:2px;stroke:#4f81bd}.vis.timeline .graphGroup1{fill:#f79646;fill-opacity:0;stroke-width:2px;stroke:#f79646}.vis.timeline .graphGroup2{fill:#8c51cf;fill-opacity:0;stroke-width:2px;stroke:#8c51cf}.vis.timeline .graphGroup3{fill:#75c841;fill-opacity:0;stroke-width:2px;stroke:#75c841}.vis.timeline .graphGroup4{fill:#ff0100;fill-opacity:0;stroke-width:2px;stroke:#ff0100}.vis.timeline .graphGroup5{fill:#37d8e6;fill-opacity:0;stroke-width:2px;stroke:#37d8e6}.vis.timeline .graphGroup6{fill:#042662;fill-opacity:0;stroke-width:2px;stroke:#042662}.vis.timeline .graphGroup7{fill:#00ff26;fill-opacity:0;stroke-width:2px;stroke:#00ff26}.vis.timeline .graphGroup8{fill:#f0f;fill-opacity:0;stroke-width:2px;stroke:#f0f}.vis.timeline .graphGroup9{fill:#8f3938;fill-opacity:0;stroke-width:2px;stroke:#8f3938}.vis.timeline .fill{fill-opacity:.1;stroke:none}.vis.timeline .bar{fill-opacity:.5;stroke-width:1px}.vis.timeline .point{stroke-width:2px;fill-opacity:1}.vis.timeline .legendBackground{stroke-width:1px;fill-opacity:.9;fill:#fff;stroke:#c2c2c2}.vis.timeline .outline{stroke-width:1px;fill-opacity:1;fill:#fff;stroke:#e5e5e5}.vis.timeline .iconFill{fill-opacity:.3;stroke:none}div.network-manipulationDiv{border-width:0;border-bottom:1px;border-style:solid;border-color:#d6d9d8;background:#fff;background:-moz-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fff),color-stop(48%,#fcfcfc),color-stop(50%,#fafafa),color-stop(100%,#fcfcfc));background:-webkit-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-o-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-ms-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:linear-gradient(to bottom,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#fcfcfc', GradientType=0);position:absolute;left:0;top:0;width:100%;height:30px}div.network-manipulation-editMode{position:absolute;left:0;top:15px;height:30px}div.network-manipulation-closeDiv{position:absolute;right:0;top:0;width:30px;height:30px;background-position:20px 3px;background-image:url(img/network/cross.png);user-select:none}div.network-manipulation-closeDiv:hover{opacity:.6}div.network-manipulationUI{position:relative;top:-7px;font-family:verdana;font-size:12px;-moz-border-radius:15px;border-radius:15px;display:inline-block;background-position:0 0;height:24px;margin:0 0 0 10px;vertical-align:middle;padding:0 8px;user-select:none}div.network-manipulationUI:hover{box-shadow:1px 1px 8px rgba(0,0,0,.2)}div.network-manipulationUI:active{box-shadow:1px 1px 8px rgba(0,0,0,.5)}div.network-manipulationUI.back{background-image:url(img/network/backIcon.png)}div.network-manipulationUI.none:hover{box-shadow:1px 1px 8px transparent;cursor:default}div.network-manipulationUI.none:active{box-shadow:1px 1px 8px transparent}div.network-manipulationUI.none{padding:0}div.network-manipulationUI.notification{margin:2px;font-weight:700}div.network-manipulationUI.add{background-image:url(img/network/addNodeIcon.png)}div.network-manipulationUI.edit{background-image:url(img/network/editIcon.png)}div.network-manipulationUI.edit.editmode{background-color:#fcfcfc;border-style:solid;border-width:1px;border-color:#ccc}div.network-manipulationUI.connect{background-image:url(img/network/connectIcon.png)}div.network-manipulationUI.delete{background-image:url(img/network/deleteIcon.png)}div.network-manipulationLabel{margin:0 0 0 23px;line-height:25px}div.network-seperatorLine{display:inline-block;width:1px;height:20px;background-color:#bdbdbd;margin:5px 7px 0 15px}div.network-navigation_wrapper{position:absolute;left:0;top:0;width:100%;height:100%}div.network-navigation{width:34px;height:34px;-moz-border-radius:17px;border-radius:17px;position:absolute;display:inline-block;background-position:2px 2px;background-repeat:no-repeat;cursor:pointer;-webkit-touch-callout:none;user-select:none}div.network-navigation:hover{box-shadow:0 0 3px 3px rgba(56,207,21,.3)}div.network-navigation:active{box-shadow:0 0 1px 3px rgba(56,207,21,.95)}div.network-navigation.up{background-image:url(img/network/upArrow.png);bottom:50px;left:55px}div.network-navigation.down{background-image:url(img/network/downArrow.png);bottom:10px;left:55px}div.network-navigation.left{background-image:url(img/network/leftArrow.png);bottom:10px;left:15px}div.network-navigation.right{background-image:url(img/network/rightArrow.png);bottom:10px;left:95px}div.network-navigation.zoomIn{background-image:url(img/network/plus.png);bottom:10px;right:15px}div.network-navigation.zoomOut{background-image:url(img/network/minus.png);bottom:10px;right:55px}div.network-navigation.zoomExtends{background-image:url(img/network/zoomExtends.png);bottom:50px;right:15px}div.network-tooltip{position:absolute;visibility:hidden;padding:5px;white-space:nowrap;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;border:1px solid;box-shadow:3px 3px 10px rgba(128,128,128,.5)}div.vis-network-configuration{position:relative;display:block;float:left;font-size:12px}div.vis-network-configuration.entree{display:block;width:495px;height:25px;vertical-align:middle;line-height:25px}div.vis-network-configuration.entree.s2{left:10px;background-color:#f7f8fa;padding-left:5px;border-radius:3px}div.vis-network-configuration.entree.s3{left:20px;background-color:#e4e9f0;padding-left:5px;border-radius:3px}div.vis-network-configuration.entree.s4{left:30px;background-color:#cfd8e6;padding-left:5px;border-radius:3px}div.vis-network-configuration.header{font-size:18px;font-weight:700}div.vis-network-configuration.label{width:120px;height:25px;line-height:25px}div.vis-network-configuration.label.s3{width:110px}div.vis-network-configuration.label.s4{width:100px}div.vis-network-configuration.colorBlock{top:1px;width:30px;height:19px;border:1px solid #444;border-radius:2px;padding:0;margin:0;cursor:pointer}input.vis-network-configuration.checkbox{left:-5px}input.vis-network-configuration.rangeinput{position:relative;top:-5px;width:60px;height:13px;padding:1px;margin:0;pointer-events:none}input.vis-network-configuration.range{-webkit-appearance:none;border:0 solid #fff;background-color:transparent;width:300px;height:20px}input.vis-network-configuration.range::-webkit-slider-runnable-track{width:300px;height:5px;background:#dedede;background:-moz-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#dedede),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-o-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:linear-gradient(to bottom,#dedede 0,#c8c8c8 99%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#dedede', endColorstr='#c8c8c8', GradientType=0);border:1px solid #999;box-shadow:#aaa 0 0 3px 0;border-radius:3px}input.vis-network-configuration.range::-webkit-slider-thumb{-webkit-appearance:none;border:1px solid #14334b;height:17px;width:17px;border-radius:50%;background:#3876c2;background:-moz-linear-gradient(top,#3876c2 0,#385380 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#3876c2),color-stop(100%,#385380));background:-webkit-linear-gradient(top,#3876c2 0,#385380 100%);background:-o-linear-gradient(top,#3876c2 0,#385380 100%);background:-ms-linear-gradient(top,#3876c2 0,#385380 100%);background:linear-gradient(to bottom,#3876c2 0,#385380 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#3876c2', endColorstr='#385380', GradientType=0);box-shadow:#111927 0 0 1px 0;margin-top:-7px}input.vis-network-configuration.range:focus{outline:0}input.vis-network-configuration.range:focus::-webkit-slider-runnable-track{background:#9d9d9d;background:-moz-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#9d9d9d),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:-o-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:linear-gradient(to bottom,#9d9d9d 0,#c8c8c8 99%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#9d9d9d', endColorstr='#c8c8c8', GradientType=0)}input.vis-network-configuration.range::-moz-range-track{width:300px;height:10px;background:#dedede;background:-moz-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#dedede),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-o-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:linear-gradient(to bottom,#dedede 0,#c8c8c8 99%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#dedede', endColorstr='#c8c8c8', GradientType=0);border:1px solid #999;box-shadow:#aaa 0 0 3px 0;border-radius:3px}input.vis-network-configuration.range::-moz-range-thumb{border:none;height:16px;width:16px;border-radius:50%;background:#385380}input.vis-network-configuration.range:-moz-focusring{outline:#fff solid 1px;outline-offset:-1px}input.vis-network-configuration.range::-ms-track{width:300px;height:5x;background:0 0;border-color:transparent;border-width:6px 0;color:transparent}input.vis-network-configuration.range::-ms-fill-lower{background:#777;border-radius:10px}input.vis-network-configuration.range::-ms-fill-upper{background:#ddd;border-radius:10px}input.vis-network-configuration.range::-ms-thumb{border:none;height:16px;width:16px;border-radius:50%;background:#385380}input.vis-network-configuration.range:focus::-ms-fill-lower{background:#888}input.vis-network-configuration.range:focus::-ms-fill-upper{background:#ccc}div.vis-network-colorPicker-frame{position:absolute;margin-top:-140px;margin-left:30px;width:293px;height:425px;padding:10px;border-radius:15px;background-color:#fff;display:none;box-shadow:rgba(0,0,0,.5)0 0 10px 0}div.vis-network-colorPicker-color{position:absolute;width:289px;height:289px;cursor:pointer}div.vis-network-colorPicker-brightness{position:absolute;top:313px}div.vis-network-colorPicker-opacity{position:absolute;top:350px}div.vis-network-colorPicker-selector{position:absolute;top:137px;left:137px;width:15px;height:15px;border-radius:15px;border:1px solid #fff;background:#4c4c4c;background:-moz-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#4c4c4c),color-stop(12%,#595959),color-stop(25%,#666),color-stop(39%,#474747),color-stop(50%,#2c2c2c),color-stop(51%,#000),color-stop(60%,#111),color-stop(76%,#2b2b2b),color-stop(91%,#1c1c1c),color-stop(100%,#131313));background:-webkit-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:-o-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:-ms-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:linear-gradient(to bottom,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#4c4c4c', endColorstr='#131313', GradientType=0)}div.vis-network-colorPicker-initialColor,div.vis-network-colorPicker-newColor{top:380px;width:140px;height:20px;border-radius:5px;border:1px solid rgba(0,0,0,.1)}div.vis-network-colorPicker-newColor{position:absolute;left:159px;text-align:right;padding-right:2px;font-size:10px;color:rgba(0,0,0,.4);vertical-align:middle;line-height:20px}div.vis-network-colorPicker-initialColor{position:absolute;left:10px;text-align:left;padding-left:2px;font-size:10px;color:rgba(0,0,0,.4);vertical-align:middle;line-height:20px}div.vis-network-colorPicker-label{position:absolute;width:300px;left:10px}div.vis-network-colorPicker-label.brightness{top:300px}div.vis-network-colorPicker-label.opacity{top:338px}div.vis-network-colorPicker-button{position:absolute;width:68px;height:25px;border-radius:10px;vertical-align:middle;text-align:center;line-height:25px;top:410px;border:2px solid #d9d9d9;background-color:#f7f7f7;cursor:pointer}div.vis-network-colorPicker-button.cancel{border:2px solid #ff4e33;background-color:#ff7761;left:5px}div.vis-network-colorPicker-button.load{border:2px solid #a153e6;background-color:#cb8dff;left:82px}div.vis-network-colorPicker-button.apply{border:2px solid #4588e6;background-color:#82b6ff;left:159px}div.vis-network-colorPicker-button.save{border:2px solid #45e655;background-color:#6dff7c;left:236px}input.vis-network-configuration.range.colorPicker{width:290px;height:20px}input.vis-network-brightnessRange,input.vis-network-saturationRange{width:289px!important} \ No newline at end of file diff --git a/examples/network/01_basic_usage.html b/examples/network/01_basic_usage.html index 987cc01e..4d8537bf 100644 --- a/examples/network/01_basic_usage.html +++ b/examples/network/01_basic_usage.html @@ -49,6 +49,7 @@ physics:{solver:'BarnesHut'} } var network = new vis.Network(container, data, options); +// network.setOptions({nodes:{color:'red'}}) diff --git a/lib/network/css/network-colorpicker.css b/lib/network/css/network-colorpicker.css index 60a02f24..15b93034 100644 --- a/lib/network/css/network-colorpicker.css +++ b/lib/network/css/network-colorpicker.css @@ -1,12 +1,15 @@ div.vis-network-colorPicker-frame { - position:relative; - margin-top:40px; - width:310px; - height:310px; + position:absolute; + margin-top:-140px; + margin-left:30px; + width:293px; + height:425px; padding: 10px; - border-radius:20px; + border-radius:15px; background-color:#ffffff; + display:none; + box-shadow: rgba(0,0,0,0.5) 0px 0px 10px 0px; } div.vis-network-colorPicker-color { @@ -16,20 +19,16 @@ div.vis-network-colorPicker-color { cursor: pointer; } + + div.vis-network-colorPicker-brightness { - position:absolute; - top:300px; - /*background-color:#00ff00;*/ + position: absolute; + top:313px; } -div.vis-network-colorPicker-saturation { +div.vis-network-colorPicker-opacity { position:absolute; - top:145px; - left:160px; - /*background-color:#ff0000;*/ - -ms-transform: rotate(270deg); /* IE 9 */ - -webkit-transform: rotate(270deg); /* Chrome, Safari, Opera */ - transform: rotate(270deg); + top:350px; } div.vis-network-colorPicker-selector{ @@ -39,6 +38,7 @@ div.vis-network-colorPicker-selector{ width:15px; height:15px; border-radius:15px; + border:1px solid #ffffff; background: #4c4c4c; /* Old browsers */ background: -moz-linear-gradient(top, #4c4c4c 0%, #595959 12%, #666666 25%, #474747 39%, #2c2c2c 50%, #000000 51%, #111111 60%, #2b2b2b 76%, #1c1c1c 91%, #131313 100%); /* FF3.6+ */ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#4c4c4c), color-stop(12%,#595959), color-stop(25%,#666666), color-stop(39%,#474747), color-stop(50%,#2c2c2c), color-stop(51%,#000000), color-stop(60%,#111111), color-stop(76%,#2b2b2b), color-stop(91%,#1c1c1c), color-stop(100%,#131313)); /* Chrome,Safari4+ */ @@ -49,6 +49,91 @@ div.vis-network-colorPicker-selector{ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#4c4c4c', endColorstr='#131313',GradientType=0 ); /* IE6-9 */ } + + +div.vis-network-colorPicker-newColor { + position:absolute; + width:140px; + height:20px; + border:1px solid rgba(0,0,0,0.1); + border-radius:5px; + top:380px; + left:159px; + text-align:right; + padding-right:2px; + font-size:10px; + color:rgba(0,0,0,0.4); + vertical-align:middle; + line-height:20px; + +} + +div.vis-network-colorPicker-initialColor { + position:absolute; + width:140px; + height:20px; + border:1px solid rgba(0,0,0,0.1); + border-radius:5px; + top:380px; + left:10px; + text-align:left; + padding-left:2px; + font-size:10px; + color:rgba(0,0,0,0.4); + vertical-align:middle; + line-height:20px; +} + +div.vis-network-colorPicker-label { + position:absolute; + width:300px; + left:10px; +} + +div.vis-network-colorPicker-label.brightness { + top:300px; +} + +div.vis-network-colorPicker-label.opacity { + top:338px; +} + +div.vis-network-colorPicker-button { + position:absolute; + width:68px; + height:25px; + border-radius:10px; + vertical-align: middle; + text-align:center; + line-height: 25px; + top:410px; + border:2px solid #d9d9d9; + background-color: #f7f7f7; + cursor:pointer; +} + +div.vis-network-colorPicker-button.cancel { + border:2px solid #ff4e33; + background-color: #ff7761; + left:5px; +} +div.vis-network-colorPicker-button.load { + border:2px solid #a153e6; + background-color: #cb8dff; + left:82px; +} +div.vis-network-colorPicker-button.apply { + border:2px solid #4588e6; + background-color: #82b6ff; + left:159px; +} +div.vis-network-colorPicker-button.save { + border:2px solid #45e655; + background-color: #6dff7c; + left:236px; +} + + input.vis-network-configuration.range.colorPicker{ width: 290px; height:20px; @@ -61,5 +146,4 @@ input.vis-network-brightnessRange { input.vis-network-saturationRange { width: 289px !important; - } \ No newline at end of file diff --git a/lib/network/css/network-configuration.css b/lib/network/css/network-configuration.css index b9d7d9a6..eeb14f6e 100644 --- a/lib/network/css/network-configuration.css +++ b/lib/network/css/network-configuration.css @@ -52,6 +52,7 @@ div.vis-network-configuration.label.s4{ } div.vis-network-configuration.colorBlock{ + top:1px; width:30px; height:19px; border:1px solid #444444; diff --git a/lib/network/modules/CanvasRenderer.js b/lib/network/modules/CanvasRenderer.js index a61eed15..cf71762f 100644 --- a/lib/network/modules/CanvasRenderer.js +++ b/lib/network/modules/CanvasRenderer.js @@ -89,7 +89,7 @@ class CanvasRenderer { * chart will be resized too. */ redraw() { - this.setSize(this.constants.width, this.constants.height); + this._setSize(this.constants.width, this.constants.height); this._redraw(); } diff --git a/lib/network/modules/ConfigurationSystem.js b/lib/network/modules/ConfigurationSystem.js index 4210181e..8c53ddc2 100644 --- a/lib/network/modules/ConfigurationSystem.js +++ b/lib/network/modules/ConfigurationSystem.js @@ -202,16 +202,47 @@ class ConfigurationSystem { } }; - this.actualOptions = {}; + this.actualOptions = { + nodes:{}, + edges:{}, + layout:{}, + interaction:{}, + manipulation:{}, + physics:{}, + selection:{}, + renderer:{}, + configure: false, + configureContainer: undefined + }; this.domElements = []; - this.colorPicker = new ColorPicker(); + this.colorPicker = new ColorPicker(this.network.canvas.pixelRatio); } + + /** + * refresh all options. + * Because all modules parse their options by themselves, we just use their options. We copy them here. + * + * @param options + */ setOptions(options) { if (options !== undefined) { - this._clean(); - util.deepExtend(this.actualOptions, options); + util.extend(this.actualOptions, options); + } + + this._clean(); + + if (this.actualOptions.configure !== undefined && this.actualOptions.configure !== false) { + util.deepExtend(this.actualOptions.nodes, this.network.nodesHandler.options, true); + util.deepExtend(this.actualOptions.edges, this.network.edgesHandler.options, true); + util.deepExtend(this.actualOptions.layout, this.network.layoutEngine.options, true); + util.deepExtend(this.actualOptions.interaction, this.network.interactionHandler.options, true); + util.deepExtend(this.actualOptions.manipulation, this.network.manipulation.options, true); + util.deepExtend(this.actualOptions.physics, this.network.nodesHandler.physics, true); + util.deepExtend(this.actualOptions.selection, this.network.selectionHandler.selection, true); + util.deepExtend(this.actualOptions.renderer, this.network.renderer.selection, true); + if (this.actualOptions.configurationContainer !== undefined) { this.container = this.actualOptions.configurationContainer; } @@ -219,31 +250,28 @@ class ConfigurationSystem { this.container = this.network.body.container; } - if (this.actualOptions.configure !== undefined && this.actualOptions.configure !== false) { - let config; - if (this.actualOptions.configure instanceof Array) { - config = this.actualOptions.configure.join(); - } - else if (typeof this.actualOptions.configure === 'string') { - config = this.actualOptions.configure; - } - else if (typeof this.actualOptions.configure === 'boolean') { - config = this.actualOptions.configure; - } - else { - this._clean(); - throw new Error('the option for configure has to be either a string, boolean or an array. Supplied:' + this.options.configure); - return; - } - this._create(config); - } - + let config; + if (this.actualOptions.configure instanceof Array) { + config = this.actualOptions.configure.join(); + } + else if (typeof this.actualOptions.configure === 'string') { + config = this.actualOptions.configure; + } + else if (typeof this.actualOptions.configure === 'boolean') { + config = this.actualOptions.configure; + } + else { + this._clean(); + throw new Error('the option for configure has to be either a string, boolean or an array. Supplied:' + this.options.configure); + return; + } + this._create(config); } } /** - * + * Create all DOM elements * @param {Boolean | String} config * @private */ @@ -271,15 +299,24 @@ class ConfigurationSystem { } this._push(); - this.colorPicker.generate(); + this.colorPicker.insertTo(this.container); } + + /** + * draw all DOM elements on the screen + * @private + */ _push() { for (var i = 0; i < this.domElements.length; i++) { this.container.appendChild(this.domElements[i]); } } + /** + * delete all DOM elements + * @private + */ _clean() { for (var i = 0; i < this.domElements.length; i++) { this.container.removeChild(this.domElements[i]); @@ -287,6 +324,12 @@ class ConfigurationSystem { this.domElements = []; } + /** + * get the value from the actualOptions if it exists + * @param {array} path | where to look for the actual option + * @returns {*} + * @private + */ _getValue(path) { let base = this.actualOptions; for (let i = 0; i < path.length; i++) { @@ -301,6 +344,14 @@ class ConfigurationSystem { return base; } + + /** + * Copy the path and add a step. It needs to copy because the path will keep stacking otherwise. + * @param path + * @param newValue + * @returns {Array} + * @private + */ _addToPath(path, newValue) { let newPath = []; for (let i = 0; i < path.length; i++) { @@ -310,7 +361,12 @@ class ConfigurationSystem { return newPath; } - + /** + * all option elements are wrapped in an entree + * @param path + * @param domElements + * @private + */ _makeEntree(path,...domElements) { let entree = document.createElement('div'); entree.className = 'vis-network-configuration entree s' + path.length; @@ -320,6 +376,11 @@ class ConfigurationSystem { this.domElements.push(entree); } + /** + * header for major subjects + * @param name + * @private + */ _makeHeader(name) { let div = document.createElement('div'); div.className = 'vis-network-configuration header'; @@ -327,6 +388,14 @@ class ConfigurationSystem { this._makeEntree([],div); } + /** + * make a label, if it is an object label, it gets different styling. + * @param name + * @param path + * @param objectLabel + * @returns {HTMLElement} + * @private + */ _makeLabel(name, path, objectLabel = false) { let div = document.createElement('div'); div.className = 'vis-network-configuration label s' + path.length; @@ -339,6 +408,14 @@ class ConfigurationSystem { return div; } + + /** + * make a dropdown list for multiple possible string optoins + * @param arr + * @param value + * @param path + * @private + */ _makeDropdown(arr, value, path) { let select = document.createElement('select'); select.className = 'vis-network-configuration select'; @@ -366,6 +443,14 @@ class ConfigurationSystem { this._makeEntree(path, label, select); } + + /** + * make a range object for numeric options + * @param arr + * @param value + * @param path + * @private + */ _makeRange(arr, value, path) { let defaultValue = arr[0]; let min = arr[1]; @@ -402,6 +487,14 @@ class ConfigurationSystem { this._makeEntree(path, label, range, input); } + + /** + * make a checkbox for boolean options. + * @param defaultValue + * @param value + * @param path + * @private + */ _makeCheckbox(defaultValue, value, path) { var checkbox = document.createElement('input'); checkbox.type = 'checkbox'; @@ -418,31 +511,60 @@ class ConfigurationSystem { this._makeEntree(path, label, checkbox); } + + /** + * make a color field with a color picker for color fields + * @param arr + * @param value + * @param path + * @private + */ _makeColorField(arr, value, path) { let defaultColor = arr[1]; let div = document.createElement('div'); + value = value === undefined ? defaultColor : value; - if (defaultColor !== 'none') { + if (value !== 'none') { div.className = 'vis-network-configuration colorBlock'; - div.style.backgroundColor = defaultColor; + div.style.backgroundColor = value; } else { div.className = 'vis-network-configuration colorBlock none'; } value = value === undefined ? defaultColor : value; - div.onclick = () => {this._showColorPicker(value, div);} + div.onclick = (event) => {this._showColorPicker(event,value,div,path);} let label = this._makeLabel(path[path.length-1], path); this._makeEntree(path,label, div); } - _showColorPicker(value, div) { - this.colorPicker.show(div); - } + /** + * used by the color buttons to call the color picker. + * @param event + * @param value + * @param div + * @param path + * @private + */ + _showColorPicker(event, value, div, path) { + this.colorPicker.show(event.pageX, event.pageY); + this.colorPicker.setColor(value); + this.colorPicker.setCallback((color) => { + let colorString = 'rgba(' + color.r + ',' + color.g + ',' + color.b + ',' + color.a + ')'; + div.style.backgroundColor = colorString; + this._update(colorString,path); + }) + } + /** + * parse an object and draw the correct entrees + * @param obj + * @param path + * @private + */ _handleObject(obj, path = []) { for (let subObj in obj) { if (obj.hasOwnProperty(subObj)) { @@ -471,6 +593,15 @@ class ConfigurationSystem { } } + + /** + * handle the array type of option + * @param optionName + * @param arr + * @param value + * @param path + * @private + */ _handleArray(optionName, arr, value, path) { if (typeof arr[0] === 'string' && arr[0] === 'color') { this._makeColorField(arr, value, path); @@ -483,6 +614,16 @@ class ConfigurationSystem { } } + + /** + * handle the string type of option. + * TODO: Not sure what to do with this + * @param optionName + * @param string + * @param value + * @param path + * @private + */ _handleString(optionName, string, value, path) { if (string === 'string') { @@ -493,6 +634,13 @@ class ConfigurationSystem { } } + + /** + * called to update the network with the new settings. + * @param value + * @param path + * @private + */ _update(value, path) { let options = {}; let pointer = options; @@ -505,6 +653,7 @@ class ConfigurationSystem { pointer[path[i]] = value; } } + console.log('options',options) this.network.setOptions(options); } } diff --git a/lib/network/modules/NodesHandler.js b/lib/network/modules/NodesHandler.js index 36b715bd..a14c7e46 100644 --- a/lib/network/modules/NodesHandler.js +++ b/lib/network/modules/NodesHandler.js @@ -101,7 +101,13 @@ class NodesHandler { if (options) { util.selectiveNotDeepExtend(['color'], this.options, options); if (options.color) { - this.options.color = util.parseColor(options.color); + let parsedColor = util.parseColor(options.color); + if (parsedColor.border !== undefined) {this.options.color.border = parsedColor.border;} + if (parsedColor.background !== undefined) {this.options.color.background = parsedColor.background;} + if (parsedColor.highlight.border !== undefined) {this.options.color.highlight.border = parsedColor.highlight.border;} + if (parsedColor.highlight.background !== undefined) {this.options.color.highlight.background = parsedColor.highlight.background;} + if (parsedColor.hover.border !== undefined) {this.options.color.hover.border = parsedColor.hover.border;} + if (parsedColor.hover.background !== undefined) {this.options.color.hover.background = parsedColor.hover.background;} } } } diff --git a/lib/network/modules/components/colorPicker.js b/lib/network/modules/components/colorPicker.js index 487ea8e2..a866a200 100644 --- a/lib/network/modules/components/colorPicker.js +++ b/lib/network/modules/components/colorPicker.js @@ -7,16 +7,287 @@ let hammerUtil = require('../../../hammerUtil'); let util = require('../../../util'); class ColorPicker { - constructor() { - this.touchTime = 0; - this.pixelRatio = 1; + constructor(pixelRatio = 1) { + this.pixelRatio = pixelRatio; this.generated = false; - this.color = undefined; + this.centerCoordinates = {x:289/2, y:289/2}; + this.r = 289 * 0.49; + this.color = {r:255,g:255,b:255,a:1.0}; + this.hueCircle = undefined; + this.initialColor = {r:255,g:255,b:255,a:1.0}; + this.previousColor= undefined; + this.applied = false; + + // bound by + this.updateCallback = () => {}; + + // create all DOM elements + this._create(); + } + + + /** + * this inserts the colorPicker into a div from the DOM + * @param container + */ + insertTo(container) { + if (this.hammer !== undefined) { + this.hammer.destroy(); + this.hammer = undefined; + } + this.container = container; + this.container.appendChild(this.frame); + this._bindHammer(); + + this._setSize(); + } + + /** + * the callback is executed on apply and save. Bind it to the application + * @param callback + */ + setCallback(callback) { + if (typeof callback === 'function') { + this.updateCallback = callback; + } + else { + throw new Error("Function attempted to set as colorPicker callback is not a function."); + } + } + + + /** + * Set the color of the colorPicker + * Supported formats: + * '#ffffff' --> hex string + * 'rbg(255,255,255)' --> rgb string + * 'rgba(255,255,255,1.0)' --> rgba string + * {r:255,g:255,b:255} --> rgb object + * {r:255,g:255,b:255,a:1.0} --> rgba object + * @param color + * @param setInitial + */ + setColor(color, setInitial = true) { + if (color === 'none') { + return; + } + + let rgba; + + // check format + if (util.isString(color) === true) { + if (util.isValidRGB(color) === true) { + let rgbaArray = color.substr(4).substr(0, color.length - 5).split(','); + rgba = {r:rgbaArray[0], g:rgbaArray[1], b:rgbaArray[2], a:1.0}; + } + else if (util.isValidRGBA(color) === true) { + let rgbaArray = color.substr(5).substr(0, color.length - 6).split(','); + rgba = {r:rgbaArray[0], g:rgbaArray[1], b:rgbaArray[2], a:rgbaArray[3]}; + } + else if (util.isValidHex(color) === true) { + let rgbObj = util.hexToRGB(color); + rgba = {r:rgbObj.r, g:rgbObj.g, b:rgbObj.b, a:1.0}; + } + } + else { + if (color instanceof Object) { + if (color.r !== undefined && color.g !== undefined && color.b !== undefined) { + let alpha = color.a !== undefined ? color.a : '1.0'; + rgba = {r:color.r, g:color.g, b:color.b, a:alpha}; + } + } + } + + // set color + if (rgba === undefined) { + throw new Error("Unknown color passed to the colorPicker. Supported are strings: rgb, hex, rgba. Object: rgb ({r:r,g:g,b:b,[a:a]}). Supplied: " + JSON.stringify(color)); + } + else { + this._setColor(rgba, setInitial); + } + } - this.create(); + + /** + * this shows the color picker at a location. The hue circle is constructed once and stored. + * @param x + * @param y + */ + show(x,y) { + this.applied = false; + this.frame.style.display = 'block'; + this.frame.style.top = y + 'px'; + this.frame.style.left = x + 'px'; + this._generateHueCircle(); + } + + + // ------------------------------------------ PRIVATE ----------------------------- // + + /** + * Hide the picker. Is called by the cancel button. + * Optional boolean to store the previous color for easy access later on. + * @param storePrevious + * @private + */ + _hide(storePrevious = true) { + // store the previous color for next time; + if (storePrevious === true) { + this.previousColor = util.extend({}, this.color); + } + + if (this.applied === true) { + this.updateCallback(this.initialColor); + } + + this.frame.style.display = 'none'; + } + + + /** + * bound to the save button. Saves and hides. + * @private + */ + _save() { + this.updateCallback(this.color); + this.applied = false; + this.hide(); } - create() { + + /** + * Bound to apply button. Saves but does not close. Is undone by the cancel button. + * @private + */ + _apply() { + this.applied = true; + this.updateCallback(this.color); + this._updatePicker(this.color); + } + + + /** + * load the color from the previous session. + * @private + */ + _loadLast() { + if (this.previousColor !== undefined) { + this.setColor(this.previousColor, false); + } + else { + alert("There is no last color to load..."); + } + } + + + /** + * set the color, place the picker + * @param rgba + * @param setInitial + * @private + */ + _setColor(rgba, setInitial = true) { + // store the initial color + if (setInitial === true) { + console.log("here") + this.initialColor = util.extend({}, rgba); + } + + this.color = rgba; + let hsv = util.RGBToHSV(rgba.r, rgba.g, rgba.b); + + let angleConvert = 2 * Math.PI; + let radius = this.r * hsv.s; + let x = this.centerCoordinates.x + radius * Math.sin(angleConvert * hsv.h); + let y = this.centerCoordinates.y + radius * Math.cos(angleConvert * hsv.h); + + this.colorPickerSelector.style.left = x - 0.5 * this.colorPickerSelector.clientWidth + 'px'; + this.colorPickerSelector.style.top = y - 0.5 * this.colorPickerSelector.clientHeight + 'px'; + + this._updatePicker(rgba); + } + + + /** + * bound to opacity control + * @param value + * @private + */ + _setOpacity(value) { + this.color.a = value / 100; + this._updatePicker(this.color); + } + + + /** + * bound to brightness control + * @param value + * @private + */ + _setBrightness(value) { + let hsv = util.RGBToHSV(this.color.r, this.color.g, this.color.b); + hsv.v = value / 100; + let rgba = util.HSVToRGB(hsv.h, hsv.s, hsv.v); + rgba['a'] = this.color.a; + this.color = rgba; + this._updatePicker(); + } + + + /** + * update the colorpicker. A black circle overlays the hue circle to mimic the brightness decreasing. + * @param rgba + * @private + */ + _updatePicker(rgba = this.color) { + let hsv = util.RGBToHSV(rgba.r, rgba.g, rgba.b); + let ctx = this.colorPickerCanvas.getContext('2d'); + if (this.pixelRation === undefined) { + this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || + ctx.mozBackingStorePixelRatio || + ctx.msBackingStorePixelRatio || + ctx.oBackingStorePixelRatio || + ctx.backingStorePixelRatio || 1); + } + ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); + + // clear the canvas + let w = this.colorPickerCanvas.clientWidth; + let h = this.colorPickerCanvas.clientHeight; + ctx.clearRect(0, 0, w, h); + + ctx.putImageData(this.hueCircle, 0,0); + ctx.fillStyle = 'rgba(0,0,0,' + (1- hsv.v) + ')'; + ctx.circle(this.centerCoordinates.x, this.centerCoordinates.y, this.r); + ctx.fill(); + + this.brightnessRange.value = 100 * hsv.v; + this.opacityRange.value = 100 * rgba.a; + + this.initialColorDiv.style.backgroundColor = 'rgba(' + this.initialColor.r + ',' + this.initialColor.g + ',' + this.initialColor.b + ',' + this.initialColor.a + ')'; + this.newColorDiv.style.backgroundColor = 'rgba(' + this.color.r + ',' + this.color.g + ',' + this.color.b + ',' + this.color.a + ')'; + } + + + /** + * used by create to set the size of the canvas. + * @private + */ + _setSize() { + this.colorPickerCanvas.style.width = '100%'; + this.colorPickerCanvas.style.height = '100%'; + + this.colorPickerCanvas.width = 289 * this.pixelRatio; + this.colorPickerCanvas.height = 289 * this.pixelRatio; + } + + + /** + * create all dom elements + * TODO: cleanup, lots of similar dom elements + * @private + */ + _create() { let visPrefix = 'vis-network-' this.frame = document.createElement('div'); @@ -51,55 +322,109 @@ class ColorPicker { this.colorPickerDiv.className = visPrefix + 'colorPicker-color'; + this.opacityDiv = document.createElement('div'); + this.opacityDiv.className = visPrefix + 'colorPicker-opacity'; + this.brightnessDiv = document.createElement('div'); this.brightnessDiv.className = visPrefix + 'colorPicker-brightness'; - this.saturationDiv = document.createElement('div'); - this.saturationDiv.className = visPrefix + 'colorPicker-saturation'; + this.opacityRange = document.createElement('input'); + this.opacityRange.type = 'range'; + this.opacityRange.min = '0'; + this.opacityRange.max = '100'; + this.opacityRange.value = '100'; + this.opacityRange.className = visPrefix + 'configuration range colorPicker'; this.brightnessRange = document.createElement('input'); this.brightnessRange.type = 'range'; this.brightnessRange.min = '0'; this.brightnessRange.max = '100'; this.brightnessRange.value = '100'; - this.brightnessRange.className = 'vis-network-configuration range colorPicker'; - - this.saturationRange = document.createElement('input'); - this.saturationRange.type = 'range'; - this.saturationRange.min = '0'; - this.saturationRange.max = '100'; - this.saturationRange.value = '100'; - this.saturationRange.className = 'vis-network-configuration range colorPicker'; + this.brightnessRange.className = visPrefix + 'configuration range colorPicker'; + this.opacityDiv.appendChild(this.opacityRange); this.brightnessDiv.appendChild(this.brightnessRange); - this.saturationDiv.appendChild(this.saturationRange); + + var me = this; + this.opacityRange.onchange = function () {me._setOpacity(this.value);} + this.opacityRange.oninput = function () {me._setOpacity(this.value);} + this.brightnessRange.onchange = function () {me._setBrightness(this.value);} + this.brightnessRange.oninput = function () {me._setBrightness(this.value);} + + this.brightnessLabel = document.createElement("div"); + this.brightnessLabel.className = visPrefix + "colorPicker-label brightness"; + this.brightnessLabel.innerHTML = 'brightness:'; + + this.opacityLabel = document.createElement("div"); + this.opacityLabel.className = visPrefix + "colorPicker-label opacity"; + this.opacityLabel.innerHTML = 'opacity:'; + + this.newColorDiv = document.createElement("div"); + this.newColorDiv.className = visPrefix + "colorPicker-newColor"; + this.newColorDiv.innerHTML = 'new'; + + this.initialColorDiv = document.createElement("div"); + this.initialColorDiv.className = visPrefix + "colorPicker-initialColor"; + this.initialColorDiv.innerHTML = 'initial'; + + this.cancelButton = document.createElement("div"); + this.cancelButton.className = visPrefix + "colorPicker-button cancel"; + this.cancelButton.innerHTML = 'cancel'; + this.cancelButton.onclick = this._hide.bind(this, false); + + this.applyButton = document.createElement("div"); + this.applyButton.className = visPrefix + "colorPicker-button apply"; + this.applyButton.innerHTML = 'apply'; + this.applyButton.onclick = this._apply.bind(this); + + this.saveButton = document.createElement("div"); + this.saveButton.className = visPrefix + "colorPicker-button save"; + this.saveButton.innerHTML = 'save'; + this.saveButton.onclick = this._save.bind(this); + + this.loadButton = document.createElement("div"); + this.loadButton.className = visPrefix + "colorPicker-button load"; + this.loadButton.innerHTML = 'load last'; + this.loadButton.onclick = this._loadLast.bind(this); this.frame.appendChild(this.colorPickerDiv); - this.frame.appendChild(this.saturationDiv); + this.frame.appendChild(this.brightnessLabel); this.frame.appendChild(this.brightnessDiv); + this.frame.appendChild(this.opacityLabel); + this.frame.appendChild(this.opacityDiv); + this.frame.appendChild(this.newColorDiv); + this.frame.appendChild(this.initialColorDiv); + + this.frame.appendChild(this.cancelButton); + this.frame.appendChild(this.applyButton); + this.frame.appendChild(this.saveButton); + this.frame.appendChild(this.loadButton); } - show(container) { - this.container = container; - this.container.appendChild(this.frame); - this.bindHammer(); - this.setSize(); - } + /** + * bind hammer to the color picker + * @private + */ + _bindHammer() { + this.drag = {}; + this.pinch = {}; + this.hammer = new Hammer(this.colorPickerCanvas); + this.hammer.get('pinch').set({enable: true}); - setColor(color) { - //todo make + hammerUtil.onTouch(this.hammer, (event) => {this._moveSelector(event)}); + this.hammer.on('tap', (event) => {this._moveSelector(event)}); + this.hammer.on('panstart', (event) => {this._moveSelector(event)}); + this.hammer.on('panmove', (event) => {this._moveSelector(event)}); + this.hammer.on('panend', (event) => {this._moveSelector(event)}); } - setSize() { - this.colorPickerCanvas.style.width = '100%'; - this.colorPickerCanvas.style.height = '100%'; - - this.colorPickerCanvas.width = this.colorPickerDiv.clientWidth * this.pixelRatio; - this.colorPickerCanvas.height = this.colorPickerDiv.clientHeight * this.pixelRatio; - } - generate() { + /** + * generate the hue circle. This is relatively heavy (200ms) and is done only once on the first time it is shown. + * @private + */ + _generateHueCircle() { if (this.generated === false) { let ctx = this.colorPickerCanvas.getContext('2d'); if (this.pixelRation === undefined) { @@ -116,44 +441,41 @@ class ColorPicker { let h = this.colorPickerCanvas.clientHeight; ctx.clearRect(0, 0, w, h); + + // draw hue circle let x, y, hue, sat; - let center = {x: w * 0.5, y: h * 0.5}; - let r = 0.49 * w; + this.centerCoordinates = {x: w * 0.5, y: h * 0.5}; + this.r = 0.49 * w; let angleConvert = (2 * Math.PI) / 360; let hfac = 1 / 360; - let sfac = 1 / r; + let sfac = 1 / this.r; let rgb; for (hue = 0; hue < 360; hue++) { - for (sat = 0; sat < r; sat++) { - x = center.x + sat * Math.sin(angleConvert * hue); - y = center.y + sat * Math.cos(angleConvert * hue); + for (sat = 0; sat < this.r; sat++) { + x = this.centerCoordinates.x + sat * Math.sin(angleConvert * hue); + y = this.centerCoordinates.y + sat * Math.cos(angleConvert * hue); rgb = util.HSVToRGB(hue * hfac, sat * sfac, 1); ctx.fillStyle = 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')'; ctx.fillRect(x - 0.5, y - 0.5, 2, 2); } } + ctx.strokeStyle = 'rgba(0,0,0,1)'; + ctx.circle(this.centerCoordinates.x, this.centerCoordinates.y, this.r); + ctx.stroke(); + + this.hueCircle = ctx.getImageData(0,0,w,h); } this.generated = true; } - bindHammer() { - this.drag = {}; - this.pinch = {}; - this.hammer = new Hammer(this.colorPickerCanvas); - this.hammer.get('pinch').set({enable: true}); - - hammerUtil.onTouch(this.hammer, (event) => {this.moveSelector(event)}); - this.hammer.on('tap', (event) => {this.moveSelector(event)}); - //this.hammer.on('doubletap', (event) => {this.moveSelector(event)}); - //this.hammer.on('press', (event) => {this.moveSelector(event)}); - this.hammer.on('panstart', (event) => {this.moveSelector(event)}); - this.hammer.on('panmove', (event) => {this.moveSelector(event)}); - this.hammer.on('panend', (event) => {this.moveSelector(event)}); - //this.hammer.on('pinch', (event) => {this.moveSelector(event)}); - } - - moveSelector(event) { + /** + * move the selector. This is called by hammer functions. + * + * @param event + * @private + */ + _moveSelector(event) { let rect = this.colorPickerDiv.getBoundingClientRect(); let left = event.center.x - rect.left; let top = event.center.y - rect.top; @@ -164,29 +486,29 @@ class ColorPicker { let x = left - centerX; let y = top - centerY; - let angle = Math.atan(y / x); - if (x < 0) { - angle += Math.PI; - } + let angle = Math.atan2(x,y); let radius = 0.98 * Math.min(Math.sqrt(x * x + y * y), centerX); - let newTop = Math.sin(angle) * radius + centerY; - let newLeft = Math.cos(angle) * radius + centerX; + let newTop = Math.cos(angle) * radius + centerY; + let newLeft = Math.sin(angle) * radius + centerX; this.colorPickerSelector.style.top = newTop - 0.5 * this.colorPickerSelector.clientHeight + 'px'; this.colorPickerSelector.style.left = newLeft - 0.5 * this.colorPickerSelector.clientWidth + 'px'; - - } - - - redraw(roomController) { - if (this.frame === undefined) { - this._create(); - } - let pos = roomController.canvasToDOM({x: 0, y: 0}); - this.frame.style.top = '50px'; - this.frame.style.left = pos.x - 350 + 'px'; + // set color + let h = angle / (2 * Math.PI); + h = h < 0 ? h + 1 : h; + let s = radius / this.r; + let hsv = util.RGBToHSV(this.color.r, this.color.g, this.color.b); + hsv.h = h; + hsv.s = s; + let rgba = util.HSVToRGB(hsv.h, hsv.s, hsv.v); + rgba['a'] = this.color.a; + this.color = rgba; + + // update previews + this.initialColorDiv.style.backgroundColor = 'rgba(' + this.initialColor.r + ',' + this.initialColor.g + ',' + this.initialColor.b + ',' + this.initialColor.a + ')'; + this.newColorDiv.style.backgroundColor = 'rgba(' + this.color.r + ',' + this.color.g + ',' + this.color.b + ',' + this.color.a + ')'; } } diff --git a/lib/util.js b/lib/util.js index a568cba1..9d229e06 100644 --- a/lib/util.js +++ b/lib/util.js @@ -903,8 +903,8 @@ exports.parseColor = function(color) { } else { c = {}; - c.background = color.background || 'white'; - c.border = color.border || c.background; + c.background = color.background || undefined; + c.border = color.border || undefined; if (exports.isString(color.highlight)) { c.highlight = { @@ -914,8 +914,8 @@ exports.parseColor = function(color) { } else { c.highlight = {}; - c.highlight.background = color.highlight && color.highlight.background || c.background; - c.highlight.border = color.highlight && color.highlight.border || c.border; + c.highlight.background = color.highlight && color.highlight.background || undefined; + c.highlight.border = color.highlight && color.highlight.border || undefined; } if (exports.isString(color.hover)) { @@ -926,8 +926,8 @@ exports.parseColor = function(color) { } else { c.hover = {}; - c.hover.background = color.hover && color.hover.background || c.background; - c.hover.border = color.hover && color.hover.border || c.border; + c.hover.background = color.hover && color.hover.background || undefined; + c.hover.border = color.hover && color.hover.border || undefined; } } @@ -1069,6 +1069,11 @@ exports.isValidRGB = function(rgb) { var isOk = /rgb\((\d{1,3}),(\d{1,3}),(\d{1,3})\)/i.test(rgb); return isOk; } +exports.isValidRGBA = function(rgba) { + rgba = rgba.replace(" ",""); + var isOk = /rgba\((\d{1,3}),(\d{1,3}),(\d{1,3}),(.{1,3})\)/i.test(rgba); + return isOk; +} /** * This recursively redirects the prototype of JSON objects to the referenceObject