UNPKG

5.5 kBJavaScriptView Raw
1const EventEmitter = require('events').EventEmitter
2const inherits = require('inherits')
3const isNumeric = require('is-numeric')
4const css = require('dom-css')
5const format = require('param-case')
6const precision = require('mumath/precision')
7
8module.exports = Range
9inherits(Range, EventEmitter)
10
11function Range (opts) {
12 if (!(this instanceof Range)) return new Range(opts);
13
14 this.update(opts);
15}
16
17Range.prototype.update = function (opts) {
18 var scaleValue, scaleValueInverse, logmin, logmax, logsign
19
20 if (!!opts.step && !!opts.steps) {
21 throw new Error('Cannot specify both step and steps. Got step = ' + opts.step + ', steps = ', opts.steps)
22 }
23
24 opts.container.innerHTML = '';
25
26 if (!opts.container) opts.container = document.body;
27
28 var input = opts.container.querySelector('.settings-panel-range');
29
30 if (!input) {
31 input = opts.container.appendChild(document.createElement('input'))
32 input.type = 'range'
33 input.className = 'settings-panel-range'
34 }
35
36 if (opts.disabled) input.disabled = true;
37
38 // Create scale functions for converting to/from the desired scale:
39 if (opts.scale === 'log') {
40 scaleValue = function (x) {
41 return logsign * Math.exp(Math.log(logmin) + (Math.log(logmax) - Math.log(logmin)) * x / 100)
42 }
43 scaleValueInverse = function (y) {
44 return (Math.log(y * logsign) - Math.log(logmin)) * 100 / (Math.log(logmax) - Math.log(logmin))
45 }
46 } else {
47 scaleValue = scaleValueInverse = function (x) { return x }
48 }
49
50 // Get initial value:
51 if (opts.scale === 'log') {
52 // Get options or set defaults:
53 opts.max = (isNumeric(opts.max)) ? opts.max : 100
54 opts.min = (isNumeric(opts.min)) ? opts.min : 0.1
55
56 // Check if all signs are valid:
57 if (opts.min * opts.max <= 0) {
58 throw new Error('Log range min/max must have the same sign and not equal zero. Got min = ' + opts.min + ', max = ' + opts.max)
59 } else {
60 // Pull these into separate variables so that opts can define the *slider* mapping
61 logmin = opts.min
62 logmax = opts.max
63 logsign = opts.min > 0 ? 1 : -1
64
65 // Got the sign so force these positive:
66 logmin = Math.abs(logmin)
67 logmax = Math.abs(logmax)
68
69 // These are now simply 0-100 to which we map the log range:
70 opts.min = 0
71 opts.max = 100
72
73 // Step is invalid for a log range:
74 if (isNumeric(opts.step)) {
75 throw new Error('Log may only use steps (integer number of steps), not a step value. Got step =' + opts.step)
76 }
77 // Default step is simply 1 in linear slider space:
78 opts.step = 1
79 }
80
81 opts.value = scaleValueInverse(isNumeric(opts.value) ? opts.value : scaleValue((opts.min + opts.max) * 0.5))
82
83 if (opts.value * scaleValueInverse(opts.max) <= 0) {
84 throw new Error('Log range initial value must have the same sign as min/max and must not equal zero. Got initial value = ' + opts.value)
85 }
86 } else {
87 // If linear, this is much simpler:
88 opts.max = (isNumeric(opts.max)) ? opts.max : 100
89 opts.min = (isNumeric(opts.min)) ? opts.min : 0
90 opts.step = (isNumeric(opts.step)) ? opts.step : (opts.max - opts.min) / 100
91
92 opts.value = isNumeric(opts.value) ? opts.value : (opts.min + opts.max) * 0.5
93 }
94
95 // If we got a number of steps, use that instead:
96 if (isNumeric(opts.steps)) {
97 opts.step = isNumeric(opts.steps) ? (opts.max - opts.min) / opts.steps : opts.step
98 }
99
100 // Quantize the initial value to the requested step:
101 var initialStep = Math.round((opts.value - opts.min) / opts.step)
102 opts.value = opts.min + opts.step * initialStep
103
104 //preser container data for display
105 opts.container.setAttribute('data-min', opts.min);
106 opts.container.setAttribute('data-max', opts.max);
107
108 if (opts.scale === 'log') {
109 //FIXME: not every log is of precision 3
110 var prec = 3;
111 }
112 else {
113 if (opts.step) {
114 var prec = precision(opts.step);
115 }
116 else if (opts.steps) {
117 var prec = precision( (opts.max - opts.min) / opts.steps );
118 }
119 }
120
121 var value = require('./value')({
122 id: opts.id,
123 container: opts.container,
124 className: 'settings-panel-range-value',
125 value: scaleValue(opts.value).toFixed(prec),
126 type: opts.scale === 'log' ? 'text' : 'number',
127 min: scaleValue(opts.min),
128 max: scaleValue(opts.max),
129 disabled: opts.disabled,
130 //FIXME: step here might vary
131 step: opts.scale === 'log' ? 0.01 : opts.step,
132 input: (v) => {
133 input.value = scaleValueInverse(v)
134 // value.value = v
135 this.emit('input', v);
136 input.setAttribute('value', v.toFixed(0))
137 opts.container.style.setProperty('--value', v + '%');
138 opts.container.style.setProperty('--coef', v/100);
139 }
140 });
141
142 // Set value on the input itself:
143 input.min = opts.min
144 input.max = opts.max
145 input.step = opts.step
146 input.value = opts.value
147 let v = 100 * (opts.value - opts.min) / (opts.max - opts.min);
148 input.setAttribute('value', v.toFixed(0))
149 opts.container.style.setProperty('--value', v + '%');
150 opts.container.style.setProperty('--coef', v/100);
151
152 setTimeout(() => {
153 this.emit('init', parseFloat(input.value))
154 });
155
156 input.oninput = (data) => {
157 var scaledValue = scaleValue(parseFloat(data.target.value));
158 value.value = scaledValue.toFixed(prec);
159 let v = 100 * (data.target.value - opts.min) / (opts.max - opts.min);
160 input.setAttribute('value', v.toFixed(0));
161 opts.container.style.setProperty('--value', v + '%');
162 opts.container.style.setProperty('--coef', v/100);
163 this.emit('input', scaledValue);
164 }
165
166 return this;
167}