All files interpolate.js

98.08% Statements 51/52
78.05% Branches 32/41
100% Functions 16/16
97.62% Lines 41/42
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 853x 3x         3x 27x       23x 18x                 17x       17x 30x 30x 30x 30x         56x     15x 5x 5x   11x 8x 8x     8x 8x 1x 7x 1x   13x 6x 6x 6x       8x         14x         22x 20x 20x 15x 15x 15x   5x 3x 3x 8x       11x  
const STATIC_VALUES = ['auto'];
const SUPPORTED_UNITS = [
  'em', 'ex', 'rem', '%', 'px', 'vh', 'vw', 'vmin', 'vmax',
];
 
const unitPattern =
  new RegExp(`^(\\d+)(?:\\.\\d+)?(${
    SUPPORTED_UNITS.map(u => `(${u})`).join('|')
  })?$`);
 
function transform(node) {
  if (typeof node !== 'object') return node;
  return Object.keys(node).reduce((h, key) => (
    Object.assign({}, h, {
      [key]: isGeneric(node[key])
        ? transform(node[key])
        : makeResolver(node[key]) })
  ), {});
}
 
function isGeneric(node) {
  return typeof node !== 'object' || !Object.keys(partition(node)[1]).length;
}
 
function partition(node) {
  return Object.keys(node).reduce((a, key) => {
    const r = [];
    r[0] = a[0];
    r[1] = a[1];
    r[isNumeric(key) ? 1 : 0][key] = node[key]; return r;
  }, [{}, {}]);
}
 
function isNumeric(s) {
  return !isNaN(parseFloat(s)) && isFinite(s);
}
 
function makeResolver(node) {
  const [generics, dynamics] = partition(node);
  const doMerge = (typeof dynamics[Object.keys(dynamics)[0]] === 'object');
  const bounds =
    Object.keys(dynamics).map(k => parseInt(k, 10)).sort((a, b) => a - b);
  return (v = 0) => {
    Iif (dynamics[v]) {
      return doMerge ? Object.assign({}, generics, dynamics[v]) : dynamics[v];
    }
    let interpolated;
    if (v < bounds[0]) {
      interpolated = dynamics[bounds[0]];
    } else if (v > bounds[bounds.length - 1]) {
      interpolated = dynamics[bounds[bounds.length - 1]];
    } else {
      const upperBound = bounds.find(b => b > v);
      const lowerBound = bounds[bounds.indexOf(upperBound) - 1];
      const delta = (v - lowerBound) / (upperBound - lowerBound);
      interpolated = doMerge
        ? interpolate(dynamics[lowerBound], dynamics[upperBound], delta)
        : interpolateValues(dynamics[lowerBound], dynamics[upperBound], delta);
    }
    return doMerge ? Object.assign({}, generics, interpolated) : interpolated;
  };
}
 
function interpolate(a, b, x) {
  return Object.keys(a).reduce((h, key) =>
    Object.assign({}, h, { [key]: interpolateValues(a[key], b[key], x) }), {});
}
 
function interpolateValues(a, b, x) {
  if (isNumeric(a) && isNumeric(b)) return a + ((b - a) * x);
  const unitMatch = a.match(unitPattern);
  if (unitMatch) {
    const unit = unitMatch[2] || b.match(unitPattern)[2] || '';
    const aFloat = parseFloat(a);
    return (aFloat + ((parseFloat(b) - aFloat) * x)) + unit;
  }
  if (STATIC_VALUES.indexOf(a) !== -1) return a;
  const bTokens = b.split(/\s+/);
  return a.split(/\s+/)
          .map((t, i) => interpolateValues(t, bTokens[i], x))
          .join(' ');
}
 
export default node => transform({ t: node }).t;