From f2c0cc2da945f418cc8935868ea4225ec7d02181 Mon Sep 17 00:00:00 2001 From: kcantrel Date: Fri, 28 May 2021 23:07:07 -0700 Subject: [PATCH 01/17] added ability to select min/max of color gradient --- empress/support_files/js/barplot-layer.js | 156 +++------- .../support_files/js/color-options-handler.js | 270 ++++++++++++++++++ empress/support_files/js/colorer.js | 32 ++- empress/support_files/js/empress.js | 25 +- .../support_files/js/side-panel-handler.js | 88 +++--- empress/support_files/js/util.js | 2 +- .../templates/empress-template.html | 3 +- .../support_files/templates/side-panel.html | 24 +- tests/python/make-dev-page.py | 2 +- 9 files changed, 416 insertions(+), 186 deletions(-) create mode 100644 empress/support_files/js/color-options-handler.js diff --git a/empress/support_files/js/barplot-layer.js b/empress/support_files/js/barplot-layer.js index f0c0119e1..be82451e2 100644 --- a/empress/support_files/js/barplot-layer.js +++ b/empress/support_files/js/barplot-layer.js @@ -4,8 +4,9 @@ define([ "spectrum", "Colorer", "Legend", + "ColorOptionsHandler", "util", -], function ($, _, spectrum, Colorer, Legend, util) { +], function ($, _, spectrum, Colorer, Legend, ColorOptionsHandler, util) { /** * * @class BarplotLayer @@ -96,6 +97,9 @@ define([ this.colorByFMColorMap = null; this.colorByFMColorReverse = false; this.colorByFMContinuous = false; + this.colorByFMContinuousScale = false; + this.colorByFMContinuousMin = null; + this.colorByFMContinuousMax = null; this.colorByFMColorMapDiscrete = true; this.defaultLength = BarplotLayer.DEFAULT_LENGTH; this.scaleLengthByFM = false; @@ -295,79 +299,28 @@ define([ colorDetailsDiv.classList.add("indented"); colorDetailsDiv.classList.add("hidden"); - // Add a row for choosing the color map - var colormapP = colorDetailsDiv.appendChild( - document.createElement("p") + var fColorOptions = new ColorOptionsHandler( + colorDetailsDiv, + enableContinuousColoring=true ); - var colormapLbl = colormapP.appendChild( - document.createElement("label") - ); - colormapLbl.innerText = "Color Map"; - var colormapSC = colormapP.appendChild(document.createElement("label")); - colormapSC.classList.add("select-container"); - var colormapSelector = document.createElement("select"); - Colorer.addColorsToSelect(colormapSelector); - colormapSC.appendChild(colormapSelector); - colormapSelector.id = - "barplot-layer-" + this.uniqueNum + "-fm-colormap"; - colormapLbl.setAttribute("for", colormapSelector.id); - - // Add a row for choosing whether the color scale should - // be reversed - var reverseColormapP = colorDetailsDiv.appendChild( - document.createElement("p") - ); - var reverseColormapLbl = reverseColormapP.appendChild( - document.createElement("label") - ); - reverseColormapLbl.innerText = "Reverse Color Map"; - var reverseColormapCheckbox = reverseColormapP.appendChild( - document.createElement("input") - ); - reverseColormapCheckbox.id = - "barplot-layer-" + this.uniqueNum + "-fmcolor-reverse-chk"; - reverseColormapCheckbox.setAttribute("type", "checkbox"); - reverseColormapCheckbox.classList.add("empress-input"); - reverseColormapLbl.setAttribute("for", reverseColormapCheckbox.id); - - // Add a row for choosing the scale type (i.e. whether to use - // continuous coloring or not) - // This mimics Emperor's "Continuous values" checkbox - var continuousValP = colorDetailsDiv.appendChild( - document.createElement("p") - ); - var continuousValLbl = continuousValP.appendChild( - document.createElement("label") - ); - continuousValLbl.innerText = "Continuous values?"; - var continuousValCheckbox = continuousValP.appendChild( - document.createElement("input") - ); - continuousValCheckbox.id = - "barplot-layer-" + this.uniqueNum + "-fmcolor-continuous-chk"; - continuousValCheckbox.setAttribute("type", "checkbox"); - continuousValCheckbox.classList.add("empress-input"); - continuousValLbl.setAttribute("for", continuousValCheckbox.id); - // Hide the "Continuous values?" stuff by default, since the default - // colormap is discrete - continuousValP.classList.add("hidden"); // Initialize defaults to match the UI defaults (e.g. the default // feature metadata field for coloring is the first in the selector) + var colorOptions = fColorOptions.getOptions(); this.colorByFMField = chgColorFMFieldSelector.value; - this.colorByFMColorMap = colormapSelector.value; - this.colorByFMColorReverse = reverseColormapCheckbox.checked; + this.colorByFMColorMap = colorOptions.color; + this.colorByFMColorReverse = colorOptions.reverse; // Alter visibility of the color-changing details when the "Color // by..." checkbox is clicked $(chgColorCheckbox).change(function () { + colorOptions = fColorOptions.getOptions(); if (chgColorCheckbox.checked) { colorDetailsDiv.classList.remove("hidden"); chgColorFMFieldSelector.disabled = false; scope.colorByFM = true; scope.colorByFMField = chgColorFMFieldSelector.value; - scope.colorByFMColorMap = colormapSelector.value; - scope.colorByFMColorReverse = reverseColormapCheckbox.checked; - scope.colorByFMContinuous = continuousValCheckbox.checked; + scope.colorByFMColorMap = colorOptions.color; + scope.colorByFMColorReverse = colorOptions.reverse; // Hide the default color row (since default colors // aren't used when f.m. coloring is enabled) dfltColorP.classList.add("hidden"); @@ -386,25 +339,19 @@ define([ $(chgColorFMFieldSelector).change(function () { scope.colorByFMField = chgColorFMFieldSelector.value; }); - $(colormapSelector).change(function () { - scope.colorByFMColorMap = colormapSelector.value; - // Hide the "Continuous values?" row based on the selected - // colormap's type. This matches how Emperor's ColorViewController - // hides/shows its "Continuous values" elements. - if (Colorer.isColorMapDiscrete(scope.colorByFMColorMap)) { - continuousValP.classList.add("hidden"); - scope.colorByFMColorMapDiscrete = true; - } else { - continuousValP.classList.remove("hidden"); - scope.colorByFMColorMapDiscrete = false; + + // register color options + fColorOptions.registerObserver({ + colorOptionsUpdate: function(options) { + scope.colorByFMColorMap = options.color; + scope.colorByFMColorReverse = options.reverse; + scope.colorByFMColorMapDiscrete = !options.continuousColoring; + scope.colorByFMContinuous = options.continuousColoring; + scope.colorByFMContinuousScale = options.continuousScale; + scope.colorByFMContinuousMin = options.min; + scope.colorByFMContinuousMax = options.max; } - }); - $(reverseColormapCheckbox).change(function () { - scope.colorByFMColorReverse = reverseColormapCheckbox.checked; - }); - $(continuousValCheckbox).change(function () { - scope.colorByFMContinuous = continuousValCheckbox.checked; - }); + }) // create default length settings var dfltLenP = document.createElement("p"); @@ -561,39 +508,7 @@ define([ chgFieldLbl.setAttribute("for", chgFieldSMFieldSelector.id); chgFieldSC.appendChild(chgFieldSMFieldSelector); - // Add a row for choosing the color map - var colormapP = this.smDiv.appendChild(document.createElement("p")); - var colormapLbl = colormapP.appendChild( - document.createElement("label") - ); - colormapLbl.innerText = "Color Map"; - var colormapSC = colormapP.appendChild(document.createElement("label")); - colormapSC.classList.add("select-container"); - var colormapSelector = document.createElement("select"); - Colorer.addColorsToSelect(colormapSelector); - colormapSC.appendChild(colormapSelector); - colormapSelector.id = - "barplot-layer-" + this.uniqueNum + "-sm-colormap"; - colormapLbl.setAttribute("for", colormapSelector.id); - - // Add a row for choosing whether the color scale should - // be reversed - var reverseColormapP = this.smDiv.appendChild( - document.createElement("p") - ); - var reverseColormapLbl = reverseColormapP.appendChild( - document.createElement("label") - ); - reverseColormapLbl.innerText = "Reverse Color Map"; - var reverseColormapCheckbox = reverseColormapP.appendChild( - document.createElement("input") - ); - reverseColormapCheckbox.id = - "barplot-layer-" + this.uniqueNum + "-smcolor-reverse-chk"; - reverseColormapCheckbox.setAttribute("type", "checkbox"); - reverseColormapCheckbox.classList.add("empress-input"); - reverseColormapLbl.setAttribute("for", reverseColormapCheckbox.id); - + var sColorOptions = new ColorOptionsHandler(this.smDiv); var lenP = this.smDiv.appendChild(document.createElement("p")); var lenLbl = lenP.appendChild(document.createElement("label")); lenLbl.innerText = "Length"; @@ -606,18 +521,21 @@ define([ lenLbl.setAttribute("for", lenInput.id); // TODO initialize defaults more sanely + var options = sColorOptions.getOptions(); this.colorBySMField = chgFieldSMFieldSelector.value; - this.colorBySMColorMap = colormapSelector.value; - this.colorBySMColorReverse = reverseColormapCheckbox.checked; + this.colorBySMColorMap = options.color; + this.colorBySMColorReverse = options.reverse; $(chgFieldSMFieldSelector).change(function () { scope.colorBySMField = chgFieldSMFieldSelector.value; }); - $(colormapSelector).change(function () { - scope.colorBySMColorMap = colormapSelector.value; - }); - $(reverseColormapCheckbox).change(function () { - scope.colorBySMColorReverse = reverseColormapCheckbox.checked; + sColorOptions.registerObserver({ + colorOptionsUpdate: () => { + options = sColorOptions.getOptions(); + scope.colorBySMColorMap = options.color; + scope.colorBySMColorReverse = options.reverse; + } }); + $(lenInput).change(function () { scope.lengthSM = util.parseAndValidateNum( lenInput, diff --git a/empress/support_files/js/color-options-handler.js b/empress/support_files/js/color-options-handler.js new file mode 100644 index 000000000..b2dbdc27f --- /dev/null +++ b/empress/support_files/js/color-options-handler.js @@ -0,0 +1,270 @@ +define(["underscore", "Colorer", "util"], function(_, Colorer, util) { + + var TotalColorOptionsHanlders = 0; + + function ColorOptionsHanlder(container, enableContinuousColoring=false) { + this.container = container; + this.observers = []; + this.defaultColor = "discrete-coloring-qiime"; + this.defaultReverseChk = false; + // create unique num + TotalColorOptionsHanlders += 1; + this.uniqueNum = TotalColorOptionsHanlders; + this.enableContinuousColoring = enableContinuousColoring + + // Add a row for choosing the color map + var colormapP = this.container.appendChild( + document.createElement("p") + ); + var colormapLbl = colormapP.appendChild( + document.createElement("label") + ); + colormapLbl.innerText = "Color Map"; + var colormapSC = colormapP.appendChild(document.createElement("label")); + colormapSC.classList.add("select-container"); + this.colormapSelector = document.createElement("select"); + Colorer.addColorsToSelect(this.colormapSelector); + colormapSC.appendChild(this.colormapSelector); + this.colormapSelector.id = + "color-options-hanlder-" + + this.uniqueNum + + "-colormap-select"; + colormapLbl.setAttribute("for", this.colormapSelector.id); + + // Add a row for choosing whether the color scale should + // be reversed + var reverseColormapP = this.container.appendChild( + document.createElement("p") + ); + var reverseColormapLbl = reverseColormapP.appendChild( + document.createElement("label") + ); + reverseColormapLbl.innerText = "Reverse Color Map"; + this.reverseColormapCheckbox = reverseColormapP.appendChild( + document.createElement("input") + ); + this.reverseColormapCheckbox.id = + "color-options-hanlder-" + + this.uniqueNum + + "-reverse-chk"; + this.reverseColormapCheckbox.setAttribute("type", "checkbox"); + this.reverseColormapCheckbox.classList.add("empress-input"); + reverseColormapLbl.setAttribute("for", this.reverseColormapCheckbox.id); + + var scope = this; + var notify = function() { + var options = scope.getOptions(); + _.each(scope.observers, function(obs) { + obs.colorOptionsUpdate(options); + }); + }; + + if (this.enableContinuousColoring) { + // add continuous values checkbox + var continuousValP = this.container.appendChild( + document.createElement("p") + ); + var continuousValLbl = continuousValP.appendChild( + document.createElement("label") + ); + continuousValLbl.innerText = "Continuous values?"; + this.continuousValCheckbox = continuousValP.appendChild( + document.createElement("input") + ); + this.continuousValCheckbox.id = + "color-options-hanlder-" + + this.uniqueNum + + "-continuous-chk"; + this.continuousValCheckbox.setAttribute("type", "checkbox"); + this.continuousValCheckbox.classList.add("empress-input"); + continuousValLbl.setAttribute("for", this.continuousValCheckbox.id); + // Hide the "Continuous values?" stuff by default, since the default + // colormap is discrete + continuousValP.classList.add("hidden"); + + // add continuous values min/middle/max inputs + var continuousScaleDiv = this.container.appendChild( + document.createElement("div") + ); + continuousScaleDiv.classList.add("hidden"); + var continuousScaleP = continuousScaleDiv.appendChild( + document.createElement("p") + ); + var continuousScaleLbl = continuousScaleP.appendChild( + document.createElement("label") + ); + continuousScaleLbl.innerText = "Manually set boundaries"; + this.continuousScaleCheckbox = continuousScaleP.appendChild( + document.createElement("input") + ); + this.continuousScaleCheckbox.id = + "color-options-hanlder-" + + this.uniqueNum + + "-continuous-scale-chk"; + this.continuousScaleCheckbox.setAttribute("type", "checkbox"); + this.continuousScaleCheckbox.classList.add("empress-input"); + continuousScaleLbl.setAttribute( + "for", + this.continuousScaleCheckbox.id + ); + var continuousMinMaxDiv = continuousScaleDiv.appendChild( + document.createElement("div") + ); + continuousMinMaxDiv.classList.add("hidden"); + + // add min scale input + var continuousMinP = continuousMinMaxDiv.appendChild( + document.createElement("p") + ); + var continuousMinLbl = continuousMinP.appendChild( + document.createElement("label") + ); + continuousMinLbl.innerText = "Min"; + this.continuousMinInput = continuousMinP.appendChild( + document.createElement("input") + ); + this.continuousMinInput.setAttribute("type", "number"); + this.continuousMinInput.classList.add("empress-input"); + this.continuousMinInput.value = null; + this.continuousMinInput.id = + "color-options-hanlder-" + + this.uniqueNum + + "-continuous-min-input"; + continuousMinLbl.setAttribute("for", this.continuousMinInput.id); + + // add max scale input + var continuousMaxP = continuousMinMaxDiv.appendChild( + document.createElement("p") + ); + var continuousMaxLbl = continuousMaxP.appendChild( + document.createElement("label") + ); + continuousMaxLbl.innerText = "Max"; + this.continuousMaxInput = continuousMaxP.appendChild( + document.createElement("input") + ); + this.continuousMaxInput.setAttribute("type", "number"); + this.continuousMaxInput.classList.add("empress-input"); + this.continuousMaxInput.value = null; + this.continuousMaxInput.id = + "color-options-hanlder-" + + this.uniqueNum + + "-continuous-max-input"; + continuousMaxLbl.setAttribute("for", this.continuousMaxInput.id); + + var validateNumInput = function(input) { + util.parseAndValidateNum( + input, + null + ); + }; + + // add events + this.continuousValCheckbox.onchange = () => { + if(scope.continuousValCheckbox.checked) { + continuousScaleDiv.classList.remove("hidden"); + } else { + continuousScaleDiv.classList.add("hidden"); + } + notify(); + }; + this.continuousScaleCheckbox.onchange = () => { + if(scope.continuousScaleCheckbox.checked) { + scope.continuousMinInput.value = null; + scope.continuousMaxInput.value = null; + continuousMinMaxDiv.classList.remove("hidden"); + } else { + continuousMinMaxDiv.classList.add("hidden"); + } + notify(); + } + this.continuousMinInput.onchange = () => { + validateNumInput(scope.continuousMinInput, null); + notify(); + }; + this.continuousMinInput.addEventListener( + "focusout", + () => { + validateNumInput(scope.continuousMinInput, null); + notify(); + } + ); + this.continuousMaxInput.onchange = () => { + validateNumInput(scope.continuousMaxInput, null); + notify(); + }; + this.continuousMaxInput.addEventListener( + "focusout", + () => { + validateNumInput(scope.continuousMaxInput, null); + notify(); + } + ); + } + + + this.colormapSelector.onchange = () => { + if (scope.enableContinuousColoring) { + if (Colorer.isColorMapDiscrete(scope.colormapSelector.value)) { + continuousValP.classList.add("hidden"); + } else { + continuousValP.classList.remove("hidden"); + } + } + notify(); + }; + this.reverseColormapCheckbox.onchange = notify; + } + + ColorOptionsHanlder.prototype.registerObserver = function(obs) { + this.observers.push(obs); + }; + + ColorOptionsHanlder.prototype.getOptions = function() { + var options = { + "color": this.colormapSelector.value, + "reverse": this.reverseColormapCheckbox.checked, + }; + if(this.enableContinuousColoring) { + options.continuousColoring = this.continuousValCheckbox.checked; + options.continuousScale = this.continuousScaleCheckbox.checked; + options.min = this.verifyMinBoundary(); + options.max = this.verifyMaxBoundary(); + } + return options; + }; + + ColorOptionsHanlder.prototype.reset = function() { + this.colormapSelector.value = this.defaultColor; + this.reverseColormapCheckbox.checked = this.defaultReverseChk; + }; + + ColorOptionsHanlder.prototype.verifyMinBoundary = function( ) { + var min = parseFloat(this.continuousMinInput.value); + + if (isNaN(min)) { + return "Min boundary field is missing." + } + + return min; + }; + + ColorOptionsHanlder.prototype.verifyMaxBoundary = function( ) { + var min = parseFloat(this.continuousMinInput.value); + var max = parseFloat(this.continuousMaxInput.value); + + if (isNaN(max)) { + return "Max boundary field is missing." + } + + // It should be noted that if min isNaN that this will always return + // false + if (max <= min) { + return "Max boundary must be greater than Min boundary." + } + + return max; + }; + + return ColorOptionsHanlder; +}); \ No newline at end of file diff --git a/empress/support_files/js/colorer.js b/empress/support_files/js/colorer.js index f2c5f0f51..0566f7992 100644 --- a/empress/support_files/js/colorer.js +++ b/empress/support_files/js/colorer.js @@ -31,6 +31,11 @@ define(["chroma", "underscore", "util"], function (chroma, _, util) { * @param{Boolean} reverse Defaults to false. If true, the color scale * will be reversed, with respect to its default * orientation. + * @param{Object} domain [min, max] or null, default null. + * [min, max] set the min and max of the color + * gradient. + * null will use default min/max for color gradient. + * * @return{Colorer} * constructs Colorer */ @@ -39,7 +44,8 @@ define(["chroma", "underscore", "util"], function (chroma, _, util) { values, useQuantScale = false, gradientIDSuffix = 0, - reverse = false + reverse = false, + domain=null, ) { var scope = this; @@ -48,6 +54,7 @@ define(["chroma", "underscore", "util"], function (chroma, _, util) { this.color = color; this.reverse = reverse; + this.domain = domain; // This object will describe a mapping of unique field values to colors this.__valueToColor = {}; @@ -213,6 +220,21 @@ define(["chroma", "underscore", "util"], function (chroma, _, util) { } }; + Colorer.prototype.getContinuousColorRange = function (nums) { + var min, mid, max; + if (this.domain!==null) { + min = this.domain[0]; + max = this.domain[1]; + } else { + min = _.min(nums); + max = _.max(nums); + } + return { + min: min, + max: max, + } + }; + /** * Assigns colors from a sequential or diverging color palette (specified * by this.color) for every value in this.sortedUniqueValues, taking into @@ -229,7 +251,7 @@ define(["chroma", "underscore", "util"], function (chroma, _, util) { * * @throws Error if this method is called when using a custom colormap. */ - Colorer.prototype.assignContinuousScaledColors = function () { + Colorer.prototype.assignContinuousScaledColors = function (nums) { var scope = this; if (_.isObject(this.color)) { @@ -243,14 +265,16 @@ define(["chroma", "underscore", "util"], function (chroma, _, util) { throw new Error("Category has less than 2 unique numeric values."); } var nums = _.map(split.numeric, parseFloat); - var min = _.min(nums); - var max = _.max(nums); + var range = this.getContinuousColorRange(nums); + var min = range.min; + var max = range.max; var domain; if (this.reverse) { domain = [max, min]; } else { domain = [min, max]; } + var interpolator; if (this.color === Colorer.__GN_OR_PR) { interpolator = chroma.scale(Colorer.__gnOrPr).domain(domain); diff --git a/empress/support_files/js/empress.js b/empress/support_files/js/empress.js index 873b6ae16..544a082c9 100644 --- a/empress/support_files/js/empress.js +++ b/empress/support_files/js/empress.js @@ -1896,13 +1896,36 @@ define([ // if the color map is discrete in the first place. (This is tested // in the Colorer tests; ctrl-F for "CVALDISCRETETEST" in // tests/test-colorer.js to see this.) + var domain = null; + var msg = + "Error with assigning colors in barplot layer " + + layer.num + + ": "; + if (layer.colorByFMContinuousScale) { + var min = layer.colorByFMContinuousMin; + var max = layer.colorByFMContinuousMax; + + if (isNaN(min)) { + msg += min; + util.toastMsg(msg, 5000); + throw msg; + } + + if (isNaN(max)) { + msg += max; + util.toastMsg(msg, 5000); + throw msg; + } + domain = [min, max]; + } try { colorer = new Colorer( layer.colorByFMColorMap, sortedUniqueColorValues, layer.colorByFMContinuous, layer.uniqueNum, - layer.colorByFMColorReverse + layer.colorByFMColorReverse, + domain, ); } catch (err) { // If the Colorer construction failed (should only have diff --git a/empress/support_files/js/side-panel-handler.js b/empress/support_files/js/side-panel-handler.js index bc8a475fc..ef5828adc 100644 --- a/empress/support_files/js/side-panel-handler.js +++ b/empress/support_files/js/side-panel-handler.js @@ -1,4 +1,4 @@ -define(["underscore", "Colorer", "util"], function (_, Colorer, util) { +define(["underscore", "Colorer", "ColorOptionsHandler", "util"], function (_, Colorer, ColorOptionsHandler, util) { /** * * @class SidePanel @@ -52,10 +52,13 @@ define(["underscore", "Colorer", "util"], function (_, Colorer, util) { this.sChk = document.getElementById("sample-chk"); this.sSel = document.getElementById("sample-options"); this.sAddOpts = document.getElementById("sample-add"); - this.sColor = document.getElementById("sample-color"); - this.sReverseColor = document.getElementById( - "sample-reverse-color-chk" + this.sColorOptions = new ColorOptionsHandler( + document.getElementById('sample-color-options-div') ); + this.sColorOptions.registerObserver(this); + // this.sReverseColor = document.getElementById( + // "sample-reverse-color-chk" + // ); this.sCollapseCladesChk = document.getElementById( "sample-collapse-chk" ); @@ -66,10 +69,10 @@ define(["underscore", "Colorer", "util"], function (_, Colorer, util) { this.fChk = document.getElementById("feature-chk"); this.fSel = document.getElementById("feature-options"); this.fAddOpts = document.getElementById("feature-add"); - this.fColor = document.getElementById("feature-color"); - this.fReverseColor = document.getElementById( - "feature-reverse-color-chk" + this.fColorOptions = new ColorOptionsHandler( + document.getElementById('feature-color-options-div') ); + this.fColorOptions.registerObserver(this); this.fCollapseCladesChk = document.getElementById( "feature-collapse-chk" ); @@ -187,6 +190,18 @@ define(["underscore", "Colorer", "util"], function (_, Colorer, util) { }; } + SidePanel.prototype.colorOptionsUpdate = function(options) { + this.showUpdateBtn(); + }; + + SidePanel.prototype.resetColorOptions = function() { + if (this.sChk.checked) { + this.fColorOptions.reset(); + } else if (this.fChk.checked) { + this.fColorOptions.reset(); + } + }; + /** * Utility function that resets various HTML elements, then resets the * tree and its legends. @@ -208,6 +223,7 @@ define(["underscore", "Colorer", "util"], function (_, Colorer, util) { */ SidePanel.prototype._resetTab = function (eleNameToProperties, elesToHide) { var scope = this; + this.resetColorOptions(); _.each(eleNameToProperties, function (properties, eleName) { _.each(properties, function (propVal, prop) { scope[eleName][prop] = propVal; @@ -228,8 +244,6 @@ define(["underscore", "Colorer", "util"], function (_, Colorer, util) { { sChk: { checked: false }, sSel: { disabled: true }, - sColor: { value: "discrete-coloring-qiime" }, - sReverseColor: { checked: false }, sLineWidth: { value: 0 }, sCollapseCladesChk: { checked: false }, }, @@ -243,8 +257,6 @@ define(["underscore", "Colorer", "util"], function (_, Colorer, util) { { fChk: { checked: false }, fSel: { disabled: true }, - fColor: { value: "discrete-coloring-qiime" }, - fReverseColor: { checked: false }, fLineWidth: { value: 0 }, fMethodChk: { checked: true }, fCollapseCladesChk: { checked: false }, @@ -313,9 +325,12 @@ define(["underscore", "Colorer", "util"], function (_, Colorer, util) { */ SidePanel.prototype._colorSampleTree = function () { var colBy = this.sSel.value; - var col = this.sColor.value; - var reverse = this.sReverseColor.checked; - var keyInfo = this.empress.colorBySampleCat(colBy, col, reverse); + var colorOptions = this.sColorOptions.getOptions(); + var keyInfo = this.empress.colorBySampleCat( + colBy, + colorOptions.color, + colorOptions.reverse + ); if (keyInfo === null) { util.toastMsg( "No unique branches found for this metadata category" @@ -330,14 +345,13 @@ define(["underscore", "Colorer", "util"], function (_, Colorer, util) { */ SidePanel.prototype._colorFeatureTree = function () { var colBy = this.fSel.value; - var col = this.fColor.value; var coloringMethod = this.fMethodChk.checked ? "tip" : "all"; - var reverse = this.fReverseColor.checked; + var colorOptions = this.fColorOptions.getOptions(); this.empress.colorByFeatureMetadata( colBy, - col, + colorOptions.color, coloringMethod, - reverse + colorOptions.reverse ); }; @@ -483,6 +497,14 @@ define(["underscore", "Colorer", "util"], function (_, Colorer, util) { }; }; + SidePanel.prototype.showUpdateBtn = function() { + if (this.sChk.checked) { + this.sUpdateBtn.classList.remove("hidden"); + } else if (this.fChk.checked) { + this.fUpdateBtn.classList.remove("hidden"); + } + } + /** * Initializes sample components */ @@ -500,9 +522,6 @@ define(["underscore", "Colorer", "util"], function (_, Colorer, util) { this.sSel.appendChild(opt); } - // The color map selector - Colorer.addColorsToSelect(this.sColor); - // toggle the sample/color map selectors this.sChk.onclick = function () { if (scope.sChk.checked) { @@ -515,13 +534,12 @@ define(["underscore", "Colorer", "util"], function (_, Colorer, util) { } }; - var showUpdateBtn = function () { - scope.sUpdateBtn.classList.remove("hidden"); + this.sSel.onchange = () => { + scope.showUpdateBtn(); + }; + this.sLineWidth.onchange = () => { + scope.showUpdateBtn(); }; - this.sSel.onchange = showUpdateBtn; - this.sColor.onchange = showUpdateBtn; - this.sReverseColor.onchange = showUpdateBtn; - this.sLineWidth.onchange = showUpdateBtn; this.sUpdateBtn.onclick = function () { scope._updateColoring( @@ -590,9 +608,6 @@ define(["underscore", "Colorer", "util"], function (_, Colorer, util) { this.fSel.appendChild(opt); } - // The color map selector - Colorer.addColorsToSelect(this.fColor); - // toggle the sample/color map selectors this.fChk.onclick = function () { if (scope.fChk.checked) { @@ -606,16 +621,15 @@ define(["underscore", "Colorer", "util"], function (_, Colorer, util) { } }; - var showUpdateBtn = function () { - scope.fUpdateBtn.classList.remove("hidden"); + this.fSel.onchange = () => { + scope.showUpdateBtn(); + }; + this.fLineWidth.onchange = () => { + scope.showUpdateBtn; }; - this.fSel.onchange = showUpdateBtn; - this.fColor.onchange = showUpdateBtn; - this.fReverseColor.onchange = showUpdateBtn; - this.fLineWidth.onchange = showUpdateBtn; this.fMethodChk.onchange = function () { scope.updateFeatureMethodDesc(); - showUpdateBtn(); + scope.showUpdateBtn(); }; this.fUpdateBtn.onclick = function () { diff --git a/empress/support_files/js/util.js b/empress/support_files/js/util.js index 9a5027fe0..4e1e6656c 100644 --- a/empress/support_files/js/util.js +++ b/empress/support_files/js/util.js @@ -171,7 +171,7 @@ define(["underscore"], function (_) { function parseAndValidateNum(inputEle, min = 0) { if (isValidNumber(inputEle.value)) { var pfVal = parseFloat(inputEle.value); - if (pfVal >= min) { + if (min === null || pfVal >= min) { return pfVal; } } diff --git a/empress/support_files/templates/empress-template.html b/empress/support_files/templates/empress-template.html index 891217e1b..57cc699f8 100644 --- a/empress/support_files/templates/empress-template.html +++ b/empress/support_files/templates/empress-template.html @@ -115,7 +115,8 @@ 'util' : './js/util', 'LayoutsUtil': './js/layouts-util', 'ExportUtil': './js/export-util', - 'TreeController': './js/tree-controller' + 'TreeController': './js/tree-controller', + 'ColorOptionsHandler': './js/color-options-handler', } }); diff --git a/empress/support_files/templates/side-panel.html b/empress/support_files/templates/side-panel.html index 2a59552fe..92187bc76 100644 --- a/empress/support_files/templates/side-panel.html +++ b/empress/support_files/templates/side-panel.html @@ -30,17 +30,7 @@