1 | "use strict";
|
2 |
|
3 | Object.defineProperty(exports, "__esModule", {
|
4 | value: true
|
5 | });
|
6 | exports.createScale = createScale;
|
7 | exports.luminance = luminance;
|
8 | exports.contrast = contrast;
|
9 | exports.binarySearch = binarySearch;
|
10 | exports.generateBaseScale = generateBaseScale;
|
11 | exports.generateContrastColors = generateContrastColors;
|
12 | exports.minPositive = minPositive;
|
13 | exports.ratioName = ratioName;
|
14 | exports.generateAdaptiveTheme = generateAdaptiveTheme;
|
15 |
|
16 | var _d = _interopRequireDefault(require("d3"));
|
17 |
|
18 | var d3cam02 = _interopRequireWildcard(require("d3-cam02"));
|
19 |
|
20 | var _d3Hsluv = _interopRequireDefault(require("d3-hsluv"));
|
21 |
|
22 | var d3hsv = _interopRequireWildcard(require("d3-hsv"));
|
23 |
|
24 | var _curve = require("./curve.js");
|
25 |
|
26 | function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }
|
27 |
|
28 | function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
29 |
|
30 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
31 |
|
32 |
|
33 |
|
34 |
|
35 |
|
36 |
|
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 | function assign(dest, ...src) {
|
45 | for (let obj of src) {
|
46 | for (let prop in obj) {
|
47 | if (prop !== 'default') {
|
48 | dest[prop] = obj[prop];
|
49 | }
|
50 | }
|
51 | }
|
52 | }
|
53 |
|
54 | let interpolateJch = (start, end) => {
|
55 |
|
56 |
|
57 | const constant = x => () => x;
|
58 |
|
59 | const linear = (a, d) => t => a + t * d;
|
60 |
|
61 | const colorInterpolate = (a, b) => {
|
62 | const d = b - a;
|
63 | return d ? linear(a, d) : constant(isNaN(a) ? b : a);
|
64 | };
|
65 |
|
66 | start = _d.default.jch(start);
|
67 | end = _d.default.jch(end);
|
68 | const zero = Math.abs(start.h - end.h);
|
69 | const plus = Math.abs(start.h - (end.h + 360));
|
70 | const minus = Math.abs(start.h - (end.h - 360));
|
71 |
|
72 | if (plus < zero && plus < minus) {
|
73 | end.h += 360;
|
74 | }
|
75 |
|
76 | if (minus < zero && minus < plus) {
|
77 | end.h -= 360;
|
78 | }
|
79 |
|
80 | const startc = _d.default.hcl(start + '').c;
|
81 |
|
82 | const endc = _d.default.hcl(end + '').c;
|
83 |
|
84 | if (!startc) {
|
85 | start.h = end.h;
|
86 | }
|
87 |
|
88 | if (!endc) {
|
89 | end.h = start.h;
|
90 | }
|
91 |
|
92 | const J = colorInterpolate(start.J, end.J),
|
93 | C = colorInterpolate(start.C, end.C),
|
94 | h = colorInterpolate(start.h, end.h),
|
95 | opacity = colorInterpolate(start.opacity, end.opacity);
|
96 | return t => {
|
97 | start.J = J(t);
|
98 | start.C = C(t);
|
99 | start.h = h(t);
|
100 | start.opacity = opacity(t);
|
101 | return start + '';
|
102 | };
|
103 | };
|
104 |
|
105 | function smoothScale(ColorsArray, domains, space) {
|
106 | const points = space.channels.map(() => []);
|
107 | ColorsArray.forEach((color, i) => points.forEach((point, j) => point.push(domains[i], color[space.channels[j]])));
|
108 |
|
109 | if (space.name == "hcl") {
|
110 | const point = points[1];
|
111 |
|
112 | for (let i = 1; i < point.length; i += 2) {
|
113 | if (isNaN(point[i])) {
|
114 | point[i] = 0;
|
115 | }
|
116 | }
|
117 | }
|
118 |
|
119 | points.forEach(point => {
|
120 | const nans = [];
|
121 |
|
122 | for (let i = 1; i < point.length; i += 2) {
|
123 | if (isNaN(point[i])) {
|
124 | nans.push(i);
|
125 | } else {
|
126 | nans.forEach(j => point[j] = point[i]);
|
127 | nans.length = 0;
|
128 | break;
|
129 | }
|
130 | }
|
131 |
|
132 |
|
133 | if (nans.length) {
|
134 |
|
135 | const safeJChHue = _d.default.jch("#ccc").h;
|
136 |
|
137 | nans.forEach(j => point[j] = safeJChHue);
|
138 | }
|
139 |
|
140 | nans.length = 0;
|
141 |
|
142 | for (let i = point.length - 1; i > 0; i -= 2) {
|
143 | if (isNaN(point[i])) {
|
144 | nans.push(i);
|
145 | } else {
|
146 | nans.forEach(j => point[j] = point[i]);
|
147 | break;
|
148 | }
|
149 | }
|
150 |
|
151 |
|
152 | for (let i = 1; i < point.length; i += 2) {
|
153 | if (isNaN(point[i])) {
|
154 | point.splice(i - 1, 2);
|
155 | i -= 2;
|
156 | }
|
157 | }
|
158 |
|
159 |
|
160 | if (space.name in {
|
161 | hcl: 1,
|
162 | hsl: 1,
|
163 | hsluv: 1,
|
164 | hsv: 1,
|
165 | jch: 1
|
166 | }) {
|
167 | let prev = point[1];
|
168 | let addon = 0;
|
169 |
|
170 | for (let i = 3; i < point.length; i += 2) {
|
171 | const p = point[i] + addon;
|
172 | const zero = Math.abs(prev - p);
|
173 | const plus = Math.abs(prev - (p + 360));
|
174 | const minus = Math.abs(prev - (p - 360));
|
175 |
|
176 | if (plus < zero && plus < minus) {
|
177 | addon += 360;
|
178 | }
|
179 |
|
180 | if (minus < zero && minus < plus) {
|
181 | addon -= 360;
|
182 | }
|
183 |
|
184 | point[i] += addon;
|
185 | prev = point[i];
|
186 | }
|
187 | }
|
188 | });
|
189 | const prep = points.map(point => (0, _curve.catmullRom2bezier)(point).map(curve => (0, _curve.prepareCurve)(...curve)));
|
190 | return d => {
|
191 | const ch = prep.map(p => {
|
192 | for (let i = 0; i < p.length; i++) {
|
193 | const res = p[i](d);
|
194 |
|
195 | if (res != null) {
|
196 | return res;
|
197 | }
|
198 | }
|
199 | });
|
200 |
|
201 | if (space.name == 'jch' && ch[1] < 0) {
|
202 | ch[1] = 0;
|
203 | }
|
204 |
|
205 | return _d.default[space.name](...ch) + "";
|
206 | };
|
207 | }
|
208 |
|
209 |
|
210 | const colorSpaces = {
|
211 | CAM02: {
|
212 | name: 'jab',
|
213 | channels: ['J', 'a', 'b'],
|
214 | interpolator: _d.default.interpolateJab
|
215 | },
|
216 | CAM02p: {
|
217 | name: 'jch',
|
218 | channels: ['J', 'C', 'h'],
|
219 | interpolator: interpolateJch
|
220 | },
|
221 | LCH: {
|
222 | name: 'hcl',
|
223 | channels: ['h', 'c', 'l'],
|
224 | interpolator: _d.default.interpolateHcl,
|
225 | white: _d.default.hcl(NaN, 0, 100),
|
226 | black: _d.default.hcl(NaN, 0, 0)
|
227 | },
|
228 | LAB: {
|
229 | name: 'lab',
|
230 | channels: ['l', 'a', 'b'],
|
231 | interpolator: _d.default.interpolateLab
|
232 | },
|
233 | HSL: {
|
234 | name: 'hsl',
|
235 | channels: ['h', 's', 'l'],
|
236 | interpolator: _d.default.interpolateHsl
|
237 | },
|
238 | HSLuv: {
|
239 | name: 'hsluv',
|
240 | channels: ['l', 'u', 'v'],
|
241 | interpolator: _d.default.interpolateHsluv,
|
242 | white: _d3Hsluv.default.hsluv(NaN, NaN, 100),
|
243 | black: _d3Hsluv.default.hsluv(NaN, NaN, 0)
|
244 | },
|
245 | RGB: {
|
246 | name: 'rgb',
|
247 | channels: ['r', 'g', 'b'],
|
248 | interpolator: _d.default.interpolateRgb
|
249 | },
|
250 | HSV: {
|
251 | name: 'hsv',
|
252 | channels: ['h', 's', 'v'],
|
253 | interpolator: _d.default.interpolateHsv
|
254 | }
|
255 | };
|
256 |
|
257 | function cArray(c) {
|
258 | const color = _d3Hsluv.default.hsluv(c);
|
259 |
|
260 | const L = color.l;
|
261 | const U = color.u;
|
262 | const V = color.v;
|
263 | return [L, U, V];
|
264 | }
|
265 |
|
266 | function removeDuplicates(originalArray, prop) {
|
267 | var newArray = [];
|
268 | var lookupObject = {};
|
269 |
|
270 | for (var i in originalArray) {
|
271 | lookupObject[originalArray[i][prop]] = originalArray[i];
|
272 | }
|
273 |
|
274 | for (i in lookupObject) {
|
275 | newArray.push(lookupObject[i]);
|
276 | }
|
277 |
|
278 | return newArray;
|
279 | }
|
280 |
|
281 | function createScale({
|
282 | swatches,
|
283 | colorKeys,
|
284 | colorspace = 'LAB',
|
285 | shift = 1,
|
286 | fullScale = true,
|
287 | smooth = false
|
288 | } = {}) {
|
289 | const space = colorSpaces[colorspace];
|
290 |
|
291 | if (!space) {
|
292 | throw new Error(`Colorspace “${colorspace}” not supported`);
|
293 | }
|
294 |
|
295 | let domains = colorKeys.map(key => swatches - swatches * (_d3Hsluv.default.hsluv(key).v / 100)).sort((a, b) => a - b).concat(swatches);
|
296 | domains.unshift(0);
|
297 |
|
298 | let sqrtDomains = _d.default.scalePow().exponent(shift).domain([1, swatches]).range([1, swatches]);
|
299 |
|
300 | sqrtDomains = domains.map(d => {
|
301 | if (sqrtDomains(d) < 0) {
|
302 | return 0;
|
303 | }
|
304 |
|
305 | return sqrtDomains(d);
|
306 | });
|
307 |
|
308 | domains = sqrtDomains;
|
309 | let sortedColor = colorKeys
|
310 | .map((c, i) => {
|
311 | return {
|
312 | colorKeys: cArray(c),
|
313 | index: i
|
314 | };
|
315 | })
|
316 | .sort((c1, c2) => c2.colorKeys[2] - c1.colorKeys[2])
|
317 | .map(data => colorKeys[data.index]);
|
318 | let inverseSortedColor = colorKeys
|
319 | .map((c, i) => {
|
320 | return {
|
321 | colorKeys: cArray(c),
|
322 | index: i
|
323 | };
|
324 | })
|
325 | .sort((c1, c2) => c1.colorKeys[2] - c2.colorKeys[2])
|
326 | .map(data => colorKeys[data.index]);
|
327 | let ColorsArray = [];
|
328 | let scale;
|
329 |
|
330 | if (fullScale) {
|
331 | ColorsArray = [space.white || '#fff', ...sortedColor, space.black || '#000'];
|
332 | } else {
|
333 | ColorsArray = sortedColor;
|
334 | }
|
335 |
|
336 | const stringColors = ColorsArray;
|
337 | ColorsArray = ColorsArray.map(d => _d.default[space.name](d));
|
338 |
|
339 | if (space.name == 'hcl') {
|
340 |
|
341 | ColorsArray.forEach(c => c.c = isNaN(c.c) ? 0 : c.c);
|
342 | }
|
343 |
|
344 | if (space.name == 'jch') {
|
345 |
|
346 |
|
347 | for (let i = 0; i < stringColors.length; i++) {
|
348 | const color = _d.default.hcl(stringColors[i]);
|
349 |
|
350 | if (!color.c) {
|
351 | ColorsArray[i].h = NaN;
|
352 | }
|
353 | }
|
354 | }
|
355 |
|
356 | if (smooth) {
|
357 | scale = smoothScale(ColorsArray, domains, space);
|
358 | } else {
|
359 | scale = _d.default.scaleLinear().range(ColorsArray).domain(domains).interpolate(space.interpolator);
|
360 | }
|
361 |
|
362 | let Colors = _d.default.range(swatches).map(d => scale(d));
|
363 |
|
364 | let colors = Colors.filter(el => el != null);
|
365 |
|
366 | let colorsHex = [];
|
367 |
|
368 | for (let i = 0; i < colors.length; i++) {
|
369 | colorsHex.push(_d.default.rgb(colors[i]).formatHex());
|
370 | }
|
371 |
|
372 | return {
|
373 | colorKeys: colorKeys,
|
374 | colorspace: colorspace,
|
375 | shift: shift,
|
376 | colors: colors,
|
377 | scale: scale,
|
378 | colorsHex: colorsHex
|
379 | };
|
380 | }
|
381 |
|
382 | function generateBaseScale({
|
383 | colorKeys,
|
384 | colorspace = 'LAB',
|
385 | smooth
|
386 | } = {}) {
|
387 |
|
388 | let swatches = 1000;
|
389 | let scale = createScale({
|
390 | swatches: swatches,
|
391 | colorKeys: colorKeys,
|
392 | colorspace: colorspace,
|
393 | shift: 1,
|
394 | smooth: smooth
|
395 | });
|
396 | let newColors = scale.colorsHex;
|
397 | let colorObj = newColors
|
398 | .map((c, i) => {
|
399 | return {
|
400 | value: Math.round(cArray(c)[2]),
|
401 | index: i
|
402 | };
|
403 | });
|
404 | let filteredArr = removeDuplicates(colorObj, "value").map(data => newColors[data.index]);
|
405 | return filteredArr;
|
406 | }
|
407 |
|
408 | function generateContrastColors({
|
409 | colorKeys,
|
410 | base,
|
411 | ratios,
|
412 | colorspace = 'LAB',
|
413 | smooth = false
|
414 | } = {}) {
|
415 | if (!base) {
|
416 | throw new Error(`Base is undefined`);
|
417 | }
|
418 |
|
419 | if (!colorKeys) {
|
420 | throw new Error(`Color Keys are undefined`);
|
421 | }
|
422 |
|
423 | for (let i = 0; i < colorKeys.length; i++) {
|
424 | if (colorKeys[i].length < 6) {
|
425 | throw new Error('Color Key must be greater than 6 and include hash # if hex.');
|
426 | } else if (colorKeys[i].length == 6 && colorKeys[i].charAt(0) != 0) {
|
427 | throw new Error('Color Key missing hash #');
|
428 | }
|
429 | }
|
430 |
|
431 | if (!ratios) {
|
432 | throw new Error(`Ratios are undefined`);
|
433 | }
|
434 |
|
435 | let swatches = 3000;
|
436 | let scaleData = createScale({
|
437 | swatches: swatches,
|
438 | colorKeys: colorKeys,
|
439 | colorspace: colorspace,
|
440 | shift: 1,
|
441 | smooth: smooth
|
442 | });
|
443 | let baseV = _d3Hsluv.default.hsluv(base).v / 100;
|
444 |
|
445 | let Contrasts = _d.default.range(swatches).map(d => {
|
446 | let rgbArray = [_d.default.rgb(scaleData.scale(d)).r, _d.default.rgb(scaleData.scale(d)).g, _d.default.rgb(scaleData.scale(d)).b];
|
447 | let baseRgbArray = [_d.default.rgb(base).r, _d.default.rgb(base).g, _d.default.rgb(base).b];
|
448 | let ca = contrast(rgbArray, baseRgbArray, baseV).toFixed(2);
|
449 | return Number(ca);
|
450 | });
|
451 |
|
452 | let contrasts = Contrasts.filter(el => el != null);
|
453 | let newColors = [];
|
454 | ratios = ratios.map(Number);
|
455 |
|
456 | for (let i = 0; i < ratios.length; i++) {
|
457 | let r = binarySearch(contrasts, ratios[i], baseV);
|
458 | newColors.push(_d.default.rgb(scaleData.colors[r]).hex());
|
459 | }
|
460 |
|
461 | return newColors;
|
462 | }
|
463 |
|
464 | function luminance(r, g, b) {
|
465 | let a = [r, g, b].map(v => {
|
466 | v /= 255;
|
467 | return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
|
468 | });
|
469 | return a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722;
|
470 | }
|
471 |
|
472 | function contrast(color, base, baseV) {
|
473 | let colorLum = luminance(color[0], color[1], color[2]);
|
474 | let baseLum = luminance(base[0], base[1], base[2]);
|
475 | let cr1 = (colorLum + 0.05) / (baseLum + 0.05);
|
476 | let cr2 = (baseLum + 0.05) / (colorLum + 0.05);
|
477 |
|
478 | if (baseV < 0.5) {
|
479 | if (cr1 >= 1) {
|
480 | return cr1;
|
481 | } else {
|
482 | return cr2 * -1;
|
483 | }
|
484 |
|
485 | } else {
|
486 | if (cr1 < 1) {
|
487 | return cr2;
|
488 | } else {
|
489 | return cr1 * -1;
|
490 | }
|
491 |
|
492 | }
|
493 | }
|
494 |
|
495 | function minPositive(r) {
|
496 | if (!r) {
|
497 | throw new Error('Array undefined');
|
498 | }
|
499 |
|
500 | if (!Array.isArray(r)) {
|
501 | throw new Error('Passed object is not an array');
|
502 | }
|
503 |
|
504 | let arr = [];
|
505 |
|
506 | for (let i = 0; i < r.length; i++) {
|
507 | if (r[i] >= 1) {
|
508 | arr.push(r[i]);
|
509 | }
|
510 | }
|
511 |
|
512 | return Math.min(...arr);
|
513 | }
|
514 |
|
515 | function ratioName(r) {
|
516 | if (!r) {
|
517 | throw new Error('Ratios undefined');
|
518 | }
|
519 |
|
520 | r = r.sort(function (a, b) {
|
521 | return a - b;
|
522 | });
|
523 |
|
524 | let min = minPositive(r);
|
525 | let minIndex = r.indexOf(min);
|
526 | let nArr = [];
|
527 |
|
528 | let rNeg = r.slice(0, minIndex);
|
529 | let rPos = r.slice(minIndex, r.length);
|
530 |
|
531 | for (let i = 0; i < rNeg.length; i++) {
|
532 | let d = 1 / (rNeg.length + 1);
|
533 | let m = d * 100;
|
534 | let nVal = m * (i + 1);
|
535 | nArr.push(Number(nVal.toFixed()));
|
536 | }
|
537 |
|
538 |
|
539 | for (let i = 0; i < rPos.length; i++) {
|
540 | nArr.push((i + 1) * 100);
|
541 | }
|
542 |
|
543 | nArr.sort(function (a, b) {
|
544 | return a - b;
|
545 | });
|
546 |
|
547 | return nArr;
|
548 | }
|
549 |
|
550 | function generateAdaptiveTheme({
|
551 | colorScales,
|
552 | baseScale,
|
553 | brightness,
|
554 | contrast = 1
|
555 | }) {
|
556 | if (!baseScale) {
|
557 | throw new Error('baseScale is undefined');
|
558 | }
|
559 |
|
560 | let found = false;
|
561 |
|
562 | for (let i = 0; i < colorScales.length; i++) {
|
563 | if (colorScales[i].name !== baseScale) {
|
564 | found = true;
|
565 | }
|
566 | }
|
567 |
|
568 | if (found = false) {
|
569 | throw new Error('baseScale must match the name of a colorScales object');
|
570 | }
|
571 |
|
572 | if (!colorScales) {
|
573 | throw new Error('colorScales are undefined');
|
574 | }
|
575 |
|
576 | if (!Array.isArray(colorScales)) {
|
577 | throw new Error('colorScales must be an array of objects');
|
578 | }
|
579 |
|
580 | if (brightness === undefined) {
|
581 | return function (brightness, contrast) {
|
582 | return generateAdaptiveTheme({
|
583 | baseScale: baseScale,
|
584 | colorScales: colorScales,
|
585 | brightness: brightness,
|
586 | contrast: contrast
|
587 | });
|
588 | };
|
589 | } else {
|
590 |
|
591 | let baseIndex = colorScales.findIndex(x => x.name === baseScale);
|
592 | let baseKeys = colorScales[baseIndex].colorKeys;
|
593 | let baseMode = colorScales[baseIndex].colorspace;
|
594 | let smooth = colorScales[baseIndex].smooth;
|
595 |
|
596 | let bscale = generateBaseScale({
|
597 | colorKeys: baseKeys,
|
598 | colorspace: baseMode,
|
599 | smooth: smooth
|
600 | });
|
601 |
|
602 | let bval = bscale[brightness];
|
603 | let baseObj = {
|
604 | background: bval
|
605 | };
|
606 | let arr = [];
|
607 | arr.push(baseObj);
|
608 |
|
609 | for (let i = 0; i < colorScales.length; i++) {
|
610 | if (!colorScales[i].name) {
|
611 | throw new Error('Color missing name');
|
612 | }
|
613 |
|
614 | let name = colorScales[i].name;
|
615 | let ratios = colorScales[i].ratios;
|
616 | let smooth = colorScales[i].smooth;
|
617 | let newArr = [];
|
618 | let colorObj = {
|
619 | name: name,
|
620 | values: newArr
|
621 | };
|
622 | ratios = ratios.map(function (d) {
|
623 | let r;
|
624 |
|
625 | if (d > 1) {
|
626 | r = (d - 1) * contrast + 1;
|
627 | } else if (d < -1) {
|
628 | r = (d + 1) * contrast - 1;
|
629 | } else {
|
630 | r = 1;
|
631 | }
|
632 |
|
633 | return Number(r.toFixed(2));
|
634 | });
|
635 | let outputColors = generateContrastColors({
|
636 | colorKeys: colorScales[i].colorKeys,
|
637 | colorspace: colorScales[i].colorspace,
|
638 | ratios: ratios,
|
639 | base: bval,
|
640 | smooth: smooth
|
641 | });
|
642 |
|
643 | for (let i = 0; i < outputColors.length; i++) {
|
644 | let rVal = ratioName(ratios)[i];
|
645 | let n = name.concat(rVal);
|
646 | let obj = {
|
647 | name: n,
|
648 | contrast: ratios[i],
|
649 | value: outputColors[i]
|
650 | };
|
651 | newArr.push(obj);
|
652 | }
|
653 |
|
654 | arr.push(colorObj);
|
655 | }
|
656 |
|
657 | return arr;
|
658 | }
|
659 | }
|
660 |
|
661 |
|
662 |
|
663 | function binarySearch(list, value, baseLum) {
|
664 |
|
665 | let start = 0;
|
666 | let stop = list.length - 1;
|
667 | let middle = Math.floor((start + stop) / 2);
|
668 | let minContrast = Math.min(...list);
|
669 | let maxContrast = Math.max(...list);
|
670 |
|
671 | while (list[middle] !== value && start < stop) {
|
672 |
|
673 | if (baseLum > 0.5) {
|
674 |
|
675 | if (value < list[middle]) {
|
676 | stop = middle - 1;
|
677 | } else {
|
678 | start = middle + 1;
|
679 | }
|
680 | } else {
|
681 |
|
682 | if (value > list[middle]) {
|
683 | stop = middle - 1;
|
684 | } else {
|
685 | start = middle + 1;
|
686 | }
|
687 | }
|
688 |
|
689 |
|
690 | middle = Math.floor((start + stop) / 2);
|
691 | }
|
692 |
|
693 |
|
694 | let closest = list.reduce((prev, curr) => curr > value ? curr : prev);
|
695 |
|
696 | return list[middle] == !value ? closest : middle;
|
697 | } |
\ | No newline at end of file |