All Downloads are FREE. Search and download functionalities are using the official Maven repository.

package.src.animations.shared.js Maven / Gradle / Ivy

import { isString, minErr, extend } from "../shared/utils.js";
import { JQLite } from "../shared/jqlite/jqlite.js";
import { ASTType } from "../core/parse/ast-type.js";

export const ADD_CLASS_SUFFIX = "-add";
export const REMOVE_CLASS_SUFFIX = "-remove";
export const EVENT_CLASS_PREFIX = "ng-";
export const ACTIVE_CLASS_SUFFIX = "-active";
export const PREPARE_CLASS_SUFFIX = "-prepare";

export const NG_ANIMATE_CLASSNAME = "ng-animate";
export const NG_ANIMATE_CHILDREN_DATA = "$$ngAnimateChildren";

// Detect proper transitionend/animationend event names.
export let CSS_PREFIX = "";
export let TRANSITION_PROP;
export let TRANSITIONEND_EVENT;
export let ANIMATION_PROP;
export let ANIMATIONEND_EVENT;

// If unprefixed events are not supported but webkit-prefixed are, use the latter.
// Otherwise, just use W3C names, browsers not supporting them at all will just ignore them.
// Note: Chrome implements `window.onwebkitanimationend` and doesn't implement `window.onanimationend`
// but at the same time dispatches the `animationend` event and not `webkitAnimationEnd`.
// Register both events in case `window.onanimationend` is not supported because of that,
// do the same for `transitionend` as Safari is likely to exhibit similar behavior.
// Also, the only modern browser that uses vendor prefixes for transitions/keyframes is webkit
// therefore there is no reason to test anymore for other vendor prefixes:
// http://caniuse.com/#search=transition
if (
  window.ontransitionend === undefined &&
  window.onwebkittransitionend !== undefined
) {
  CSS_PREFIX = "-webkit-";
  TRANSITION_PROP = "WebkitTransition";
  TRANSITIONEND_EVENT = "webkitTransitionEnd transitionend";
} else {
  TRANSITION_PROP = "transition";
  TRANSITIONEND_EVENT = "transitionend";
}

if (
  window.onanimationend === undefined &&
  window.onwebkitanimationend !== undefined
) {
  CSS_PREFIX = "-webkit-";
  ANIMATION_PROP = "WebkitAnimation";
  ANIMATIONEND_EVENT = "webkitAnimationEnd animationend";
} else {
  ANIMATION_PROP = "animation";
  ANIMATIONEND_EVENT = "animationend";
}

export const DURATION_KEY = "Duration";
export const PROPERTY_KEY = ASTType.Property;
export const DELAY_KEY = "Delay";
export const TIMING_KEY = "TimingFunction";
export const ANIMATION_ITERATION_COUNT_KEY = "IterationCount";
export const ANIMATION_PLAYSTATE_KEY = "PlayState";
export const SAFE_FAST_FORWARD_DURATION_VALUE = 9999;

export const ANIMATION_DELAY_PROP = ANIMATION_PROP + DELAY_KEY;
export const ANIMATION_DURATION_PROP = ANIMATION_PROP + DURATION_KEY;
export const TRANSITION_DELAY_PROP = TRANSITION_PROP + DELAY_KEY;
export const TRANSITION_DURATION_PROP = TRANSITION_PROP + DURATION_KEY;

export const ngMinErr = minErr("ng");
export function assertArg(arg, name, reason) {
  if (!arg) {
    throw ngMinErr(
      "areq",
      "Argument '{0}' is {1}",
      name || "?",
      reason || "required",
    );
  }
  return arg;
}

export function mergeClasses(a, b) {
  if (!a && !b) return "";
  if (!a) return b;
  if (!b) return a;
  if (Array.isArray(a)) a = a.join(" ");
  if (Array.isArray(b)) b = b.join(" ");
  return `${a} ${b}`;
}

export function packageStyles(options) {
  const styles = {};
  if (options && (options.to || options.from)) {
    styles.to = options.to;
    styles.from = options.from;
  }
  return styles;
}

export function pendClasses(classes, fix, isPrefix) {
  let className = "";

  classes = Array.isArray(classes)
    ? classes
    : classes && isString(classes) && classes.length
      ? classes.split(/\s+/)
      : [];
  classes.forEach((klass, i) => {
    if (klass && klass.length > 0) {
      className += i > 0 ? " " : "";
      className += isPrefix ? fix + klass : klass + fix;
    }
  });
  return className;
}

export function removeFromArray(arr, val) {
  const index = arr.indexOf(val);
  if (val >= 0) {
    arr.splice(index, 1);
  }
}

/**
 *
 * @param {JQLite|Node} element
 * @returns {JQLite}
 */
export function stripCommentsFromElement(element) {
  if (element instanceof JQLite) {
    switch (element.length) {
      case 0:
        return /** @type {JQLite} */ (element);

      case 1:
        // there is no point of stripping anything if the element
        // is the only element within the JQLite wrapper.
        // (it's important that we retain the element instance.)
        if (element[0].nodeType === Node.ELEMENT_NODE) {
          return /** @type {JQLite} */ (element);
        }
        break;

      default:
        return JQLite(extractElementNode(element));
    }
  }

  if (/** @type {Node} */ (element).nodeType === Node.ELEMENT_NODE) {
    return JQLite(element);
  }
}

/**
 * @param {JQLite|Node} element
 * @returns {Node}
 */
export function extractElementNode(element) {
  if (!element[0]) return /** @type {Node} */ (element);
  for (let i = 0; i < /** @type {JQLite} */ (element).length; i++) {
    const elm = element[i];
    if (elm.nodeType === Node.ELEMENT_NODE) {
      return elm;
    }
  }
}

export function applyAnimationClassesFactory() {
  return function (element, options) {
    if (options.addClass) {
      element[0].classList.add(...options.addClass.trim().split(" "));
      options.addClass = null;
    }
    if (options.removeClass) {
      element[0].classList.remove(...options.removeClass.trim().split(" "));
      options.removeClass = null;
    }
  };
}

export function prepareAnimationOptions(options) {
  options = options || {};
  if (!options.$$prepared) {
    let domOperation = options.domOperation || (() => {});
    options.domOperation = function () {
      options.$$domOperationFired = true;
      domOperation();
      domOperation = () => {};
    };
    options.$$prepared = true;
  }
  return options;
}

export function applyAnimationStyles(element, options) {
  applyAnimationFromStyles(element, options);
  applyAnimationToStyles(element, options);
}

export function applyAnimationFromStyles(element, options) {
  if (options.from) {
    //element.css(options.from);
    options.from = null;
  }
}

export function applyAnimationToStyles(element, options) {
  if (options.to) {
    //element.css(options.to);
    options.to = null;
  }
}

export function mergeAnimationDetails(element, oldAnimation, newAnimation) {
  const target = oldAnimation.options || {};
  const newOptions = newAnimation.options || {};

  const toAdd = `${target.addClass || ""} ${newOptions.addClass || ""}`;
  const toRemove = `${target.removeClass || ""} ${newOptions.removeClass || ""}`;
  const classes = resolveElementClasses(element.attr("class"), toAdd, toRemove);

  if (newOptions.preparationClasses) {
    target.preparationClasses = concatWithSpace(
      newOptions.preparationClasses,
      target.preparationClasses,
    );
    delete newOptions.preparationClasses;
  }

  // noop is basically when there is no callback; otherwise something has been set
  const realDomOperation =
    target.domOperation !== (() => {}) ? target.domOperation : null;

  extend(target, newOptions);

  // TODO(matsko or sreeramu): proper fix is to maintain all animation callback in array and call at last,but now only leave has the callback so no issue with this.
  if (realDomOperation) {
    target.domOperation = realDomOperation;
  }

  if (classes.addClass) {
    target.addClass = classes.addClass;
  } else {
    target.addClass = null;
  }

  if (classes.removeClass) {
    target.removeClass = classes.removeClass;
  } else {
    target.removeClass = null;
  }

  oldAnimation.addClass = target.addClass;
  oldAnimation.removeClass = target.removeClass;

  return target;
}

export function resolveElementClasses(existing, toAdd, toRemove) {
  const ADD_CLASS = 1;
  const REMOVE_CLASS = -1;

  const flags = {};
  existing = splitClassesToLookup(existing);

  toAdd = splitClassesToLookup(toAdd);
  Object.keys(toAdd).forEach((key) => {
    flags[key] = ADD_CLASS;
  });

  toRemove = splitClassesToLookup(toRemove);
  Object.keys(toRemove).forEach((key) => {
    flags[key] = flags[key] === ADD_CLASS ? null : REMOVE_CLASS;
  });

  const classes = {
    addClass: "",
    removeClass: "",
  };

  Object.entries(flags).forEach(([klass, val]) => {
    var prop, allow;
    if (val === ADD_CLASS) {
      prop = "addClass";
      allow = !existing[klass] || existing[klass + REMOVE_CLASS_SUFFIX];
    } else if (val === REMOVE_CLASS) {
      prop = "removeClass";
      allow = existing[klass] || existing[klass + ADD_CLASS_SUFFIX];
    }
    if (allow) {
      if (classes[prop].length) {
        classes[prop] += " ";
      }
      classes[prop] += klass;
    }
  });

  function splitClassesToLookup(classes) {
    if (isString(classes)) {
      classes = classes.trim().split(" ");
    }

    const obj = {};
    if (classes) {
      classes.forEach((klass) => {
        // sometimes the split leaves empty string values
        // incase extra spaces were applied to the options
        if (klass.length) {
          obj[klass] = true;
        }
      });
    }
    return obj;
  }

  return classes;
}

/**
 *
 * @param {JQLite|Element} element
 * @returns {Element}
 */
export function getDomNode(element) {
  return element instanceof JQLite ? element[0] : element;
}

export function applyGeneratedPreparationClasses(element, event, options) {
  let classes = "";
  if (event) {
    classes = pendClasses(event, EVENT_CLASS_PREFIX, true);
  }
  if (options.addClass) {
    classes = concatWithSpace(
      classes,
      pendClasses(options.addClass, ADD_CLASS_SUFFIX),
    );
  }
  if (options.removeClass) {
    classes = concatWithSpace(
      classes,
      pendClasses(options.removeClass, REMOVE_CLASS_SUFFIX),
    );
  }
  if (classes.length) {
    options.preparationClasses = classes;
    element[0].className += ` ${classes}`;
  }
}

export function clearGeneratedClasses(element, options) {
  if (options.preparationClasses) {
    options.preparationClasses
      .split(" ")
      .forEach((cls) => element[0].classList.remove(cls));
    options.preparationClasses = null;
  }
  if (options.activeClasses) {
    options.activeClasses
      .split(" ")
      .forEach((cls) => element[0].classList.remove(cls));
    options.activeClasses = null;
  }
}

export function blockKeyframeAnimations(node, applyBlock) {
  const value = applyBlock ? "paused" : "";
  const key = ANIMATION_PROP + ANIMATION_PLAYSTATE_KEY;
  applyInlineStyle(node, [key, value]);
  return [key, value];
}

export function applyInlineStyle(node, styleTuple) {
  const prop = styleTuple[0];
  const value = styleTuple[1];
  node.style[prop] = value;
}

export function concatWithSpace(a, b) {
  if (!a) return b;
  if (!b) return a;
  return `${a} ${b}`;
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy