- Jump To … +
  ./source/core/animation-loop.js ./source/core/display-cycle.js ./source/core/document-root-elements.js ./source/core/document.js ./source/core/events.js ./source/core/init.js ./source/core/library.js ./source/core/random-seed.js ./source/core/shared-vars.js ./source/core/snippets.js ./source/core/system-flags.js ./source/core/user-interaction.js ./source/core/utilities.js ./source/factory/action.js ./source/factory/anchor.js ./source/factory/animation.js ./source/factory/array-pool.js ./source/factory/bezier.js ./source/factory/block.js ./source/factory/canvas.js ./source/factory/cell-fragment.js ./source/factory/cell.js ./source/factory/cog.js ./source/factory/color.js ./source/factory/conic-gradient.js ./source/factory/coordinate.js ./source/factory/crescent.js ./source/factory/drag-zone.js ./source/factory/element.js ./source/factory/emitter.js ./source/factory/filter-engine-bluenoise-data.js ./source/factory/filter-engine.js ./source/factory/filter.js ./source/factory/font-attributes.js ./source/factory/gradient.js ./source/factory/grid.js ./source/factory/group.js ./source/factory/image-asset.js ./source/factory/keyboard-zone.js ./source/factory/line-spiral.js ./source/factory/line.js ./source/factory/loom.js ./source/factory/mesh.js ./source/factory/net.js ./source/factory/noise-asset.js ./source/factory/observe-update.js ./source/factory/oval.js ./source/factory/palette.js ./source/factory/particle-force.js ./source/factory/particle-history.js ./source/factory/particle-spring.js ./source/factory/particle-world.js ./source/factory/particle.js ./source/factory/pattern.js ./source/factory/phrase.js ./source/factory/picture.js ./source/factory/polygon.js ./source/factory/polyline.js ./source/factory/quadratic.js ./source/factory/quaternion.js ./source/factory/radial-gradient.js ./source/factory/raw-asset.js ./source/factory/reaction-diffusion-asset.js ./source/factory/rectangle.js ./source/factory/render-animation.js ./source/factory/shape.js ./source/factory/spiral.js ./source/factory/sprite-asset.js ./source/factory/stack.js ./source/factory/star.js ./source/factory/state.js ./source/factory/tetragon.js ./source/factory/ticker.js ./source/factory/tracer.js ./source/factory/tween.js ./source/factory/unstacked-element.js ./source/factory/vector.js ./source/factory/video-asset.js ./source/factory/wheel.js ./source/mixin/anchor.js ./source/mixin/asset-advanced-functionality.js ./source/mixin/asset-consumer.js ./source/mixin/asset.js ./source/mixin/base.js ./source/mixin/cascade.js ./source/mixin/cell-key-functions.js ./source/mixin/delta.js ./source/mixin/display-shape.js ./source/mixin/dom.js ./source/mixin/entity.js ./source/mixin/filter.js ./source/mixin/mimic.js ./source/mixin/path.js ./source/mixin/pattern.js ./source/mixin/pivot.js ./source/mixin/position.js ./source/mixin/shape-basic.js ./source/mixin/shape-curve.js ./source/mixin/shape-path-calculation.js ./source/mixin/styles.js ./source/mixin/tween.js

- §

  # Core utility functions

  A ragtag collection of helper functions which other modules can import and use

      import { _cos, _create, _entries, _floor, _freeze, _isArray, _pi, _pow, _random, _sin, _sqrt, $CANVAS_ELEMENT, $OBJECT, BOOLEAN, BOTTOM, CENTER, FUNCTION, LEFT, MS, PC, PC0, PC100, PC50, RIGHT, T_QUATERNION, TOP, UNDEF } from './shared-vars.js';

- §

  #### Functions

- §

  addStrings adds the two arguments together and returns a percentage string value if either of the values was a string; or a sum of the two numbers.

  Examples:

      addStrings(20, 40);
      -> 60

      addStrings('20%', 40);
      -> '60%'

      addStrings(20, '40%');
      -> '60%'

      addStrings('20%', '40%');
      -> '60%'

      addStrings('center', 3);
      -> '53%'

      export const addStrings = (current, delta) => {

          if ((delta != null)) {

- §

  Correct for labels

              if (LEFT == current || TOP == current) current = PC0;
              else if (RIGHT == current || BOTTOM == current) current = PC100;
              else if (CENTER == current) current = PC50;

              const stringFlag = (current.substring || delta.substring) ? true : false;

              if (isa_number(current)) current += (isa_number(delta) ? delta : parseFloat(delta));
              else current = parseFloat(current) + (isa_number(delta) ? delta : parseFloat(delta));

              return (stringFlag) ? current + PC : current;
          }
          return current;
      };

- §

  constrain - clamps a value between a minimum and maximum value

      export const constrain = function (val, min, max) {

          if (val < min) return min;
          if (val > max) return max;
          return val;
      };

- §

  convertTime converts a time value into its component string suffix and (millisecond) number value properties; returns an array

  Examples:

      convertTime(5000);
      -> ['ms', 5000]

      convertTime('50%');
      -> ['%', 50]

      convertTime('5000ms');
      -> ['ms', 5000]

      convertTime('5s');
      -> ['ms', 5000]

      export const convertTime = (item) => {

          if (isa_number(item)) return [MS, item];

          if (item.substring) {

              const a = item.match(/^\d+\.?\d*(\D*)/);
              let timeUnit = (a[1].toLowerCase) ? a[1].toLowerCase() : MS;

              let timeValue = parseFloat(item);

              if (!isNaN(timeValue)) {

                  switch (timeUnit) {

                      case 's':
                          timeValue *= 1000;
                          break;

                      case '%':
                          break;

                      default:
                          timeUnit = MS;
                  }

                  return [timeUnit, timeValue];
              }
              return [MS, 0];
          }
      };

- §

  correctAngle makes sure any degree-based angle is in the range `0-360`

      export const correctAngle = (item) => {

          if (!item.toFixed || isNaN(item)) return 0;

          item = item % 360;

          if (item < 0) item += 360;

          return item;
      };

- §

  correctForZero checks and corrects for minor deviations from zero (eNumbers)

      export const correctForZero = (item) => {

          if (item.toFixed && !isNaN(item)) {

              if (item < -0.000001) return item;
              if (item > 0.000001) return item;
          }
          return 0;
      };

- §

  λ functions helps us avoid errors when invoking a function attribute settable by the coder

      export const λnull = () => {};
      export const λfirstArg = function (a) { return a; };
      export const λthis = function () { return this; };
      export const λpromise = () => Promise.resolve(true);
      export const Ωempty = _freeze({});

- §

  generateUuid is a simple (crude) uuid generator http://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript (imported 2017-07-08)

      export const generateUuid = () => {

          function s4() {

              return _floor((1 + _random()) * 0x10000).toString(16).substring(1);
          }

          return `${s4()}${s4()}-${s4()}-${s4()}-${s4()}-${s4()}${s4()}${s4()}`;
      };

- §

  generateUniqueString is a simple random String generator https://gist.github.com/SimonHoiberg/ad2710c8626c5a74cddd8f6385795cc0 (imported 2020-11-22)

      export const generateUniqueString = () => {

          return performance.now().toString(36) + _random().toString(36).substr(2);
      };

- §

  interpolate clamp a value between a maximum and minimum value

      export const interpolate = function (val, min, max) {

          return min + val * (max - min);
      };

- §

  isa_boolean checks to make sure the argument is a boolean

      export const isa_boolean = item => (typeof item == BOOLEAN) ? true : false;

- §

  isa_canvas checks to make sure the argument is a DOM <canvas> element

      export const isa_canvas = item => (Object.prototype.toString.call(item) == $CANVAS_ELEMENT) ? true : false;

- §

  isa_dom checks to make sure the argument is a DOM element of some sort

      export const isa_dom = item => (item && item.querySelector && item.dispatchEvent) ? true : false;

- §

  isa_fn checks to make sure the argument is a JavaScript function object

      export const isa_fn = item => (typeof item == FUNCTION) ? true : false;

- §

  isa_number checks to make sure the argument is true number (excluding NaN)

      export const isa_number = item => (item != null && item.toFixed && !Number.isNaN(item)) ? true : false;

- §

  isa_obj checks to make sure the argument is a JavaScript Object

      export const isa_obj = item => (Object.prototype.toString.call(item) == $OBJECT) ? true : false;

- §

  isa_quaternion checks to make sure the argument is a Scrawl-canvas Quaternion object

      export const isa_quaternion = item => (item && item.type && item.type == T_QUATERNION) ? true : false;

- §

  mergeInto takes two objects and merges the attributes of one into the other. This function mutates the ‘original’ object rather than generating a third, new onject

  Example:

      var original = { name: 'Peter', age: 42, job: 'lawyer' };
      var additional = { age: 32, job: 'coder', pet: 'cat' };
      scrawl.utils.mergeInto(original, additional);

      -> { name: 'Peter', age: 42, job: 'lawyer', pet: 'cat' }

      export const mergeInto = (original, additional) => {

          if (isa_obj(original) && isa_obj(additional)) {

              for (const key in additional) {

      /* eslint-disable-next-line */
                  if (additional.hasOwnProperty(key) && typeof original[key] == UNDEF) {

                      original[key] = additional[key];
                  }
              }
          }
          return original;
      };

- §

  mergeOver takes two objects and writes the attributes of one over the other. This function mutates the ‘original’ object rather than generating a third, new onject

  Example:

      var original = { name: 'Peter', age: 42, job: 'lawyer' };
      var additional = { age: 32, job: 'coder', pet: 'cat' };
      scrawl.utils.mergeOver(original, additional);

      -> { name: 'Peter', age: 32, job: 'coder', pet: 'cat' }

      export const mergeOver = (original, additional) => {

          if (isa_obj(original) && isa_obj(additional)) {

              for (const key in additional) {

      /* eslint-disable-next-line */
                  if (additional.hasOwnProperty(key)) original[key] = additional[key];
              }
          }
          return original;
      };

- §

  mergeDiscard iterates over the additional object to perform a mergeOver operation, and also removing attributes from the original object where they have been set to null in the additional object

  Example:

      var original = { name: 'Peter', age: 42, job: 'lawyer' };
      var additional = { age: 32, job: null, pet: 'cat' };
      scrawl.utils.mergeOver(original, additional);

      -> { name: 'Peter', age: 32, pet: 'cat' }

      export const mergeDiscard = (original, additional) => {

          if (isa_obj(original) && isa_obj(additional)) {

              _entries(additional).forEach(([key, val]) => {

                  if (val === null) delete original[key];
                  else original[key] = additional[key];
              });
          }
          return original;
      };

- §

  pushUnique adds a value to the end of an array, if that value is not already present in the array. This function mutates the array

  Example:

      var myarray = ['apple', 'orange'];
      scrawl.utils.pushUnique(myarray, 'apple');
      -> ['apple', 'orange']

      scrawl.utils.pushUnique(myarray, 'banana');
      -> ['apple', 'orange', 'banana']

      export const pushUnique = (myArray, potentialMember) => {

          if (_isArray(myArray)) {

              if (_isArray(potentialMember)) {

                  potentialMember.forEach(item => pushUnique(myArray, item));
              }
              else {

                  if (!myArray.includes(potentialMember)) myArray.push(potentialMember);
              }
          }
          return myArray;
      };

- §

  removeItem removes a value from an array. This function mutates the array

  Example:

      var myarray = ['apple', 'orange'];
      scrawl.utils.removeItem(myarray, 'banana');
      -> ['apple', 'orange']

      scrawl.utils.removeItem(myarray, 'apple');
      -> ['orange']

      export const removeItem = (myArray, unwantedMember) => {

          if (_isArray(myArray)) {

              const index = myArray.indexOf(unwantedMember);

              if (index >= 0) myArray.splice(index, 1);

          }
          return myArray;
      };

- §

  xt checks to see if argument exists (is not ‘undefined’)

      export const xt = item => (typeof item == UNDEF) ? false : true;

- §

  xta checks to make sure that all the arguments supplied to the function exist (none are ‘undefined’)

      export const xta = (...args) => args.every(item => typeof item != UNDEF);

- §

  xtGet returns the first existing (not ‘undefined’) argument supplied to the function

      export const xtGet = (...args) => args.find(item => typeof item != UNDEF);

- §

  xto checks to make sure that at least one of the arguments supplied to the function exists (is not ‘undefined’)

      export const xto = (...args) => (args.find(item => typeof item != UNDEF)) ? true : false;

- §

  ##### Easing engines

  `easeEngines` - an object in which keys define various easing functions

      export const easeEngines = {

- §

  Legacy easings

          out: (t) => 1 - _cos((t * _pi) / 2),
          in: (t) => _sin((t * _pi) / 2),
          easeIn: (t) => {

              const temp = 1 - t;
              return 1 - (temp * temp);
          },
          easeIn3: (t) => {

              const temp = 1 - t;
              return 1 - (temp * temp * temp);
          },
          easeIn4: (t) => {

              const temp = 1 - t;
              return 1 - (temp * temp * temp * temp);
          },
          easeIn5: (t) => {

              const temp = 1 - t;
              return 1 - (temp * temp * temp * temp * temp);
          },
          easeOutIn: (t) => (t < 0.5) ? 2 * t * t : 1 - _pow(-2 * t + 2, 2) / 2,
          easeOutIn3: (t) => (t < 0.5) ? 4 * t * t * t : 1 - _pow(-2 * t + 2, 3) / 2,
          easeOutIn4: (t) => (t < 0.5) ? 8 * t * t * t * t : 1 - _pow(-2 * t + 2, 4) / 2,
          easeOutIn5: (t) => (t < 0.5) ? 16 * t * t * t * t * t : 1 - _pow(-2 * t + 2, 5) / 2,

          easeInOut: (t) => {

              const tin = 0.5 - t,
                  tout = t - 0.5;

              if (t < 0.5) return 0.5 + (-2 * tin * tin);
              return 0.5 + _pow(2 * tout, 2) / 2;
          },
          easeInOut3: (t) => {

              const tin = 0.5 - t,
                  tout = t - 0.5;

              if (t < 0.5) return 0.5 + (-4 * tin * tin * tin);
              return 0.5 + _pow(2 * tout, 3) / 2;
          },
          easeInOut4: (t) => {

              const tin = 0.5 - t,
                  tout = t - 0.5;

              if (t < 0.5) return 0.5 + (-8 * tin * tin * tin * tin);
              return 0.5 + _pow(2 * tout, 4) / 2;
          },
          easeInOut5: (t) => {

              const tin = 0.5 - t,
                  tout = t - 0.5;

              if (t < 0.5) return 0.5 + (-16 * tin * tin * tin * tin * tin);
              return 0.5 + _pow(2 * tout, 5) / 2;
          },

          easeOut: (t) => t * t,
          easeOut3: (t) => t * t * t,
          easeOut4: (t) => t * t * t * t,
          easeOut5: (t) => t * t * t * t * t,

          none: (val) => val,
          linear: (val) => val,

- §

  Noise functionality easing engines

          cosine: (t) => .5 * (1 + _cos((1 - t) * _pi)),
          hermite: (t) => t * t * (-t * 2 + 3),
          quintic: (t) => t * t * t * (t * (t * 6 - 15) + 10),

- §

  The following easing variations come from the easings.net web page

  - Note: the naming convention for easing is different in Scrawl-canvas. Easing out implies a speeding up, while easing in implies a slowing down. Think of a train easing into a station, and then easing out of it again as it continues its journey.

          easeOutSine: (t) => 1 - _cos((t * _pi) / 2),
          easeInSine: (t) => _sin((t * _pi) / 2),
          easeOutInSine: (t) => -(_cos(_pi * t) - 1) / 2,

          easeOutQuad: (t) => t * t,
          easeInQuad: (t) => 1 - ((1 - t) * (1 - t)),
          easeOutInQuad: (t) => (t < 0.5) ? 2 * t * t : 1 - _pow(-2 * t + 2, 2) / 2,

          easeOutCubic: (t) => t * t * t,
          easeInCubic: (t) => 1 - _pow(1 - t, 3),
          easeOutInCubic: (t) => (t < 0.5) ? 4 * t * t * t : 1 - _pow(-2 * t + 2, 3) / 2,

          easeOutQuart: (t) => t * t * t * t,
          easeInQuart: (t) => 1 - _pow(1 - t, 4),
          easeOutInQuart: (t) => (t < 0.5) ? 8 * t * t * t * t : 1 - _pow(-2 * t + 2, 4) / 2,

          easeOutQuint: (t) => t * t * t * t * t,
          easeInQuint: (t) => 1 - _pow(1 - t, 5),
          easeOutInQuint: (t) => (t < 0.5) ? 16 * t * t * t * t * t : 1 - _pow(-2 * t + 2, 5) / 2,

          easeOutExpo: (t) => (t === 0) ? 0 : _pow(2, 10 * t - 10),
          easeInExpo: (t) => (t === 1) ? 1 : 1 - _pow(2, -10 * t),
          easeOutInExpo: (t) => {
              if (t === 0 || t === 1) return t;
              return t < 0.5 ? _pow(2, 20 * t - 10) / 2 : (2 - _pow(2, -20 * t + 10)) / 2;
          },

          easeOutCirc: (t) => 1 - _sqrt(1 - _pow(t, 2)),
          easeInCirc: (t) => _sqrt(1 - _pow(t - 1, 2)),
          easeOutInCirc: (t) => {

              if (t < 0.5) return (1 - _sqrt(1 - _pow(2 * t, 2))) / 2;
              return (_sqrt(1 - _pow(-2 * t + 2, 2)) + 1) / 2;
          },

          easeOutBack: (t) => (2.70158 * t * t * t) - (1.70158 * t * t),
          easeInBack: (t) => 1 + (2.70158 * _pow(t - 1, 3)) + (1.70158 * _pow(t - 1, 2)),
          easeOutInBack: (t) => {

              const c1 = 1.70158, c2 = c1 * 1.525;
              if (t < 0.5) return (_pow(2 * t, 2) * ((c2 + 1) * 2 * t - c2)) / 2;
              return (_pow(2 * t - 2, 2) * ((c2 + 1) * (t * 2 - 2) + c2) + 2) / 2;
          },

          easeOutElastic: (t) => {

              const c4 = (2 * _pi) / 3;
              if (t === 0 || t === 1) return t;
              return -_pow(2, 10 * t - 10) * _sin((t * 10 - 10.75) * c4);
          },
          easeInElastic: (t) => {

              const c4 = (2 * _pi) / 3;
              if (t === 0 || t === 1) return t;
              return _pow(2, -10 * t) * _sin((t * 10 - 0.75) * c4) + 1;
          },
          easeOutInElastic: (t) => {

              const c5 = (2 * _pi) / 4.5;
              if (t === 0 || t === 1) return t;
              if (t < 0.5) return -(_pow(2, 20 * t - 10) * _sin((20 * t - 11.125) * c5)) / 2;
              return (_pow(2, -20 * t + 10) * _sin((20 * t - 11.125) * c5)) / 2 + 1;
          },

          easeOutBounce: (t) => {

              t = 1 - t;

              const n1 = 7.5625,
                  d1 = 2.75;

              if (t < 1 / d1) return 1 - (n1 * t * t);
              if (t < 2 / d1) return 1 - (n1 * (t -= 1.5 / d1) * t + 0.75);
              if (t < 2.5 / d1) return 1 - (n1 * (t -= 2.25 / d1) * t + 0.9375);
              return 1 - (n1 * (t -= 2.625 / d1) * t + 0.984375);
          },

          easeInBounce: (t) => {

              const n1 = 7.5625,
                  d1 = 2.75;

              if (t < 1 / d1) return n1 * t * t;
              if (t < 2 / d1) return n1 * (t -= 1.5 / d1) * t + 0.75;
              if (t < 2.5 / d1) return n1 * (t -= 2.25 / d1) * t + 0.9375;
              return n1 * (t -= 2.625 / d1) * t + 0.984375;
          },

- §

  This is wrong

          easeOutInBounce: (t) => {

              const n1 = 7.5625, d1 = 2.75;
              let res;

              if (t < 0.5) {
                  t = 1 - 2 * t;
                  if (t < 1 / d1) res = n1 * t * t;
                  else if (t < 2 / d1) res = n1 * (t -= 1.5 / d1) * t + 0.75;
                  else if (t < 2.5 / d1) res = n1 * (t -= 2.25 / d1) * t + 0.9375;
                  else res = n1 * (t -= 2.625 / d1) * t + 0.984375;
                  return (1 - res) / 2;
              }
              else {
                  t = 2 * t - 1;
                  if (t < 1 / d1) res = n1 * t * t;
                  else if (t < 2 / d1) res = n1 * (t -= 1.5 / d1) * t + 0.75;
                  else if (t < 2.5 / d1) res = n1 * (t -= 2.25 / d1) * t + 0.9375;
                  else res = n1 * (t -= 2.625 / d1) * t + 0.984375;
                  return (1 + res) / 2;
              }
          },
      };

      export const getArrayType = someObject => someObject && someObject.constructor && someObject.constructor.name && someObject.constructor.name || null;

- §

  Code to detect browser from Code Box: https://code-boxx.com/detect-browser-with-javascript/

  - Ideally we need to get rid of the need for this functionality. Currently used in `/core/events.js` only

      export const detectBrowser = function () {

          const result = [];

      /* eslint-disable-next-line */
          if ((!!window.opr && !!opr.addons) || !!window.opera || navigator.userAgent.includes(' OPR/')) result.push('old-opera');

          if (typeof InstallTrigger != UNDEF) result.push('firefox');

      /* eslint-disable-next-line */
          else if (/constructor/i.test(window.HTMLElement) || (function (p) { return p.toString() == "[object SafariRemoteNotification]"; })(!window['safari'] || (typeof safari != UNDEF && safari.pushNotification))) result.push('safari');

      /* eslint-disable-next-line */
          if (/*@cc_on!@*/false || !!document.documentMode) result.push('internet-explorer');

          if (!result.includes('internet-explorer') && !!window.StyleMedia) result.push('edge');

          if(window.chrome) result.push('chrome');

          if((result.includes('chrome') || result.includes('old-opera')) && !!window.CSS) result.push('blink');

          return result;
      };

- §

  Create an Object prototype

      export const doCreate = () => _create(Object.prototype);