From a5bb92ac84042b6cfc07898a00b36a1a10fa06d0 Mon Sep 17 00:00:00 2001 From: Leon Gersen Date: Mon, 11 May 2020 17:42:47 +0200 Subject: [PATCH] noUiSlider 14.5.0 --- CHANGELOG.MD | 3 + distribute/nouislider.css | 2 +- distribute/nouislider.js | 164 +++++++++++++++++++++++++++------- distribute/nouislider.min.css | 2 +- distribute/nouislider.min.js | 4 +- package.json | 2 +- 6 files changed, 141 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.MD b/CHANGELOG.MD index 3ad43c07..021e6da8 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -1,5 +1,8 @@ # Changelog +### 14.5.0 (*2020-05-20*) +- Added: Support for `margin`, `padding` and `limit` on non-linear sliders (#911, #1030, #1031, #1071); + ### 14.4.0 (*2020-05-06*) - Added: `getOrigins` and `getTooltips` methods; - Added: Default styling to support merging overlapping tooltips (#1032); diff --git a/distribute/nouislider.css b/distribute/nouislider.css index 26a3c801..6286add3 100644 --- a/distribute/nouislider.css +++ b/distribute/nouislider.css @@ -1,4 +1,4 @@ -/*! nouislider - 14.4.0 - 5/6/2020 */ +/*! nouislider - 14.5.0 - 5/11/2020 */ /* Functional styling; * These styles are required for noUiSlider to function. * You don't need to change these rules to apply your design. diff --git a/distribute/nouislider.js b/distribute/nouislider.js index 63a7c000..574528cb 100644 --- a/distribute/nouislider.js +++ b/distribute/nouislider.js @@ -1,4 +1,4 @@ -/*! nouislider - 14.4.0 - 5/6/2020 */ +/*! nouislider - 14.5.0 - 5/11/2020 */ (function(factory) { if (typeof define === "function" && define.amd) { // AMD. Register as an anonymous module. @@ -13,7 +13,7 @@ })(function() { "use strict"; - var VERSION = "14.4.0"; + var VERSION = "14.5.0"; //region Helper Methods @@ -206,13 +206,13 @@ } // (percentage) How many percent is this value of this range? - function fromPercentage(range, value) { - return (value * 100) / (range[1] - range[0]); + function fromPercentage(range, value, startRange) { + return (value * 100) / (range[startRange + 1] - range[startRange]); } // (percentage) Where is this value on this range? function toPercentage(range, value) { - return fromPercentage(range, range[0] < 0 ? value + Math.abs(range[0]) : value - range[0]); + return fromPercentage(range, range[0] < 0 ? value + Math.abs(range[0]) : value - range[0], 0); } // (value) How much is this percentage on this range? @@ -348,7 +348,7 @@ // Factor to range ratio that.xSteps[i] = - fromPercentage([that.xVal[i], that.xVal[i + 1]], n) / subRangeRatio(that.xPct[i], that.xPct[i + 1]); + fromPercentage([that.xVal[i], that.xVal[i + 1]], n, 0) / subRangeRatio(that.xPct[i], that.xPct[i + 1]); var totalSteps = (that.xVal[i + 1] - that.xVal[i]) / that.xNumSteps[i]; var highestStep = Math.ceil(Number(totalSteps.toFixed(3)) - 1); @@ -406,14 +406,107 @@ } } - Spectrum.prototype.getMargin = function(value) { - var step = this.xNumSteps[0]; + Spectrum.prototype.getDistance = function(value) { + var index; + var distances = []; + + for (index = 0; index < this.xNumSteps.length - 1; index++) { + // last "range" can't contain step size as it is purely an endpoint. + var step = this.xNumSteps[index]; + + if (step && (value / step) % 1 !== 0) { + throw new Error( + "noUiSlider (" + + VERSION + + "): 'limit', 'margin' and 'padding' of " + + this.xPct[index] + + "% range must be divisible by step." + ); + } + + // Calculate percentual distance in current range of limit, margin or padding + distances[index] = fromPercentage(this.xVal, value, index); + } + + return distances; + }; + + // Calculate the percentual distance over the whole scale of ranges. + // direction: 0 = backwards / 1 = forwards + Spectrum.prototype.getAbsoluteDistance = function(value, distances, direction) { + var xPct_index = 0; + + // Calculate range where to start calculation + if (value < this.xPct[this.xPct.length - 1]) { + while (value > this.xPct[xPct_index + 1]) { + xPct_index++; + } + } else if (value === this.xPct[this.xPct.length - 1]) { + xPct_index = this.xPct.length - 2; + } + + // If looking backwards and the value is exactly at a range separator then look one range further + if (!direction && value === this.xPct[xPct_index + 1]) { + xPct_index++; + } + + var start_factor; + var rest_factor = 1; + + var rest_rel_distance = distances[xPct_index]; + + var range_pct = 0; - if (step && (value / step) % 1 !== 0) { - throw new Error("noUiSlider (" + VERSION + "): 'limit', 'margin' and 'padding' must be divisible by step."); + var rel_range_distance = 0; + var abs_distance_counter = 0; + var range_counter = 0; + + // Calculate what part of the start range the value is + if (direction) { + start_factor = (value - this.xPct[xPct_index]) / (this.xPct[xPct_index + 1] - this.xPct[xPct_index]); + } else { + start_factor = (this.xPct[xPct_index + 1] - value) / (this.xPct[xPct_index + 1] - this.xPct[xPct_index]); + } + + // Do until the complete distance across ranges is calculated + while (rest_rel_distance > 0) { + // Calculate the percentage of total range + range_pct = this.xPct[xPct_index + 1 + range_counter] - this.xPct[xPct_index + range_counter]; + + // Detect if the margin, padding or limit is larger then the current range and calculate + if (distances[xPct_index + range_counter] * rest_factor + 100 - start_factor * 100 > 100) { + // If larger then take the percentual distance of the whole range + rel_range_distance = range_pct * start_factor; + // Rest factor of relative percentual distance still to be calculated + rest_factor = (rest_rel_distance - 100 * start_factor) / distances[xPct_index + range_counter]; + // Set start factor to 1 as for next range it does not apply. + start_factor = 1; + } else { + // If smaller or equal then take the percentual distance of the calculate percentual part of that range + rel_range_distance = ((distances[xPct_index + range_counter] * range_pct) / 100) * rest_factor; + // No rest left as the rest fits in current range + rest_factor = 0; + } + + if (direction) { + abs_distance_counter = abs_distance_counter - rel_range_distance; + // Limit range to first range when distance becomes outside of minimum range + if (this.xPct.length + range_counter >= 1) { + range_counter--; + } + } else { + abs_distance_counter = abs_distance_counter + rel_range_distance; + // Limit range to last range when distance becomes outside of maximum range + if (this.xPct.length - range_counter >= 1) { + range_counter++; + } + } + + // Rest of relative percentual distance still to be calculated + rest_rel_distance = distances[xPct_index + range_counter] * rest_factor; } - return this.xPct.length === 2 ? fromPercentage(this.xVal, value) : false; + return value + abs_distance_counter; }; Spectrum.prototype.toStepping = function(value) { @@ -678,11 +771,7 @@ return; } - parsed.margin = parsed.spectrum.getMargin(entry); - - if (!parsed.margin) { - throw new Error("noUiSlider (" + VERSION + "): 'margin' option is only supported on linear sliders."); - } + parsed.margin = parsed.spectrum.getDistance(entry); } function testLimit(parsed, entry) { @@ -690,7 +779,7 @@ throw new Error("noUiSlider (" + VERSION + "): 'limit' option must be numeric."); } - parsed.limit = parsed.spectrum.getMargin(entry); + parsed.limit = parsed.spectrum.getDistance(entry); if (!parsed.limit || parsed.handles < 2) { throw new Error( @@ -702,6 +791,8 @@ } function testPadding(parsed, entry) { + var index; + if (!isNumeric(entry) && !Array.isArray(entry)) { throw new Error( "noUiSlider (" + VERSION + "): 'padding' option must be numeric or array of exactly 2 numbers." @@ -722,18 +813,21 @@ entry = [entry, entry]; } - // 'getMargin' returns false for invalid values. - parsed.padding = [parsed.spectrum.getMargin(entry[0]), parsed.spectrum.getMargin(entry[1])]; + // 'getDistance' returns false for invalid values. + parsed.padding = [parsed.spectrum.getDistance(entry[0]), parsed.spectrum.getDistance(entry[1])]; - if (parsed.padding[0] === false || parsed.padding[1] === false) { - throw new Error("noUiSlider (" + VERSION + "): 'padding' option is only supported on linear sliders."); + for (index = 0; index < parsed.spectrum.xNumSteps.length - 1; index++) { + // last "range" can't contain step size as it is purely an endpoint. + if (parsed.padding[0][index] < 0 || parsed.padding[1][index] < 0) { + throw new Error("noUiSlider (" + VERSION + "): 'padding' option must be a positive number(s)."); + } } - if (parsed.padding[0] < 0 || parsed.padding[1] < 0) { - throw new Error("noUiSlider (" + VERSION + "): 'padding' option must be a positive number(s)."); - } + var totalPadding = entry[0] + entry[1]; + var firstValue = parsed.spectrum.xVal[0]; + var lastValue = parsed.spectrum.xVal[parsed.spectrum.xVal.length - 1]; - if (parsed.padding[0] + parsed.padding[1] > 100) { + if (totalPadding / (lastValue - firstValue) > 1) { throw new Error("noUiSlider (" + VERSION + "): 'padding' option must not exceed 100% of the range."); } } @@ -2008,15 +2102,19 @@ // Split out the handle positioning logic so the Move event can use it, too function checkHandlePosition(reference, handleNumber, to, lookBackward, lookForward, getValue) { + var distance; + // For sliders with multiple handles, limit movement to the other handle. // Apply the margin option by adding it to the handle positions. if (scope_Handles.length > 1 && !options.events.unconstrained) { if (lookBackward && handleNumber > 0) { - to = Math.max(to, reference[handleNumber - 1] + options.margin); + distance = scope_Spectrum.getAbsoluteDistance(reference[handleNumber - 1], options.margin, 0); + to = Math.max(to, distance); } if (lookForward && handleNumber < scope_Handles.length - 1) { - to = Math.min(to, reference[handleNumber + 1] - options.margin); + distance = scope_Spectrum.getAbsoluteDistance(reference[handleNumber + 1], options.margin, 1); + to = Math.min(to, distance); } } @@ -2025,11 +2123,13 @@ // handles would be unmovable. if (scope_Handles.length > 1 && options.limit) { if (lookBackward && handleNumber > 0) { - to = Math.min(to, reference[handleNumber - 1] + options.limit); + distance = scope_Spectrum.getAbsoluteDistance(reference[handleNumber - 1], options.limit, 0); + to = Math.min(to, distance); } if (lookForward && handleNumber < scope_Handles.length - 1) { - to = Math.max(to, reference[handleNumber + 1] - options.limit); + distance = scope_Spectrum.getAbsoluteDistance(reference[handleNumber + 1], options.limit, 1); + to = Math.max(to, distance); } } @@ -2037,11 +2137,13 @@ // edges of the slider. Padding must be > 0. if (options.padding) { if (handleNumber === 0) { - to = Math.max(to, options.padding[0]); + distance = scope_Spectrum.getAbsoluteDistance(0, options.padding[0], 0); + to = Math.max(to, distance); } if (handleNumber === scope_Handles.length - 1) { - to = Math.min(to, 100 - options.padding[1]); + distance = scope_Spectrum.getAbsoluteDistance(100, options.padding[1], 1); + to = Math.min(to, distance); } } diff --git a/distribute/nouislider.min.css b/distribute/nouislider.min.css index 32a5f79a..ecb97869 100644 --- a/distribute/nouislider.min.css +++ b/distribute/nouislider.min.css @@ -1,2 +1,2 @@ -/*! nouislider - 14.4.0 - 5/6/2020 */ +/*! nouislider - 14.5.0 - 5/11/2020 */ .noUi-target,.noUi-target *{-webkit-touch-callout:none;-webkit-tap-highlight-color:transparent;-webkit-user-select:none;-ms-touch-action:none;touch-action:none;-ms-user-select:none;-moz-user-select:none;user-select:none;-moz-box-sizing:border-box;box-sizing:border-box}.noUi-target{position:relative}.noUi-base,.noUi-connects{width:100%;height:100%;position:relative;z-index:1}.noUi-connects{overflow:hidden;z-index:0}.noUi-connect,.noUi-origin{will-change:transform;position:absolute;z-index:1;top:0;right:0;-ms-transform-origin:0 0;-webkit-transform-origin:0 0;-webkit-transform-style:preserve-3d;transform-origin:0 0;transform-style:flat}.noUi-connect{height:100%;width:100%}.noUi-origin{height:10%;width:10%}.noUi-txt-dir-rtl.noUi-horizontal .noUi-origin{left:0;right:auto}.noUi-vertical .noUi-origin{width:0}.noUi-horizontal .noUi-origin{height:0}.noUi-handle{-webkit-backface-visibility:hidden;backface-visibility:hidden;position:absolute}.noUi-touch-area{height:100%;width:100%}.noUi-state-tap .noUi-connect,.noUi-state-tap .noUi-origin{-webkit-transition:transform .3s;transition:transform .3s}.noUi-state-drag *{cursor:inherit!important}.noUi-horizontal{height:18px}.noUi-horizontal .noUi-handle{width:34px;height:28px;right:-17px;top:-6px}.noUi-vertical{width:18px}.noUi-vertical .noUi-handle{width:28px;height:34px;right:-6px;top:-17px}.noUi-txt-dir-rtl.noUi-horizontal .noUi-handle{left:-17px;right:auto}.noUi-target{background:#FAFAFA;border-radius:4px;border:1px solid #D3D3D3;box-shadow:inset 0 1px 1px #F0F0F0,0 3px 6px -5px #BBB}.noUi-connects{border-radius:3px}.noUi-connect{background:#3FB8AF}.noUi-draggable{cursor:ew-resize}.noUi-vertical .noUi-draggable{cursor:ns-resize}.noUi-handle{border:1px solid #D9D9D9;border-radius:3px;background:#FFF;cursor:default;box-shadow:inset 0 0 1px #FFF,inset 0 1px 7px #EBEBEB,0 3px 6px -3px #BBB}.noUi-active{box-shadow:inset 0 0 1px #FFF,inset 0 1px 7px #DDD,0 3px 6px -3px #BBB}.noUi-handle:after,.noUi-handle:before{content:"";display:block;position:absolute;height:14px;width:1px;background:#E8E7E6;left:14px;top:6px}.noUi-handle:after{left:17px}.noUi-vertical .noUi-handle:after,.noUi-vertical .noUi-handle:before{width:14px;height:1px;left:6px;top:14px}.noUi-vertical .noUi-handle:after{top:17px}[disabled] .noUi-connect{background:#B8B8B8}[disabled] .noUi-handle,[disabled].noUi-handle,[disabled].noUi-target{cursor:not-allowed}.noUi-pips,.noUi-pips *{-moz-box-sizing:border-box;box-sizing:border-box}.noUi-pips{position:absolute;color:#999}.noUi-value{position:absolute;white-space:nowrap;text-align:center}.noUi-value-sub{color:#ccc;font-size:10px}.noUi-marker{position:absolute;background:#CCC}.noUi-marker-sub{background:#AAA}.noUi-marker-large{background:#AAA}.noUi-pips-horizontal{padding:10px 0;height:80px;top:100%;left:0;width:100%}.noUi-value-horizontal{-webkit-transform:translate(-50%,50%);transform:translate(-50%,50%)}.noUi-rtl .noUi-value-horizontal{-webkit-transform:translate(50%,50%);transform:translate(50%,50%)}.noUi-marker-horizontal.noUi-marker{margin-left:-1px;width:2px;height:5px}.noUi-marker-horizontal.noUi-marker-sub{height:10px}.noUi-marker-horizontal.noUi-marker-large{height:15px}.noUi-pips-vertical{padding:0 10px;height:100%;top:0;left:100%}.noUi-value-vertical{-webkit-transform:translate(0,-50%);transform:translate(0,-50%);padding-left:25px}.noUi-rtl .noUi-value-vertical{-webkit-transform:translate(0,50%);transform:translate(0,50%)}.noUi-marker-vertical.noUi-marker{width:5px;height:2px;margin-top:-1px}.noUi-marker-vertical.noUi-marker-sub{width:10px}.noUi-marker-vertical.noUi-marker-large{width:15px}.noUi-tooltip{display:block;position:absolute;border:1px solid #D9D9D9;border-radius:3px;background:#fff;color:#000;padding:5px;text-align:center;white-space:nowrap}.noUi-horizontal .noUi-tooltip{-webkit-transform:translate(-50%,0);transform:translate(-50%,0);left:50%;bottom:120%}.noUi-vertical .noUi-tooltip{-webkit-transform:translate(0,-50%);transform:translate(0,-50%);top:50%;right:120%}.noUi-horizontal .noUi-origin>.noUi-tooltip{-webkit-transform:translate(50%,0);transform:translate(50%,0);left:auto;bottom:10px}.noUi-vertical .noUi-origin>.noUi-tooltip{-webkit-transform:translate(0,-18px);transform:translate(0,-18px);top:auto;right:28px} \ No newline at end of file diff --git a/distribute/nouislider.min.js b/distribute/nouislider.min.js index 133c7931..f8c8fa69 100644 --- a/distribute/nouislider.min.js +++ b/distribute/nouislider.min.js @@ -1,2 +1,2 @@ -/*! nouislider - 14.4.0 - 5/6/2020 */ -!function(t){"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?module.exports=t():window.noUiSlider=t()}(function(){"use strict";var lt="14.4.0";function ut(t){t.parentElement.removeChild(t)}function a(t){return null!=t}function ct(t){t.preventDefault()}function i(t){return"number"==typeof t&&!isNaN(t)&&isFinite(t)}function pt(t,e,r){0=e[r];)r+=1;return r}function r(t,e,r){if(r>=t.slice(-1)[0])return 100;var n,i,o=f(r,t),s=t[o-1],a=t[o],l=e[o-1],u=e[o];return l+(i=r,p(n=[s,a],n[0]<0?i+Math.abs(n[0]):i-n[0])/c(l,u))}function n(t,e,r,n){if(100===n)return n;var i,o,s=f(n,t),a=t[s-1],l=t[s];return r?(l-a)/2= 2) required for mode 'count'.");var n=e-1,i=100/n;for(e=[];n--;)e[n]=n*i;e.push(100),t="positions"}return"positions"===t?e.map(function(t){return y.fromStepping(r?y.getStep(t):t)}):"values"===t?r?e.map(function(t){return y.fromStepping(y.getStep(y.toStepping(t)))}):e:void 0}(n,t.values||!1,t.stepped||!1),a=(m=i,g=n,v=s,b={},e=y.xVal[0],r=y.xVal[y.xVal.length-1],x=S=!1,w=0,(v=v.slice().sort(function(t,e){return t-e}).filter(function(t){return!this[t]&&(this[t]=!0)},{}))[0]!==e&&(v.unshift(e),S=!0),v[v.length-1]!==r&&(v.push(r),x=!0),v.forEach(function(t,e){var r,n,i,o,s,a,l,u,c,p,f=t,d=v[e+1],h="steps"===g;if(h&&(r=y.xNumSteps[e]),r||(r=d-f),!1!==f&&void 0!==d)for(r=Math.max(r,1e-7),n=f;n<=d;n=(n+r).toFixed(7)/1){for(u=(s=(o=y.toStepping(n))-w)/m,p=s/(c=Math.round(u)),i=1;i<=c;i+=1)b[(a=w+i*p).toFixed(5)]=[y.fromStepping(a),0];l=-1r.stepAfter.startValue&&(i=r.stepAfter.startValue-n),o=n>r.thisStep.startValue?r.thisStep.step:!1!==r.stepBefore.step&&n-r.stepBefore.highestStep,100===e?i=null:0===e&&(o=null);var s=y.countStepDecimals();return null!==i&&!1!==i&&(i=Number(i.toFixed(s))),null!==o&&!1!==o&&(o=Number(o.toFixed(s))),[o,i]}return ht(e=h,v.cssClasses.target),0===v.dir?ht(e,v.cssClasses.ltr):ht(e,v.cssClasses.rtl),0===v.ort?ht(e,v.cssClasses.horizontal):ht(e,v.cssClasses.vertical),ht(e,"rtl"===getComputedStyle(e).direction?v.cssClasses.textDirectionRtl:v.cssClasses.textDirectionLtr),l=A(e,v.cssClasses.base),function(t,e){var r=A(e,v.cssClasses.connects);u=[],(s=[]).push(O(r,t[0]));for(var n=0;n=e[r];)r+=1;return r}function r(t,e,r){if(r>=t.slice(-1)[0])return 100;var n,i,o=f(r,t),s=t[o-1],a=t[o],l=e[o-1],u=e[o];return l+(i=r,p(n=[s,a],n[0]<0?i+Math.abs(n[0]):i-n[0],0)/c(l,u))}function n(t,e,r,n){if(100===n)return n;var i,o,s=f(n,t),a=t[s-1],l=t[s];return r?(l-a)/2this.xPct[i+1];)i++;else t===this.xPct[this.xPct.length-1]&&(i=this.xPct.length-2);r||t!==this.xPct[i+1]||i++;var o=1,s=e[i],a=0,l=0,u=0,c=0;for(n=r?(t-this.xPct[i])/(this.xPct[i+1]-this.xPct[i]):(this.xPct[i+1]-t)/(this.xPct[i+1]-this.xPct[i]);0= 2) required for mode 'count'.");var n=e-1,i=100/n;for(e=[];n--;)e[n]=n*i;e.push(100),t="positions"}return"positions"===t?e.map(function(t){return y.fromStepping(r?y.getStep(t):t)}):"values"===t?r?e.map(function(t){return y.fromStepping(y.getStep(y.toStepping(t)))}):e:void 0}(n,t.values||!1,t.stepped||!1),a=(m=i,g=n,v=s,x={},e=y.xVal[0],r=y.xVal[y.xVal.length-1],S=b=!1,w=0,(v=v.slice().sort(function(t,e){return t-e}).filter(function(t){return!this[t]&&(this[t]=!0)},{}))[0]!==e&&(v.unshift(e),b=!0),v[v.length-1]!==r&&(v.push(r),S=!0),v.forEach(function(t,e){var r,n,i,o,s,a,l,u,c,p,f=t,d=v[e+1],h="steps"===g;if(h&&(r=y.xNumSteps[e]),r||(r=d-f),!1!==f&&void 0!==d)for(r=Math.max(r,1e-7),n=f;n<=d;n=(n+r).toFixed(7)/1){for(u=(s=(o=y.toStepping(n))-w)/m,p=s/(c=Math.round(u)),i=1;i<=c;i+=1)x[(a=w+i*p).toFixed(5)]=[y.fromStepping(a),0];l=-1r.stepAfter.startValue&&(i=r.stepAfter.startValue-n),o=n>r.thisStep.startValue?r.thisStep.step:!1!==r.stepBefore.step&&n-r.stepBefore.highestStep,100===e?i=null:0===e&&(o=null);var s=y.countStepDecimals();return null!==i&&!1!==i&&(i=Number(i.toFixed(s))),null!==o&&!1!==o&&(o=Number(o.toFixed(s))),[o,i]}return ht(e=h,v.cssClasses.target),0===v.dir?ht(e,v.cssClasses.ltr):ht(e,v.cssClasses.rtl),0===v.ort?ht(e,v.cssClasses.horizontal):ht(e,v.cssClasses.vertical),ht(e,"rtl"===getComputedStyle(e).direction?v.cssClasses.textDirectionRtl:v.cssClasses.textDirectionLtr),l=k(e,v.cssClasses.base),function(t,e){var r=k(e,v.cssClasses.connects);u=[],(s=[]).push(M(r,t[0]));for(var n=0;n