| Index: third_party/pkg/angular/lib/animate/animation_optimizer.dart
|
| diff --git a/third_party/pkg/angular/lib/animate/animation_optimizer.dart b/third_party/pkg/angular/lib/animate/animation_optimizer.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..843e219478bc78914983e3e9b55982fd3b8dbab8
|
| --- /dev/null
|
| +++ b/third_party/pkg/angular/lib/animate/animation_optimizer.dart
|
| @@ -0,0 +1,163 @@
|
| +part of angular.animate;
|
| +
|
| +/**
|
| + * The optimizer tracks elements and running animations. It's used to control
|
| + * and optionally skip certain animations that are deemed "expensive" such as
|
| + * running animations on child elements while the dom parent is also running an
|
| + * animation.
|
| + */
|
| +@Injectable()
|
| +class AnimationOptimizer {
|
| + final Map<dom.Element, Set<Animation>> _elements = new Map<dom.Element,
|
| + Set<Animation>>();
|
| + final Map<Animation, dom.Element> _animations = new Map<Animation,
|
| + dom.Element>();
|
| +
|
| + final Map<dom.Node, bool> _alwaysAnimate = new Map<dom.Node, bool>();
|
| + final Map<dom.Node, bool> _alwaysAnimateChildren = new Map<dom.Node, bool>();
|
| +
|
| + Expando _expando;
|
| +
|
| + AnimationOptimizer(this._expando);
|
| +
|
| + /**
|
| + * Track an animation that is running against a dom element. Usually, this
|
| + * should occur when an animation starts.
|
| + */
|
| + void track(Animation animation, dom.Element forElement) {
|
| + if (forElement != null) {
|
| + var animations = _elements.putIfAbsent(forElement, () =>
|
| + new Set<Animation>());
|
| + animations.add(animation);
|
| + _animations[animation] = forElement;
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Stop tracking an animation. If it's the last tracked animation on an
|
| + * element forget about that element as well.
|
| + */
|
| + void forget(Animation animation) {
|
| + var element = _animations.remove(animation);
|
| + if (element != null) {
|
| + var animationsOnElement = _elements[element];
|
| + animationsOnElement.remove(animation);
|
| + // It may be more efficient just to keep sets around even after
|
| + // animations complete.
|
| + if (animationsOnElement.length == 0) {
|
| + _elements.remove(element);
|
| + }
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Since we can't overload forget...
|
| + */
|
| + void detachAlwaysAnimateOptions(dom.Element element) {
|
| + _alwaysAnimate.remove(element);
|
| + _alwaysAnimateChildren.remove(element);
|
| + }
|
| +
|
| + /**
|
| + * Control animation for a specific element, ignoring every other option.
|
| + * [mode] "always" will always animate this element.
|
| + * [mode] "never" will never animate this element.
|
| + * [mode] "auto" will detect if a parent animation is running or has child animations set.
|
| + */
|
| + void alwaysAnimate(dom.Element element, String mode) {
|
| + if (mode == "always") {
|
| + _alwaysAnimate[element] = true;
|
| + } else if (mode == "never") {
|
| + _alwaysAnimate[element] = false;
|
| + } else if (mode == "auto") {
|
| + _alwaysAnimate.remove(element);
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Control animation for child elements, ignoring running animations unless 'auto' is provided as an option.
|
| + * [mode] "always" will always animate children, unless it is specifically marked not to by [alwaysAnimate].
|
| + * [mode] "never" will never animate children.
|
| + * [mode] "auto" will detect if a parent animation is running or has child animations set.
|
| + */
|
| + void alwaysAnimateChildren(dom.Element element, String mode) {
|
| + if (mode == "always") {
|
| + _alwaysAnimateChildren[element] = true;
|
| + } else if (mode == "never") {
|
| + _alwaysAnimateChildren[element] = false;
|
| + } else if (mode == "auto") {
|
| + _alwaysAnimateChildren.remove(element);
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Returns true if there is tracked animation on the given element.
|
| + */
|
| + bool _isAnimating(dom.Element element) {
|
| + return _elements.containsKey(element);
|
| + }
|
| +
|
| + /**
|
| + * Given all the information this optimizer knows about currently executing
|
| + * animations, return [true] if this element can be animated in an ideal case
|
| + * and [false] if the optimizer thinks that it should not execute.
|
| + */
|
| + bool shouldAnimate(dom.Node node) {
|
| + bool alwaysAnimate = _alwaysAnimate[node];
|
| + if (alwaysAnimate != null) {
|
| + return alwaysAnimate;
|
| + }
|
| +
|
| + // If there are 'always allow' or 'always prevent' animations declared,
|
| + // fallback to the automatic detection of running parent animations. By
|
| + // default, we assume that we can run.
|
| + bool autoDecision = true;
|
| +
|
| + node = node.parentNode;
|
| + while (node != null) {
|
| + // Does this node give us animation information about our children?
|
| + alwaysAnimate = _alwaysAnimateChildren[node];
|
| + if (alwaysAnimate != null) {
|
| + return alwaysAnimate;
|
| + }
|
| +
|
| + // If we hit a running parent animation, we still need to continue up
|
| + // the dom tree to see if there is or is not an 'alwaysAnimateChildren'
|
| + // decision somewhere.
|
| + if (autoDecision
|
| + && node.nodeType == dom.Node.ELEMENT_NODE
|
| + && _isAnimating(node)) {
|
| + // If there is an already running animation, don't animate.
|
| + autoDecision = false;
|
| + }
|
| +
|
| + // If we hit a null parent, try to break out of shadow dom.
|
| + if (node.parentNode == null) {
|
| + var probe = _findElementProbe(node);
|
| + if (probe != null && probe.parent != null) {
|
| + // Escape shadow dom!
|
| + node = probe.parent.element;
|
| + } else {
|
| + // If we can't go any further, return the auto decision because we
|
| + // havent hit any other more important optimizations.
|
| + return autoDecision;
|
| + }
|
| + } else {
|
| + node = node.parentNode;
|
| + }
|
| + }
|
| +
|
| + return autoDecision;
|
| + }
|
| +
|
| + // Search and find the element probe for a given node.
|
| + ElementProbe _findElementProbe(dom.Node node) {
|
| + while (node != null) {
|
| + if (_expando[node] != null) {
|
| + return _expando[node];
|
| + }
|
| + node = node.parentNode;
|
| + }
|
| + return null;
|
| + }
|
| +}
|
|
|