PimpTrizkit / PJs

Pimped Javascript - Just a library of my javascript functions, or shims. They are generally built for speed, size, versatility, and portability (copy and paste-able). Readability will be sacrificed. Because they are optimized for usage right out-of-the-box.
GNU Lesser General Public License v3.0
207 stars 25 forks source link

unobfuscated pSBC #7

Open nodingneu opened 2 years ago

nodingneu commented 2 years ago

Do you have an unobfuscated pSBC.js? It would make it easier to separate pSBCr from pSBC as a pure function and use this code in Node.js ES6+ modules too.

PimpTrizkit commented 2 years ago

Actually, no. I wrote this function exactly the way it is, even through the several versions. (I often code for just myself and use single letter identifiers and recycle them.) From the beginning it started out small, tight, and super fast.. a quick-and-easy copy-and-paste solution for the no-brainer masses, lol.. Because that was part of my end goal when making it, namely speed and size (check out the 2-liner speed demon versions for the ultimate in size and speed). This obfuscation was not intentional, sorry to anyone who cares to read it. Anyhow.... moving forward. Seeing that this is not the first request for pSBCr to be moved outside pSBC... I will provide this separated version. (Honestly, its not hard to do. Just cut and past out the pSBCr and get rid of the if statement.) When I made this function, I actually didn't feel that people would be using pSBCr and when assuming this, I instead better liked the idea of having the whole thing as only one function to copy-and-paste for the masses. Thank you for your comments! Further thinking, I don't actually make websites often and I am not very familiar with Node.js... Other than wanting pSBCr separate from pSBC; I don't understand why this code can't be used in Node.js modules. Please comment.

Thanks PT

PimpTrizkit commented 2 years ago

Ok, apparently I already made this change in the < > Code tab but I didn't add the change to the Wiki (nor the test html). So, I just fixed those up as well. Did you not see that pSBCr had already been separated out in the master < > Code? Or am I missing something here? lol

PT

samschurter commented 1 year ago

@nodingneu We've been using the "obfuscated" pSBC in ES6 modules for a while and it works fine. But I was curious and de-compiled all the ternaries to see what was going on, and got this result. I don't guarantee that it actually still works as before (the performance is almost certainly slightly slower), but it still passes all the tests.

/**
 * Input colors as rgb, rgba, or 3, 4, 6, or 8 digit hex. Input a single color and a +/- percentage to lighten or
 * darken the color. Input two colors to blend them together (percentage sets how much of the second color is added).
 * Blending mode defaults to log, but can be set to linear with the 4th parameter.
 * @param {number} percentage percentage to lighten/darken OR percent to blend towards c1
 * @param {string} color1 color to blend from. Can be hex, rgb, rgba
 * @param {string} color2 color to blend towards. Can be hex, rgb, rgba
 * @param {boolean} useLinear Blending mode: defaults false for log scale, true for linear scale
 * @return {string}
 */
const pSBC = (percentage, color1, color2, useLinear) => {
  let rOut;
  let gOut;
  let bOut;
  let toColor;
  let rgbaOutput;
  const color2IsStr = typeof(color2) == 'string';

  if ( typeof(percentage) != 'number' || percentage < -1 || percentage > 1) return null;
  if ( typeof(color1) != 'string' || (color1[0] != 'r' && color1[0] != '#') || (color2 && !color2IsStr)) return null;

  rgbaOutput = color1.length > 9;
  if (color2IsStr) {
    if (color2.length > 9) {
      rgbaOutput = true;
    } else {
      if (color2 == 'c') {
        rgbaOutput = !rgbaOutput;
      } else {
        rgbaOutput = false;
      }
    }
  }

  const fromColor = pSBC.pSBCr(color1);
  const isDarken = percentage < 0;
  toColor = {r: 255, g: 255, b: 255, a: -1};
  if (color2 && (color2 != 'c')) {
    toColor = pSBC.pSBCr(color2);
  } else if (isDarken) {
    toColor = {r: 0, g: 0, b: 0, a: -1};
  }
  if (isDarken) {
    percentage = -percentage;
  }

  const inversePct = 1 - percentage;

  if (!fromColor||!toColor) return null;

  const linCalc = (from, to) => Math.round(inversePct * from + percentage * to);
  const logCalc = (from, to) => Math.round((inversePct * from ** 2 + percentage * to ** 2) ** 0.5);

  if (useLinear) {
    rOut = linCalc(fromColor.r, toColor.r);
    gOut = linCalc(fromColor.g, toColor.g);
    bOut = linCalc(fromColor.b, toColor.b);
  } else {
    rOut = logCalc(fromColor.r, toColor.r);
    gOut = logCalc(fromColor.g, toColor.g);
    bOut = logCalc(fromColor.b, toColor.b);
  }

  let fromAlpha = fromColor.a;
  const toColorAlpha = toColor.a;
  const hasAlpha = (fromAlpha >= 0) || (toColorAlpha >= 0);
  if (hasAlpha) {
    if (fromAlpha < 0) {
      fromAlpha = toColorAlpha;
    } else if (toColorAlpha >= 0) {
      fromAlpha = (fromAlpha * inversePct) + (toColorAlpha * percentage);
    }
  } else {
    fromAlpha = 0;
  }

  if (rgbaOutput) {
    const aStr = hasAlpha ? 'a' : '';
    const aNum = hasAlpha ? `,${Math.round(fromAlpha * 1000) / 1000}` : '';
    return `rgb${aStr}(${rOut},${gOut},${bOut}${aNum})`;
  } else {
    const base = 4294967296;
    const rPlace = rOut * 16777216;
    const gPlace = gOut * 65536;
    const bPlace = bOut * 256;
    const aPlace = hasAlpha ? Math.round(fromAlpha * 255) : 0;
    return '#' + (base + rPlace + gPlace + bPlace + aPlace).toString(16).slice(1, hasAlpha ? undefined : -2);
  }
};

/**
 * Input colors as rgb, rgba, or 3, 4, 6, or 8 digit hex. Rip to an object with r, g, b, a properties.
 * @param {string} d color string to rip
 * @return {{r: number, g: number, b: number, a: number}}
 */
pSBC.pSBCr = (d) => {
  const n = d.length;
  const x = {};
  if (n > 9) {
    const parts = d.split(',');
    const [r, g, b, a] = parts;
    if (parts.length < 3 || parts.length > 4) return null;
    x.r = parseInt(r[3] == 'a' ? r.slice(5) : r.slice(4));
    x.g = parseInt(g);
    x.b = parseInt(b);
    x.a = a ? parseFloat(a) : -1;
    return x;
  }
  if (n == 8 || n == 6 || n < 4) return null;
  if (n < 6) {
    d = '#' + d[1] + d[1] + d[2] + d[2] + d[3] + d[3] + (n > 4 ? d[4] + d[4] : '');
  }
  d = parseInt(d.slice(1), 16);
  if (n == 9 || n == 5) {
    x.r = d >> 24 & 255;
    x.g = d >> 16 & 255;
    x.b = d >> 8 & 255;
    x.a = Math.round((d & 255) / 0.255) / 1000;
  } else {
    x.r = d >> 16;
    x.g = d >> 8 & 255;
    x.b = d & 255;
    x.a = -1;
  }
  return x;
};
NotReeceHarris commented 2 months ago

Why do you have to make it unreadable, it doesn't make you look like a cool programmer it makes you look like a nob.

*It also created potential security holes since we don't know whats happening in the code, for example the xz attack