diff --git a/awx/ui/client/lib/angular-animate/.bower.json b/awx/ui/client/lib/angular-animate/.bower.json
index 27ddb8cc93..22ac500bb0 100644
--- a/awx/ui/client/lib/angular-animate/.bower.json
+++ b/awx/ui/client/lib/angular-animate/.bower.json
@@ -1,19 +1,19 @@
{
"name": "angular-animate",
- "version": "1.4.3",
+ "version": "1.4.7",
"main": "./angular-animate.js",
"ignore": [],
"dependencies": {
- "angular": "1.4.3"
+ "angular": "1.4.7"
},
"homepage": "https://github.com/angular/bower-angular-animate",
- "_release": "1.4.3",
+ "_release": "1.4.7",
"_resolution": {
"type": "version",
- "tag": "v1.4.3",
- "commit": "4ce2a76359401102d2e0146ccf69e6c060799ff8"
+ "tag": "v1.4.7",
+ "commit": "3e63136fc3d882828594f3ceb929784eb43aa944"
},
"_source": "git://github.com/angular/bower-angular-animate.git",
- "_target": "~1.4.3",
+ "_target": "~1.4.7",
"_originalSource": "angular-animate"
}
\ No newline at end of file
diff --git a/awx/ui/client/lib/angular-animate/angular-animate.js b/awx/ui/client/lib/angular-animate/angular-animate.js
index fc0e217f7e..1948298182 100644
--- a/awx/ui/client/lib/angular-animate/angular-animate.js
+++ b/awx/ui/client/lib/angular-animate/angular-animate.js
@@ -1,5 +1,5 @@
/**
- * @license AngularJS v1.4.3
+ * @license AngularJS v1.4.7
* (c) 2010-2015 Google, Inc. http://angularjs.org
* License: MIT
*/
@@ -21,12 +21,60 @@ var isElement = angular.isElement;
var ELEMENT_NODE = 1;
var COMMENT_NODE = 8;
+var ADD_CLASS_SUFFIX = '-add';
+var REMOVE_CLASS_SUFFIX = '-remove';
+var EVENT_CLASS_PREFIX = 'ng-';
+var ACTIVE_CLASS_SUFFIX = '-active';
+
var NG_ANIMATE_CLASSNAME = 'ng-animate';
var NG_ANIMATE_CHILDREN_DATA = '$$ngAnimateChildren';
+// Detect proper transitionend/animationend event names.
+var CSS_PREFIX = '', TRANSITION_PROP, TRANSITIONEND_EVENT, ANIMATION_PROP, 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 (isUndefined(window.ontransitionend) && isDefined(window.onwebkittransitionend)) {
+ CSS_PREFIX = '-webkit-';
+ TRANSITION_PROP = 'WebkitTransition';
+ TRANSITIONEND_EVENT = 'webkitTransitionEnd transitionend';
+} else {
+ TRANSITION_PROP = 'transition';
+ TRANSITIONEND_EVENT = 'transitionend';
+}
+
+if (isUndefined(window.onanimationend) && isDefined(window.onwebkitanimationend)) {
+ CSS_PREFIX = '-webkit-';
+ ANIMATION_PROP = 'WebkitAnimation';
+ ANIMATIONEND_EVENT = 'webkitAnimationEnd animationend';
+} else {
+ ANIMATION_PROP = 'animation';
+ ANIMATIONEND_EVENT = 'animationend';
+}
+
+var DURATION_KEY = 'Duration';
+var PROPERTY_KEY = 'Property';
+var DELAY_KEY = 'Delay';
+var TIMING_KEY = 'TimingFunction';
+var ANIMATION_ITERATION_COUNT_KEY = 'IterationCount';
+var ANIMATION_PLAYSTATE_KEY = 'PlayState';
+var SAFE_FAST_FORWARD_DURATION_VALUE = 9999;
+
+var ANIMATION_DELAY_PROP = ANIMATION_PROP + DELAY_KEY;
+var ANIMATION_DURATION_PROP = ANIMATION_PROP + DURATION_KEY;
+var TRANSITION_DELAY_PROP = TRANSITION_PROP + DELAY_KEY;
+var TRANSITION_DURATION_PROP = TRANSITION_PROP + DURATION_KEY;
+
var isPromiseLike = function(p) {
return p && p.then ? true : false;
-}
+};
function assertArg(arg, name, reason) {
if (!arg) {
@@ -177,8 +225,21 @@ function mergeAnimationOptions(element, target, newOptions) {
var toRemove = (target.removeClass || '') + ' ' + (newOptions.removeClass || '');
var 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
+ var realDomOperation = target.domOperation !== noop ? 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 {
@@ -256,18 +317,75 @@ function getDomNode(element) {
return (element instanceof angular.element) ? element[0] : element;
}
+function applyGeneratedPreparationClasses(element, event, options) {
+ var 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.addClass(classes);
+ }
+}
+
+function clearGeneratedClasses(element, options) {
+ if (options.preparationClasses) {
+ element.removeClass(options.preparationClasses);
+ options.preparationClasses = null;
+ }
+ if (options.activeClasses) {
+ element.removeClass(options.activeClasses);
+ options.activeClasses = null;
+ }
+}
+
+function blockTransitions(node, duration) {
+ // we use a negative delay value since it performs blocking
+ // yet it doesn't kill any existing transitions running on the
+ // same element which makes this safe for class-based animations
+ var value = duration ? '-' + duration + 's' : '';
+ applyInlineStyle(node, [TRANSITION_DELAY_PROP, value]);
+ return [TRANSITION_DELAY_PROP, value];
+}
+
+function blockKeyframeAnimations(node, applyBlock) {
+ var value = applyBlock ? 'paused' : '';
+ var key = ANIMATION_PROP + ANIMATION_PLAYSTATE_KEY;
+ applyInlineStyle(node, [key, value]);
+ return [key, value];
+}
+
+function applyInlineStyle(node, styleTuple) {
+ var prop = styleTuple[0];
+ var value = styleTuple[1];
+ node.style[prop] = value;
+}
+
+function concatWithSpace(a,b) {
+ if (!a) return b;
+ if (!b) return a;
+ return a + ' ' + b;
+}
+
var $$rAFSchedulerFactory = ['$$rAF', function($$rAF) {
- var tickQueue = [];
- var cancelFn;
+ var queue, cancelFn;
function scheduler(tasks) {
// we make a copy since RAFScheduler mutates the state
// of the passed in array variable and this would be difficult
// to track down on the outside code
- tickQueue.push([].concat(tasks));
+ queue = queue.concat(tasks);
nextTick();
}
+ queue = scheduler.queue = [];
+
/* waitUntilQuiet does two things:
* 1. It will run the FINAL `fn` value only when an uncancelled RAF has passed through
* 2. It will delay the next wave of tasks from running until the quiet `fn` has run.
@@ -289,17 +407,12 @@ var $$rAFSchedulerFactory = ['$$rAF', function($$rAF) {
return scheduler;
function nextTick() {
- if (!tickQueue.length) return;
+ if (!queue.length) return;
- var updatedQueue = [];
- for (var i = 0; i < tickQueue.length; i++) {
- var innerQueue = tickQueue[i];
- runNextTask(innerQueue);
- if (innerQueue.length) {
- updatedQueue.push(innerQueue);
- }
+ var items = queue.shift();
+ for (var i = 0; i < items.length; i++) {
+ items[i]();
}
- tickQueue = updatedQueue;
if (!cancelFn) {
$$rAF(function() {
@@ -307,11 +420,6 @@ var $$rAFSchedulerFactory = ['$$rAF', function($$rAF) {
});
}
}
-
- function runNextTask(tasks) {
- var nextTask = tasks.shift();
- nextTask();
- }
}];
var $$AnimateChildrenDirective = [function() {
@@ -328,6 +436,8 @@ var $$AnimateChildrenDirective = [function() {
};
}];
+var ANIMATE_TIMER_KEY = '$$animateCss';
+
/**
* @ngdoc service
* @name $animateCss
@@ -513,7 +623,7 @@ var $$AnimateChildrenDirective = [function() {
* * `event` - The DOM event (e.g. enter, leave, move). When used, a generated CSS class of `ng-EVENT` and `ng-EVENT-active` will be applied
* to the element during the animation. Multiple events can be provided when spaces are used as a separator. (Note that this will not perform any DOM operation.)
* * `easing` - The CSS easing value that will be applied to the transition or keyframe animation (or both).
- * * `transition` - The raw CSS transition style that will be used (e.g. `1s linear all`).
+ * * `transitionStyle` - The raw CSS transition style that will be used (e.g. `1s linear all`).
* * `keyframeStyle` - The raw CSS keyframe animation style that will be used (e.g. `1s my_animation linear`).
* * `from` - The starting CSS styles (a key/value object) that will be applied at the start of the animation.
* * `to` - The ending CSS styles (a key/value object) that will be applied across the animation via a CSS transition.
@@ -528,63 +638,23 @@ var $$AnimateChildrenDirective = [function() {
* * `stagger` - A numeric time value representing the delay between successively animated elements
* ({@link ngAnimate#css-staggering-animations Click here to learn how CSS-based staggering works in ngAnimate.})
* * `staggerIndex` - The numeric index representing the stagger item (e.g. a value of 5 is equal to the sixth item in the stagger; therefore when a
- * `stagger` option value of `0.1` is used then there will be a stagger delay of `600ms`)
- * `applyClassesEarly` - Whether or not the classes being added or removed will be used when detecting the animation. This is set by `$animate` when enter/leave/move animations are fired to ensure that the CSS classes are resolved in time. (Note that this will prevent any transitions from occuring on the classes being added and removed.)
+ * * `stagger` option value of `0.1` is used then there will be a stagger delay of `600ms`)
+ * * `applyClassesEarly` - Whether or not the classes being added or removed will be used when detecting the animation. This is set by `$animate` when enter/leave/move animations are fired to ensure that the CSS classes are resolved in time. (Note that this will prevent any transitions from occuring on the classes being added and removed.)
+ * * `cleanupStyles` - Whether or not the provided `from` and `to` styles will be removed once
+ * the animation is closed. This is useful for when the styles are used purely for the sake of
+ * the animation and do not have a lasting visual effect on the element (e.g. a colapse and open animation).
+ * By default this value is set to `false`.
*
* @return {object} an object with start and end methods and details about the animation.
*
* * `start` - The method to start the animation. This will return a `Promise` when called.
* * `end` - This method will cancel the animation and remove all applied CSS classes and styles.
*/
-
-// Detect proper transitionend/animationend event names.
-var CSS_PREFIX = '', TRANSITION_PROP, TRANSITIONEND_EVENT, ANIMATION_PROP, 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';
-}
-
-var DURATION_KEY = 'Duration';
-var PROPERTY_KEY = 'Property';
-var DELAY_KEY = 'Delay';
-var TIMING_KEY = 'TimingFunction';
-var ANIMATION_ITERATION_COUNT_KEY = 'IterationCount';
-var ANIMATION_PLAYSTATE_KEY = 'PlayState';
-var ELAPSED_TIME_MAX_DECIMAL_PLACES = 3;
-var CLOSING_TIME_BUFFER = 1.5;
var ONE_SECOND = 1000;
var BASE_TEN = 10;
-var SAFE_FAST_FORWARD_DURATION_VALUE = 9999;
-
-var ANIMATION_DELAY_PROP = ANIMATION_PROP + DELAY_KEY;
-var ANIMATION_DURATION_PROP = ANIMATION_PROP + DURATION_KEY;
-
-var TRANSITION_DELAY_PROP = TRANSITION_PROP + DELAY_KEY;
-var TRANSITION_DURATION_PROP = TRANSITION_PROP + DURATION_KEY;
+var ELAPSED_TIME_MAX_DECIMAL_PLACES = 3;
+var CLOSING_TIME_BUFFER = 1.5;
var DETECT_CSS_PROPERTIES = {
transitionDuration: TRANSITION_DURATION_PROP,
@@ -602,6 +672,15 @@ var DETECT_STAGGER_CSS_PROPERTIES = {
animationDelay: ANIMATION_DELAY_PROP
};
+function getCssKeyframeDurationStyle(duration) {
+ return [ANIMATION_DURATION_PROP, duration + 's'];
+}
+
+function getCssDelayStyle(delay, isKeyframeAnimation) {
+ var prop = isKeyframeAnimation ? ANIMATION_DELAY_PROP : TRANSITION_DELAY_PROP;
+ return [prop, delay + 's'];
+}
+
function computeCssStyles($window, element, properties) {
var styles = Object.create(null);
var detectedStyles = $window.getComputedStyle(element) || {};
@@ -658,37 +737,6 @@ function getCssTransitionDurationStyle(duration, applyOnlyDuration) {
return [style, value];
}
-function getCssKeyframeDurationStyle(duration) {
- return [ANIMATION_DURATION_PROP, duration + 's'];
-}
-
-function getCssDelayStyle(delay, isKeyframeAnimation) {
- var prop = isKeyframeAnimation ? ANIMATION_DELAY_PROP : TRANSITION_DELAY_PROP;
- return [prop, delay + 's'];
-}
-
-function blockTransitions(node, duration) {
- // we use a negative delay value since it performs blocking
- // yet it doesn't kill any existing transitions running on the
- // same element which makes this safe for class-based animations
- var value = duration ? '-' + duration + 's' : '';
- applyInlineStyle(node, [TRANSITION_DELAY_PROP, value]);
- return [TRANSITION_DELAY_PROP, value];
-}
-
-function blockKeyframeAnimations(node, applyBlock) {
- var value = applyBlock ? 'paused' : '';
- var key = ANIMATION_PROP + ANIMATION_PLAYSTATE_KEY;
- applyInlineStyle(node, [key, value]);
- return [key, value];
-}
-
-function applyInlineStyle(node, styleTuple) {
- var prop = styleTuple[0];
- var value = styleTuple[1];
- node.style[prop] = value;
-}
-
function createLocalCacheLookup() {
var cache = Object.create(null);
return {
@@ -716,14 +764,31 @@ function createLocalCacheLookup() {
};
}
+// we do not reassign an already present style value since
+// if we detect the style property value again we may be
+// detecting styles that were added via the `from` styles.
+// We make use of `isDefined` here since an empty string
+// or null value (which is what getPropertyValue will return
+// for a non-existing style) will still be marked as a valid
+// value for the style (a falsy value implies that the style
+// is to be removed at the end of the animation). If we had a simple
+// "OR" statement then it would not be enough to catch that.
+function registerRestorableStyles(backup, node, properties) {
+ forEach(properties, function(prop) {
+ backup[prop] = isDefined(backup[prop])
+ ? backup[prop]
+ : node.style.getPropertyValue(prop);
+ });
+}
+
var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
var gcsLookup = createLocalCacheLookup();
var gcsStaggerLookup = createLocalCacheLookup();
this.$get = ['$window', '$$jqLite', '$$AnimateRunner', '$timeout',
- '$document', '$sniffer', '$$rAFScheduler',
+ '$$forceReflow', '$sniffer', '$$rAFScheduler', '$animate',
function($window, $$jqLite, $$AnimateRunner, $timeout,
- $document, $sniffer, $$rAFScheduler) {
+ $$forceReflow, $sniffer, $$rAFScheduler, $animate) {
var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);
@@ -780,7 +845,7 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
return stagger || {};
}
- var bod = getDomNode($document).body;
+ var cancelLastRAFRequest;
var rafWaitQueue = [];
function waitUntilQuiet(callback) {
rafWaitQueue.push(callback);
@@ -788,27 +853,19 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
gcsLookup.flush();
gcsStaggerLookup.flush();
- //the line below will force the browser to perform a repaint so
- //that all the animated elements within the animation frame will
- //be properly updated and drawn on screen. This is required to
- //ensure that the preparation animation is properly flushed so that
- //the active state picks up from there. DO NOT REMOVE THIS LINE.
- //DO NOT OPTIMIZE THIS LINE. THE MINIFIER WILL REMOVE IT OTHERWISE WHICH
- //WILL RESULT IN AN UNPREDICTABLE BUG THAT IS VERY HARD TO TRACK DOWN AND
- //WILL TAKE YEARS AWAY FROM YOUR LIFE.
- var width = bod.offsetWidth + 1;
+ // DO NOT REMOVE THIS LINE OR REFACTOR OUT THE `pageWidth` variable.
+ // PLEASE EXAMINE THE `$$forceReflow` service to understand why.
+ var pageWidth = $$forceReflow();
// we use a for loop to ensure that if the queue is changed
// during this looping then it will consider new requests
for (var i = 0; i < rafWaitQueue.length; i++) {
- rafWaitQueue[i](width);
+ rafWaitQueue[i](pageWidth);
}
rafWaitQueue.length = 0;
});
}
- return init;
-
function computeTimings(node, className, cacheKey) {
var timings = computeCachedCssStyles(node, className, cacheKey, DETECT_CSS_PROPERTIES);
var aD = timings.animationDelay;
@@ -823,9 +880,12 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
return timings;
}
- function init(element, options) {
+ return function init(element, options) {
+ var restoreStyles = {};
var node = getDomNode(element);
- if (!node || !node.parentNode) {
+ if (!node
+ || !node.parentNode
+ || !$animate.enabled()) {
return closeAndReturnNoopAnimator();
}
@@ -857,20 +917,20 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
var addRemoveClassName = '';
if (isStructural) {
- structuralClassName = pendClasses(method, 'ng-', true);
+ structuralClassName = pendClasses(method, EVENT_CLASS_PREFIX, true);
} else if (method) {
structuralClassName = method;
}
if (options.addClass) {
- addRemoveClassName += pendClasses(options.addClass, '-add');
+ addRemoveClassName += pendClasses(options.addClass, ADD_CLASS_SUFFIX);
}
if (options.removeClass) {
if (addRemoveClassName.length) {
addRemoveClassName += ' ';
}
- addRemoveClassName += pendClasses(options.removeClass, '-remove');
+ addRemoveClassName += pendClasses(options.removeClass, REMOVE_CLASS_SUFFIX);
}
// there may be a situation where a structural animation is combined together
@@ -881,12 +941,11 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
// there actually is a detected transition or keyframe animation
if (options.applyClassesEarly && addRemoveClassName.length) {
applyAnimationClasses(element, options);
- addRemoveClassName = '';
}
- var setupClasses = [structuralClassName, addRemoveClassName].join(' ').trim();
- var fullClassName = classes + ' ' + setupClasses;
- var activeClasses = pendClasses(setupClasses, '-active');
+ var preparationClasses = [structuralClassName, addRemoveClassName].join(' ').trim();
+ var fullClassName = classes + ' ' + preparationClasses;
+ var activeClasses = pendClasses(preparationClasses, ACTIVE_CLASS_SUFFIX);
var hasToStyles = styles.to && Object.keys(styles.to).length > 0;
var containsKeyframeAnimation = (options.keyframeStyle || '').length > 0;
@@ -895,7 +954,7 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
// unless there a is raw keyframe value that is applied to the element.
if (!containsKeyframeAnimation
&& !hasToStyles
- && !setupClasses) {
+ && !preparationClasses) {
return closeAndReturnNoopAnimator();
}
@@ -910,10 +969,12 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
};
} else {
cacheKey = gcsHashFn(node, fullClassName);
- stagger = computeCachedCssStaggerStyles(node, setupClasses, cacheKey, DETECT_STAGGER_CSS_PROPERTIES);
+ stagger = computeCachedCssStaggerStyles(node, preparationClasses, cacheKey, DETECT_STAGGER_CSS_PROPERTIES);
}
- $$jqLite.addClass(element, setupClasses);
+ if (!options.$$skipPreparationClasses) {
+ $$jqLite.addClass(element, preparationClasses);
+ }
var applyOnlyDuration;
@@ -952,7 +1013,7 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
// transition delay to allow for the transition to naturally do it's thing. The beauty here is
// that if there is no transition defined then nothing will happen and this will also allow
// other transitions to be stacked on top of each other without any chopping them out.
- if (isFirst) {
+ if (isFirst && !options.skipBlocking) {
blockTransitions(node, SAFE_FAST_FORWARD_DURATION_VALUE);
}
@@ -994,6 +1055,18 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
return closeAndReturnNoopAnimator();
}
+ if (options.delay != null) {
+ var delayStyle = parseFloat(options.delay);
+
+ if (flags.applyTransitionDelay) {
+ temporaryStyles.push(getCssDelayStyle(delayStyle));
+ }
+
+ if (flags.applyAnimationDelay) {
+ temporaryStyles.push(getCssDelayStyle(delayStyle, true));
+ }
+ }
+
// we need to recalculate the delay value since we used a pre-emptive negative
// delay value and the delay value is required for the final event checking. This
// property will ensure that this will happen after the RAF phase has passed.
@@ -1010,12 +1083,18 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
stagger.animationDuration === 0;
}
- applyAnimationFromStyles(element, options);
- if (!flags.blockTransition) {
- blockTransitions(node, false);
+ if (options.from) {
+ if (options.cleanupStyles) {
+ registerRestorableStyles(restoreStyles, node, Object.keys(options.from));
+ }
+ applyAnimationFromStyles(element, options);
}
- applyBlocking(maxDuration);
+ if (flags.blockTransition || flags.blockKeyframeAnimation) {
+ applyBlocking(maxDuration);
+ } else if (!options.skipBlocking) {
+ blockTransitions(node, false);
+ }
// TODO(matsko): for 1.5 change this code to have an animator object for better debugging
return {
@@ -1058,7 +1137,9 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
animationClosed = true;
animationPaused = false;
- $$jqLite.removeClass(element, setupClasses);
+ if (!options.$$skipPreparationClasses) {
+ $$jqLite.removeClass(element, preparationClasses);
+ }
$$jqLite.removeClass(element, activeClasses);
blockKeyframeAnimations(node, false);
@@ -1074,6 +1155,13 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
applyAnimationClasses(element, options);
applyAnimationStyles(element, options);
+ if (Object.keys(restoreStyles).length) {
+ forEach(restoreStyles, function(value, prop) {
+ value ? node.style.setProperty(prop, value)
+ : node.style.removeProperty(prop);
+ });
+ }
+
// the reason why we have this option is to allow a synchronous closing callback
// that is fired as SOON as the animation ends (when the CSS is removed) or if
// the animation never takes off at all. A good example is a leave animation since
@@ -1105,6 +1193,8 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
cancel: cancelFn
});
+ // should flush the cache animation
+ waitUntilQuiet(noop);
close();
return {
@@ -1185,7 +1275,7 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
$$jqLite.addClass(element, activeClasses);
if (flags.recalculateTimingStyles) {
- fullClassName = node.className + ' ' + setupClasses;
+ fullClassName = node.className + ' ' + preparationClasses;
cacheKey = gcsHashFn(node, fullClassName);
timings = computeTimings(node, fullClassName, cacheKey);
@@ -1202,27 +1292,16 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
flags.hasAnimations = timings.animationDuration > 0;
}
- if (flags.applyTransitionDelay || flags.applyAnimationDelay) {
+ if (flags.applyAnimationDelay) {
relativeDelay = typeof options.delay !== "boolean" && truthyTimingValue(options.delay)
? parseFloat(options.delay)
: relativeDelay;
maxDelay = Math.max(relativeDelay, 0);
-
- var delayStyle;
- if (flags.applyTransitionDelay) {
- timings.transitionDelay = relativeDelay;
- delayStyle = getCssDelayStyle(relativeDelay);
- temporaryStyles.push(delayStyle);
- node.style[delayStyle[0]] = delayStyle[1];
- }
-
- if (flags.applyAnimationDelay) {
- timings.animationDelay = relativeDelay;
- delayStyle = getCssDelayStyle(relativeDelay, true);
- temporaryStyles.push(delayStyle);
- node.style[delayStyle[0]] = delayStyle[1];
- }
+ timings.animationDelay = relativeDelay;
+ delayStyle = getCssDelayStyle(relativeDelay, true);
+ temporaryStyles.push(delayStyle);
+ node.style[delayStyle[0]] = delayStyle[1];
}
maxDelayTime = maxDelay * ONE_SECOND;
@@ -1251,17 +1330,52 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
}
startTime = Date.now();
- element.on(events.join(' '), onAnimationProgress);
- $timeout(onAnimationExpired, maxDelayTime + CLOSING_TIME_BUFFER * maxDurationTime);
+ var timerTime = maxDelayTime + CLOSING_TIME_BUFFER * maxDurationTime;
+ var endTime = startTime + timerTime;
- applyAnimationToStyles(element, options);
+ var animationsData = element.data(ANIMATE_TIMER_KEY) || [];
+ var setupFallbackTimer = true;
+ if (animationsData.length) {
+ var currentTimerData = animationsData[0];
+ setupFallbackTimer = endTime > currentTimerData.expectedEndTime;
+ if (setupFallbackTimer) {
+ $timeout.cancel(currentTimerData.timer);
+ } else {
+ animationsData.push(close);
+ }
+ }
+
+ if (setupFallbackTimer) {
+ var timer = $timeout(onAnimationExpired, timerTime, false);
+ animationsData[0] = {
+ timer: timer,
+ expectedEndTime: endTime
+ };
+ animationsData.push(close);
+ element.data(ANIMATE_TIMER_KEY, animationsData);
+ }
+
+ element.on(events.join(' '), onAnimationProgress);
+ if (options.to) {
+ if (options.cleanupStyles) {
+ registerRestorableStyles(restoreStyles, node, Object.keys(options.to));
+ }
+ applyAnimationToStyles(element, options);
+ }
}
function onAnimationExpired() {
- // although an expired animation is a failed animation, getting to
- // this outcome is very easy if the CSS code screws up. Therefore we
- // should still continue normally as if the animation completed correctly.
- close();
+ var animationsData = element.data(ANIMATE_TIMER_KEY);
+
+ // this will be false in the event that the element was
+ // removed from the DOM (via a leave animation or something
+ // similar)
+ if (animationsData) {
+ for (var i = 1; i < animationsData.length; i++) {
+ animationsData[i]();
+ }
+ element.removeData(ANIMATE_TIMER_KEY);
+ }
}
function onAnimationProgress(event) {
@@ -1288,7 +1402,7 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
}
}
}
- }
+ };
}];
}];
@@ -1301,16 +1415,27 @@ var $$AnimateCssDriverProvider = ['$$animationProvider', function($$animationPro
var NG_OUT_ANCHOR_CLASS_NAME = 'ng-anchor-out';
var NG_IN_ANCHOR_CLASS_NAME = 'ng-anchor-in';
- this.$get = ['$animateCss', '$rootScope', '$$AnimateRunner', '$rootElement', '$document', '$sniffer',
- function($animateCss, $rootScope, $$AnimateRunner, $rootElement, $document, $sniffer) {
+ function isDocumentFragment(node) {
+ return node.parentNode && node.parentNode.nodeType === 11;
+ }
+
+ this.$get = ['$animateCss', '$rootScope', '$$AnimateRunner', '$rootElement', '$sniffer', '$$jqLite', '$document',
+ function($animateCss, $rootScope, $$AnimateRunner, $rootElement, $sniffer, $$jqLite, $document) {
// only browsers that support these properties can render animations
if (!$sniffer.animations && !$sniffer.transitions) return noop;
- var bodyNode = getDomNode($document).body;
+ var bodyNode = $document[0].body;
var rootNode = getDomNode($rootElement);
- var rootBodyElement = jqLite(bodyNode.parentNode === rootNode ? bodyNode : rootNode);
+ var rootBodyElement = jqLite(
+ // this is to avoid using something that exists outside of the body
+ // we also special case the doc fragement case because our unit test code
+ // appends the $rootElement to the body after the app has been bootstrapped
+ isDocumentFragment(rootNode) || bodyNode.contains(rootNode) ? rootNode : bodyNode
+ );
+
+ var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);
return function initDriverFn(animationDetails) {
return animationDetails.from && animationDetails.to
@@ -1462,8 +1587,8 @@ var $$AnimateCssDriverProvider = ['$$animationProvider', function($$animationPro
}
function prepareFromToAnchorAnimation(from, to, classes, anchors) {
- var fromAnimation = prepareRegularAnimation(from);
- var toAnimation = prepareRegularAnimation(to);
+ var fromAnimation = prepareRegularAnimation(from, noop);
+ var toAnimation = prepareRegularAnimation(to, noop);
var anchorAnimations = [];
forEach(anchors, function(anchor) {
@@ -1519,19 +1644,23 @@ var $$AnimateCssDriverProvider = ['$$animationProvider', function($$animationPro
var options = animationDetails.options || {};
if (animationDetails.structural) {
- // structural animations ensure that the CSS classes are always applied
- // before the detection starts.
- options.structural = options.applyClassesEarly = true;
+ options.event = animationDetails.event;
+ options.structural = true;
+ options.applyClassesEarly = true;
// we special case the leave animation since we want to ensure that
// the element is removed as soon as the animation is over. Otherwise
// a flicker might appear or the element may not be removed at all
- options.event = animationDetails.event;
- if (options.event === 'leave') {
+ if (animationDetails.event === 'leave') {
options.onDone = options.domOperation;
}
- } else {
- options.event = null;
+ }
+
+ // We assign the preparationClasses as the actual animation event since
+ // the internals of $animateCss will just suffix the event token values
+ // with `-active` to trigger the animation.
+ if (options.preparationClasses) {
+ options.event = concatWithSpace(options.event, options.preparationClasses);
}
var animator = $animateCss(element, options);
@@ -1550,8 +1679,8 @@ var $$AnimateCssDriverProvider = ['$$animationProvider', function($$animationPro
// by the time...
var $$AnimateJsProvider = ['$animateProvider', function($animateProvider) {
- this.$get = ['$injector', '$$AnimateRunner', '$$rAFMutex', '$$jqLite',
- function($injector, $$AnimateRunner, $$rAFMutex, $$jqLite) {
+ this.$get = ['$injector', '$$AnimateRunner', '$$jqLite',
+ function($injector, $$AnimateRunner, $$jqLite) {
var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);
// $animateJs(element, 'enter');
@@ -1908,8 +2037,8 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
});
rules.skip.push(function(element, newAnimation, currentAnimation) {
- // if there is a current animation then skip the class-based animation
- return currentAnimation.structural && !newAnimation.structural;
+ // if there is an ongoing current animation then don't even bother running the class-based animation
+ return currentAnimation.structural && currentAnimation.state === RUNNING_STATE && !newAnimation.structural;
});
rules.cancel.push(function(element, newAnimation, currentAnimation) {
@@ -1932,15 +2061,32 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
});
this.$get = ['$$rAF', '$rootScope', '$rootElement', '$document', '$$HashMap',
- '$$animation', '$$AnimateRunner', '$templateRequest', '$$jqLite',
+ '$$animation', '$$AnimateRunner', '$templateRequest', '$$jqLite', '$$forceReflow',
function($$rAF, $rootScope, $rootElement, $document, $$HashMap,
- $$animation, $$AnimateRunner, $templateRequest, $$jqLite) {
+ $$animation, $$AnimateRunner, $templateRequest, $$jqLite, $$forceReflow) {
var activeAnimationsLookup = new $$HashMap();
var disabledElementsLookup = new $$HashMap();
-
var animationsEnabled = null;
+ function postDigestTaskFactory() {
+ var postDigestCalled = false;
+ return function(fn) {
+ // we only issue a call to postDigest before
+ // it has first passed. This prevents any callbacks
+ // from not firing once the animation has completed
+ // since it will be out of the digest cycle.
+ if (postDigestCalled) {
+ fn();
+ } else {
+ $rootScope.$$postDigest(function() {
+ postDigestCalled = true;
+ fn();
+ });
+ }
+ };
+ }
+
// Wait until all directive and route-related templates are downloaded and
// compiled. The $templateRequest.totalPendingRequests variable keeps track of
// all of the remote templates being currently downloaded. If there are no
@@ -1970,8 +2116,6 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
}
);
- var bodyElement = jqLite($document[0].body);
-
var callbackRegistry = {};
// remember that the classNameFilter is set during the provider/config
@@ -2005,14 +2149,6 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
return matches;
}
- function triggerCallback(event, element, phase, data) {
- $$rAF(function() {
- forEach(findCallbacks(element, event), function(callback) {
- callback(element, phase, data);
- });
- });
- }
-
return {
on: function(event, container, callback) {
var node = extractElementNode(container);
@@ -2107,22 +2243,25 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
// These methods will become available after the digest has passed
var runner = new $$AnimateRunner();
- // there are situations where a directive issues an animation for
- // a jqLite wrapper that contains only comment nodes... If this
- // happens then there is no way we can perform an animation
- if (!node) {
- close();
- return runner;
- }
+ // this is used to trigger callbacks in postDigest mode
+ var runInNextPostDigestOrNow = postDigestTaskFactory();
if (isArray(options.addClass)) {
options.addClass = options.addClass.join(' ');
}
+ if (options.addClass && !isString(options.addClass)) {
+ options.addClass = null;
+ }
+
if (isArray(options.removeClass)) {
options.removeClass = options.removeClass.join(' ');
}
+ if (options.removeClass && !isString(options.removeClass)) {
+ options.removeClass = null;
+ }
+
if (options.from && !isObject(options.from)) {
options.from = null;
}
@@ -2131,6 +2270,14 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
options.to = null;
}
+ // there are situations where a directive issues an animation for
+ // a jqLite wrapper that contains only comment nodes... If this
+ // happens then there is no way we can perform an animation
+ if (!node) {
+ close();
+ return runner;
+ }
+
var className = [node.className, options.addClass, options.removeClass].join(' ');
if (!isAnimatableClassName(className)) {
close();
@@ -2195,8 +2342,9 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
// method which will call the runner methods in async.
existingAnimation.close();
} else {
- // this will merge the existing animation options into this new follow-up animation
- mergeAnimationOptions(element, newAnimation.options, existingAnimation.options);
+ // this will merge the new animation options into existing animation options
+ mergeAnimationOptions(element, existingAnimation.options, newAnimation.options);
+ return existingAnimation.runner;
}
} else {
// a joined animation means that this animation will take over the existing one
@@ -2207,9 +2355,14 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
if (existingAnimation.state === RUNNING_STATE) {
normalizeAnimationOptions(element, options);
} else {
+ applyGeneratedPreparationClasses(element, isStructural ? event : null, options);
+
event = newAnimation.event = existingAnimation.event;
options = mergeAnimationOptions(element, existingAnimation.options, newAnimation.options);
- return runner;
+
+ //we return the same runner since only the option values of this animation will
+ //be fed into the `existingAnimation`.
+ return existingAnimation.runner;
}
}
}
@@ -2235,10 +2388,6 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
return runner;
}
- if (isStructural) {
- closeParentClassBasedAnimations(parent);
- }
-
// the counter keeps track of cancelled animations
var counter = (existingAnimation.counter || 0) + 1;
newAnimation.counter = counter;
@@ -2296,12 +2445,9 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
? 'setClass'
: animationDetails.event;
- if (animationDetails.structural) {
- closeParentClassBasedAnimations(parentElement);
- }
-
markElementAnimationState(element, RUNNING_STATE);
var realRunner = $$animation(element, event, animationDetails.options);
+
realRunner.done(function(status) {
close(!status);
var animationDetails = activeAnimationsLookup.get(node);
@@ -2320,11 +2466,25 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
return runner;
function notifyProgress(runner, event, phase, data) {
- triggerCallback(event, element, phase, data);
+ runInNextPostDigestOrNow(function() {
+ var callbacks = findCallbacks(element, event);
+ if (callbacks.length) {
+ // do not optimize this call here to RAF because
+ // we don't know how heavy the callback code here will
+ // be and if this code is buffered then this can
+ // lead to a performance regression.
+ $$rAF(function() {
+ forEach(callbacks, function(callback) {
+ callback(element, phase, data);
+ });
+ });
+ }
+ });
runner.progress(event, phase, data);
}
function close(reject) { // jshint ignore:line
+ clearGeneratedClasses(element, options);
applyAnimationClasses(element, options);
applyAnimationStyles(element, options);
options.domOperation();
@@ -2361,36 +2521,10 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
return getDomNode(nodeOrElmA) === getDomNode(nodeOrElmB);
}
- function closeParentClassBasedAnimations(startingElement) {
- var parentNode = getDomNode(startingElement);
- do {
- if (!parentNode || parentNode.nodeType !== ELEMENT_NODE) break;
-
- var animationDetails = activeAnimationsLookup.get(parentNode);
- if (animationDetails) {
- examineParentAnimation(parentNode, animationDetails);
- }
-
- parentNode = parentNode.parentNode;
- } while (true);
-
- // since animations are detected from CSS classes, we need to flush all parent
- // class-based animations so that the parent classes are all present for child
- // animations to properly function (otherwise any CSS selectors may not work)
- function examineParentAnimation(node, animationDetails) {
- // enter/leave/move always have priority
- if (animationDetails.structural || !hasAnimationClasses(animationDetails.options)) return;
-
- if (animationDetails.state === RUNNING_STATE) {
- animationDetails.runner.end();
- }
- clearElementAnimationState(node);
- }
- }
-
function areAnimationsAllowed(element, parentElement, event) {
- var bodyElementDetected = false;
- var rootElementDetected = false;
+ var bodyElement = jqLite($document[0].body);
+ var bodyElementDetected = isMatchingElement(element, bodyElement) || element[0].nodeName === 'HTML';
+ var rootElementDetected = isMatchingElement(element, $rootElement);
var parentAnimationDetected = false;
var animateChildren;
@@ -2471,19 +2605,34 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
}];
}];
-var $$rAFMutexFactory = ['$$rAF', function($$rAF) {
+var $$AnimateAsyncRunFactory = ['$$rAF', function($$rAF) {
+ var waitQueue = [];
+
+ function waitForTick(fn) {
+ waitQueue.push(fn);
+ if (waitQueue.length > 1) return;
+ $$rAF(function() {
+ for (var i = 0; i < waitQueue.length; i++) {
+ waitQueue[i]();
+ }
+ waitQueue = [];
+ });
+ }
+
return function() {
var passed = false;
- $$rAF(function() {
+ waitForTick(function() {
passed = true;
});
- return function(fn) {
- passed ? fn() : $$rAF(fn);
+ return function(callback) {
+ passed ? callback() : waitForTick(callback);
};
};
}];
-var $$AnimateRunnerFactory = ['$q', '$$rAFMutex', function($q, $$rAFMutex) {
+var $$AnimateRunnerFactory = ['$q', '$sniffer', '$$animateAsyncRun',
+ function($q, $sniffer, $$animateAsyncRun) {
+
var INITIAL_STATE = 0;
var DONE_PENDING_STATE = 1;
var DONE_COMPLETE_STATE = 2;
@@ -2528,7 +2677,7 @@ var $$AnimateRunnerFactory = ['$q', '$$rAFMutex', function($q, $$rAFMutex) {
this.setHost(host);
this._doneCallbacks = [];
- this._runInAnimationFrame = $$rAFMutex();
+ this._runInAnimationFrame = $$animateAsyncRun();
this._state = 0;
}
@@ -2640,15 +2789,93 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
return element.data(RUNNER_STORAGE_KEY);
}
- this.$get = ['$$jqLite', '$rootScope', '$injector', '$$AnimateRunner', '$$rAFScheduler',
- function($$jqLite, $rootScope, $injector, $$AnimateRunner, $$rAFScheduler) {
+ this.$get = ['$$jqLite', '$rootScope', '$injector', '$$AnimateRunner', '$$HashMap', '$$rAFScheduler',
+ function($$jqLite, $rootScope, $injector, $$AnimateRunner, $$HashMap, $$rAFScheduler) {
var animationQueue = [];
var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);
- var totalPendingClassBasedAnimations = 0;
- var totalActiveClassBasedAnimations = 0;
- var classBasedAnimationsQueue = [];
+ function sortAnimations(animations) {
+ var tree = { children: [] };
+ var i, lookup = new $$HashMap();
+
+ // this is done first beforehand so that the hashmap
+ // is filled with a list of the elements that will be animated
+ for (i = 0; i < animations.length; i++) {
+ var animation = animations[i];
+ lookup.put(animation.domNode, animations[i] = {
+ domNode: animation.domNode,
+ fn: animation.fn,
+ children: []
+ });
+ }
+
+ for (i = 0; i < animations.length; i++) {
+ processNode(animations[i]);
+ }
+
+ return flatten(tree);
+
+ function processNode(entry) {
+ if (entry.processed) return entry;
+ entry.processed = true;
+
+ var elementNode = entry.domNode;
+ var parentNode = elementNode.parentNode;
+ lookup.put(elementNode, entry);
+
+ var parentEntry;
+ while (parentNode) {
+ parentEntry = lookup.get(parentNode);
+ if (parentEntry) {
+ if (!parentEntry.processed) {
+ parentEntry = processNode(parentEntry);
+ }
+ break;
+ }
+ parentNode = parentNode.parentNode;
+ }
+
+ (parentEntry || tree).children.push(entry);
+ return entry;
+ }
+
+ function flatten(tree) {
+ var result = [];
+ var queue = [];
+ var i;
+
+ for (i = 0; i < tree.children.length; i++) {
+ queue.push(tree.children[i]);
+ }
+
+ var remainingLevelEntries = queue.length;
+ var nextLevelEntries = 0;
+ var row = [];
+
+ for (i = 0; i < queue.length; i++) {
+ var entry = queue[i];
+ if (remainingLevelEntries <= 0) {
+ remainingLevelEntries = nextLevelEntries;
+ nextLevelEntries = 0;
+ result.push(row);
+ row = [];
+ }
+ row.push(entry.fn);
+ entry.children.forEach(function(childEntry) {
+ nextLevelEntries++;
+ queue.push(childEntry);
+ });
+ remainingLevelEntries--;
+ }
+
+ if (row.length) {
+ result.push(row);
+ }
+
+ return result;
+ }
+ }
// TODO(matsko): document the signature in a better way
return function(element, event, options) {
@@ -2678,19 +2905,12 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
options.tempClasses = null;
}
- var classBasedIndex;
- if (!isStructural) {
- classBasedIndex = totalPendingClassBasedAnimations;
- totalPendingClassBasedAnimations += 1;
- }
-
animationQueue.push({
// this data is used by the postDigest code and passed into
// the driver step function
element: element,
classes: classes,
event: event,
- classBasedIndex: classBasedIndex,
structural: isStructural,
options: options,
beforeStart: beforeStart,
@@ -2705,10 +2925,6 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
if (animationQueue.length > 1) return runner;
$rootScope.$$postDigest(function() {
- totalActiveClassBasedAnimations = totalPendingClassBasedAnimations;
- totalPendingClassBasedAnimations = 0;
- classBasedAnimationsQueue.length = 0;
-
var animations = [];
forEach(animationQueue, function(entry) {
// the element was destroyed early on which removed the runner
@@ -2716,67 +2932,58 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
// at all and it already has been closed due to destruction.
if (getRunner(entry.element)) {
animations.push(entry);
+ } else {
+ entry.close();
}
});
// now any future animations will be in another postDigest
animationQueue.length = 0;
- forEach(groupAnimations(animations), function(animationEntry) {
- if (animationEntry.structural) {
- triggerAnimationStart();
- } else {
- classBasedAnimationsQueue.push({
- node: getDomNode(animationEntry.element),
- fn: triggerAnimationStart
- });
+ var groupedAnimations = groupAnimations(animations);
+ var toBeSortedAnimations = [];
- if (animationEntry.classBasedIndex === totalActiveClassBasedAnimations - 1) {
- // we need to sort each of the animations in order of parent to child
- // relationships. This ensures that the child classes are applied at the
- // right time.
- classBasedAnimationsQueue = classBasedAnimationsQueue.sort(function(a,b) {
- return b.node.contains(a.node);
- }).map(function(entry) {
- return entry.fn;
- });
+ forEach(groupedAnimations, function(animationEntry) {
+ toBeSortedAnimations.push({
+ domNode: getDomNode(animationEntry.from ? animationEntry.from.element : animationEntry.element),
+ fn: function triggerAnimationStart() {
+ // it's important that we apply the `ng-animate` CSS class and the
+ // temporary classes before we do any driver invoking since these
+ // CSS classes may be required for proper CSS detection.
+ animationEntry.beforeStart();
- $$rAFScheduler(classBasedAnimationsQueue);
- }
- }
+ var startAnimationFn, closeFn = animationEntry.close;
- function triggerAnimationStart() {
- // it's important that we apply the `ng-animate` CSS class and the
- // temporary classes before we do any driver invoking since these
- // CSS classes may be required for proper CSS detection.
- animationEntry.beforeStart();
+ // in the event that the element was removed before the digest runs or
+ // during the RAF sequencing then we should not trigger the animation.
+ var targetElement = animationEntry.anchors
+ ? (animationEntry.from.element || animationEntry.to.element)
+ : animationEntry.element;
- var startAnimationFn, closeFn = animationEntry.close;
+ if (getRunner(targetElement)) {
+ var operation = invokeFirstDriver(animationEntry);
+ if (operation) {
+ startAnimationFn = operation.start;
+ }
+ }
- // in the event that the element was removed before the digest runs or
- // during the RAF sequencing then we should not trigger the animation.
- var targetElement = animationEntry.anchors
- ? (animationEntry.from.element || animationEntry.to.element)
- : animationEntry.element;
-
- if (getRunner(targetElement) && getDomNode(targetElement).parentNode) {
- var operation = invokeFirstDriver(animationEntry);
- if (operation) {
- startAnimationFn = operation.start;
+ if (!startAnimationFn) {
+ closeFn();
+ } else {
+ var animationRunner = startAnimationFn();
+ animationRunner.done(function(status) {
+ closeFn(!status);
+ });
+ updateAnimationRunners(animationEntry, animationRunner);
}
}
-
- if (!startAnimationFn) {
- closeFn();
- } else {
- var animationRunner = startAnimationFn();
- animationRunner.done(function(status) {
- closeFn(!status);
- });
- updateAnimationRunners(animationEntry, animationRunner);
- }
- }
+ });
});
+
+ // we need to sort each of the animations in order of parent to child
+ // relationships. This ensures that the child classes are applied at the
+ // right time.
+ $$rAFScheduler(sortAnimations(toBeSortedAnimations));
});
return runner;
@@ -2963,7 +3170,7 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
/* global angularAnimateModule: true,
- $$rAFMutexFactory,
+ $$AnimateAsyncRunFactory,
$$rAFSchedulerFactory,
$$AnimateChildrenDirective,
$$AnimateRunnerFactory,
@@ -2981,7 +3188,7 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
* @description
*
* The `ngAnimate` module provides support for CSS-based animations (keyframes and transitions) as well as JavaScript-based animations via
- * callback hooks. Animations are not enabled by default, however, by including `ngAnimate` then the animation hooks are enabled for an Angular app.
+ * callback hooks. Animations are not enabled by default, however, by including `ngAnimate` the animation hooks are enabled for an Angular app.
*
*
*
@@ -3014,7 +3221,7 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
* CSS-based animations with ngAnimate are unique since they require no JavaScript code at all. By using a CSS class that we reference between our HTML
* and CSS code we can create an animation that will be picked up by Angular when an the underlying directive performs an operation.
*
- * The example below shows how an `enter` animation can be made possible on a element using `ng-if`:
+ * The example below shows how an `enter` animation can be made possible on an element using `ng-if`:
*
* ```html
*
@@ -3149,8 +3356,8 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
* /* this will have a 100ms delay between each successive leave animation */
* transition-delay: 0.1s;
*
- * /* in case the stagger doesn't work then the duration value
- * must be set to 0 to avoid an accidental CSS inheritance */
+ * /* As of 1.4.4, this must always be set: it signals ngAnimate
+ * to not accidentally inherit a delay property from another CSS class */
* transition-duration: 0s;
* }
* .my-animation.ng-enter.ng-enter-active {
@@ -3348,6 +3555,7 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
* enter: function(element, doneFn) {
* var runner = $animateCss(element, {
* event: 'enter',
+ * structural: true,
* addClass: 'maroon-setting',
* from: { height:0 },
* to: { height: 200 }
@@ -3698,15 +3906,14 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
* @description
* The ngAnimate `$animate` service documentation is the same for the core `$animate` service.
*
- * Click here {@link ng.$animate $animate to learn more about animations with `$animate`}.
+ * Click here {@link ng.$animate to learn more about animations with `$animate`}.
*/
angular.module('ngAnimate', [])
.directive('ngAnimateChildren', $$AnimateChildrenDirective)
-
- .factory('$$rAFMutex', $$rAFMutexFactory)
.factory('$$rAFScheduler', $$rAFSchedulerFactory)
.factory('$$AnimateRunner', $$AnimateRunnerFactory)
+ .factory('$$animateAsyncRun', $$AnimateAsyncRunFactory)
.provider('$$animateQueue', $$AnimateQueueProvider)
.provider('$$animation', $$AnimationProvider)
diff --git a/awx/ui/client/lib/angular-animate/angular-animate.min.js b/awx/ui/client/lib/angular-animate/angular-animate.min.js
index a99eac13cc..c24d9e066b 100644
--- a/awx/ui/client/lib/angular-animate/angular-animate.min.js
+++ b/awx/ui/client/lib/angular-animate/angular-animate.min.js
@@ -1,52 +1,56 @@
/*
- AngularJS v1.4.3
+ AngularJS v1.4.7
(c) 2010-2015 Google, Inc. http://angularjs.org
License: MIT
*/
-(function(F,t,W){'use strict';function ua(a,b,c){if(!a)throw ngMinErr("areq",b||"?",c||"required");return a}function va(a,b){if(!a&&!b)return"";if(!a)return b;if(!b)return a;X(a)&&(a=a.join(" "));X(b)&&(b=b.join(" "));return a+" "+b}function Ea(a){var b={};a&&(a.to||a.from)&&(b.to=a.to,b.from=a.from);return b}function ba(a,b,c){var d="";a=X(a)?a:a&&U(a)&&a.length?a.split(/\s+/):[];u(a,function(a,s){a&&0=F&&b>=J&&(C=!0,m())}if(!K)if(g.parentNode){var x,p=[],k=function(a){if(C)D&&a&&(D=!1,m());else if(D=!a,y.animationDuration)if(a=
-ma(g,D),D)l.push(a);else{var b=l,c=b.indexOf(a);0<=a&&b.splice(c,1)}},r=0=a&&(a=m,m=0,b.push(f),f=[]);f.push(g.fn);g.children.forEach(function(a){m++;c.push(a)});a--}f.length&&b.push(f);return b}(c)}var $=[],t=P(a);return function(h,x,A){function Y(a){a=a.hasAttribute("ng-animate-ref")?[a]:a.querySelectorAll("[ng-animate-ref]");var b=[];q(a,function(a){var c=a.getAttribute("ng-animate-ref");c&&c.length&&b.push(a)});return b}function E(a){var b=[],
+c={};q(a,function(a,e){var d=H(a.element),v=0<=["enter","move"].indexOf(a.event),d=a.structural?Y(d):[];if(d.length){var m=v?"to":"from";q(d,function(a){var b=a.getAttribute("ng-animate-ref");c[b]=c[b]||{};c[b][m]={animationID:e,element:J(a)}})}else b.push(a)});var e={},d={};q(c,function(c,m){var f=c.from,y=c.to;if(f&&y){var g=a[f.animationID],r=a[y.animationID],s=f.animationID.toString();if(!d[s]){var h=d[s]={structural:!0,beforeStart:function(){g.beforeStart();r.beforeStart()},close:function(){g.close();
+r.close()},classes:w(g.classes,r.classes),from:g,to:r,anchors:[]};h.classes.length?b.push(h):(b.push(g),b.push(r))}d[s].anchors.push({out:f.element,"in":y.element})}else f=f?f.animationID:y.animationID,y=f.toString(),e[y]||(e[y]=!0,b.push(a[f]))});return b}function w(a,b){a=a.split(" ");b=b.split(" ");for(var c=[],e=0;eF.expectedEndTime)?z.cancel(F.timer):k.push(m)}x&&(l=z(d,l,!1),k[0]={timer:l,expectedEndTime:s},k.push(m),a.data("$$animateCss",k));a.on(h.join(" "),f);c.to&&(c.cleanupStyles&&Da(v,n,Object.keys(c.to)),ya(a,c))}}function d(){var b=a.data("$$animateCss");if(b){for(var c=1;c=M&&b>=K&&(ua=!0,m())}if(!E)if(n.parentNode){var r,h=[],s=function(a){if(ua)l&&a&&(l=!1,m());else if(l=!a,B.animationDuration)if(a=ma(n,l),l)w.push(a);else{var b=w,c=b.indexOf(a);0<=a&&b.splice(c,1)}},k=0}
*/
function parseKeyValue(/**string*/keyValue) {
- var obj = {}, key_value, key;
+ var obj = {};
forEach((keyValue || "").split('&'), function(keyValue) {
+ var splitPoint, key, val;
if (keyValue) {
- key_value = keyValue.replace(/\+/g,'%20').split('=');
- key = tryDecodeURIComponent(key_value[0]);
+ key = keyValue = keyValue.replace(/\+/g,'%20');
+ splitPoint = keyValue.indexOf('=');
+ if (splitPoint !== -1) {
+ key = keyValue.substring(0, splitPoint);
+ val = keyValue.substring(splitPoint + 1);
+ }
+ key = tryDecodeURIComponent(key);
if (isDefined(key)) {
- var val = isDefined(key_value[1]) ? tryDecodeURIComponent(key_value[1]) : true;
+ val = isDefined(val) ? tryDecodeURIComponent(val) : true;
if (!hasOwnProperty.call(obj, key)) {
obj[key] = val;
} else if (isArray(obj[key])) {
@@ -1715,10 +1742,9 @@ function bindJQuery() {
// bind to jQuery if present;
var jqName = jq();
- jQuery = window.jQuery; // use default jQuery.
- if (isDefined(jqName)) { // `ngJq` present
- jQuery = jqName === null ? undefined : window[jqName]; // if empty; use jqLite. if not empty, use jQuery specified by `ngJq`.
- }
+ jQuery = isUndefined(jqName) ? window.jQuery : // use jQuery (if present)
+ !jqName ? undefined : // use jqLite
+ window[jqName]; // use jQuery specified by `ngJq`
// Use jQuery if it exists with proper functionality, otherwise default to us.
// Angular 1.2+ requires jQuery 1.7+ for on()/off() support.
@@ -1823,22 +1849,24 @@ function getter(obj, path, bindFnToScope) {
/**
* Return the DOM siblings between the first and last node in the given array.
* @param {Array} array like object
- * @returns {jqLite} jqLite collection containing the nodes
+ * @returns {Array} the inputted object or a jqLite collection containing the nodes
*/
function getBlockNodes(nodes) {
- // TODO(perf): just check if all items in `nodes` are siblings and if they are return the original
- // collection, otherwise update the original collection.
+ // TODO(perf): update `nodes` instead of creating a new object?
var node = nodes[0];
var endNode = nodes[nodes.length - 1];
- var blockNodes = [node];
+ var blockNodes;
- do {
- node = node.nextSibling;
- if (!node) break;
- blockNodes.push(node);
- } while (node !== endNode);
+ for (var i = 1; node !== endNode && (node = node.nextSibling); i++) {
+ if (blockNodes || nodes[i] !== node) {
+ if (!blockNodes) {
+ blockNodes = jqLite(slice.call(nodes, 0, i));
+ }
+ blockNodes.push(node);
+ }
+ }
- return jqLite(blockNodes);
+ return blockNodes || nodes;
}
@@ -1902,8 +1930,8 @@ function setupModuleLoader(window) {
* All modules (angular core or 3rd party) that should be available to an application must be
* registered using this mechanism.
*
- * When passed two or more arguments, a new module is created. If passed only one argument, an
- * existing module (the name passed as the first argument to `module`) is retrieved.
+ * Passing one argument retrieves an existing {@link angular.Module},
+ * whereas passing more than one argument creates a new {@link angular.Module}
*
*
* # Module
@@ -2222,7 +2250,7 @@ function serializeObject(obj) {
val = toJsonReplacer(key, val);
if (isObject(val)) {
- if (seen.indexOf(val) >= 0) return '<>';
+ if (seen.indexOf(val) >= 0) return '...';
seen.push(val);
}
@@ -2233,7 +2261,7 @@ function serializeObject(obj) {
function toDebugString(obj) {
if (typeof obj === 'function') {
return obj.toString().replace(/ \{[\s\S]*$/, '');
- } else if (typeof obj === 'undefined') {
+ } else if (isUndefined(obj)) {
return 'undefined';
} else if (typeof obj !== 'string') {
return serializeObject(obj);
@@ -2244,7 +2272,6 @@ function toDebugString(obj) {
/* global angularModule: true,
version: true,
- $LocaleProvider,
$CompileProvider,
htmlAnchorDirective,
@@ -2261,7 +2288,6 @@ function toDebugString(obj) {
ngClassDirective,
ngClassEvenDirective,
ngClassOddDirective,
- ngCspDirective,
ngCloakDirective,
ngControllerDirective,
ngFormDirective,
@@ -2298,6 +2324,7 @@ function toDebugString(obj) {
$AnchorScrollProvider,
$AnimateProvider,
+ $CoreAnimateCssProvider,
$$CoreAnimateQueueProvider,
$$CoreAnimateRunnerProvider,
$BrowserProvider,
@@ -2306,6 +2333,7 @@ function toDebugString(obj) {
$DocumentProvider,
$ExceptionHandlerProvider,
$FilterProvider,
+ $$ForceReflowProvider,
$InterpolateProvider,
$IntervalProvider,
$$HashMapProvider,
@@ -2313,6 +2341,7 @@ function toDebugString(obj) {
$HttpParamSerializerProvider,
$HttpParamSerializerJQLikeProvider,
$HttpBackendProvider,
+ $xhrFactoryProvider,
$LocationProvider,
$LogProvider,
$ParseProvider,
@@ -2339,8 +2368,9 @@ function toDebugString(obj) {
* @name angular.version
* @module ng
* @description
- * An object that contains information about the current AngularJS version. This object has the
- * following properties:
+ * An object that contains information about the current AngularJS version.
+ *
+ * This object has the following properties:
*
* - `full` – `{string}` – Full version string, such as "0.9.18".
* - `major` – `{number}` – Major version number, such as "0".
@@ -2349,11 +2379,11 @@ function toDebugString(obj) {
* - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
*/
var version = {
- full: '1.4.3', // all of these placeholder strings will be replaced by grunt's
+ full: '1.4.7', // all of these placeholder strings will be replaced by grunt's
major: 1, // package task
minor: 4,
- dot: 3,
- codeName: 'foam-acceleration'
+ dot: 7,
+ codeName: 'dark-luminescence'
};
@@ -2392,11 +2422,6 @@ function publishExternalAPI(angular) {
});
angularModule = setupModuleLoader(window);
- try {
- angularModule('ngLocale');
- } catch (e) {
- angularModule('ngLocale', []).provider('$locale', $LocaleProvider);
- }
angularModule('ng', ['ngLocale'], ['$provide',
function ngModule($provide) {
@@ -2459,6 +2484,7 @@ function publishExternalAPI(angular) {
$provide.provider({
$anchorScroll: $AnchorScrollProvider,
$animate: $AnimateProvider,
+ $animateCss: $CoreAnimateCssProvider,
$$animateQueue: $$CoreAnimateQueueProvider,
$$AnimateRunner: $$CoreAnimateRunnerProvider,
$browser: $BrowserProvider,
@@ -2467,12 +2493,14 @@ function publishExternalAPI(angular) {
$document: $DocumentProvider,
$exceptionHandler: $ExceptionHandlerProvider,
$filter: $FilterProvider,
+ $$forceReflow: $$ForceReflowProvider,
$interpolate: $InterpolateProvider,
$interval: $IntervalProvider,
$http: $HttpProvider,
$httpParamSerializer: $HttpParamSerializerProvider,
$httpParamSerializerJQLike: $HttpParamSerializerJQLikeProvider,
$httpBackend: $HttpBackendProvider,
+ $xhrFactory: $xhrFactoryProvider,
$location: $LocationProvider,
$log: $LogProvider,
$parse: $ParseProvider,
@@ -2561,7 +2589,7 @@ function publishExternalAPI(angular) {
* - [`html()`](http://api.jquery.com/html/)
* - [`next()`](http://api.jquery.com/next/) - Does not support selectors
* - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData
- * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces or selectors
+ * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces, selectors or event object as parameter
* - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors
* - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors
* - [`prepend()`](http://api.jquery.com/prepend/)
@@ -2575,7 +2603,7 @@ function publishExternalAPI(angular) {
* - [`text()`](http://api.jquery.com/text/)
* - [`toggleClass()`](http://api.jquery.com/toggleClass/)
* - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers.
- * - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces
+ * - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces or event object as parameter
* - [`val()`](http://api.jquery.com/val/)
* - [`wrap()`](http://api.jquery.com/wrap/)
*
@@ -2647,10 +2675,10 @@ function camelCase(name) {
replace(MOZ_HACK_REGEXP, 'Moz$1');
}
-var SINGLE_TAG_REGEXP = /^<(\w+)\s*\/?>(?:<\/\1>|)$/;
+var SINGLE_TAG_REGEXP = /^<([\w-]+)\s*\/?>(?:<\/\1>|)$/;
var HTML_REGEXP = /<|?\w+;/;
-var TAG_NAME_REGEXP = /<([\w:]+)/;
-var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi;
+var TAG_NAME_REGEXP = /<([\w:-]+)/;
+var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi;
var wrapMap = {
'option': [1, ''],
@@ -2946,7 +2974,7 @@ function jqLiteInheritedData(element, name, value) {
while (element) {
for (var i = 0, ii = names.length; i < ii; i++) {
- if ((value = jqLite.data(element, names[i])) !== undefined) return value;
+ if (isDefined(value = jqLite.data(element, names[i]))) return value;
}
// If dealing with a document fragment node with a host element, and no parent, use the host
@@ -3052,9 +3080,8 @@ function getBooleanAttrName(element, name) {
return booleanAttr && BOOLEAN_ELEMENTS[nodeName_(element)] && booleanAttr;
}
-function getAliasedAttrName(element, name) {
- var nodeName = element.nodeName;
- return (nodeName === 'INPUT' || nodeName === 'TEXTAREA') && ALIASED_ATTR[name];
+function getAliasedAttrName(name) {
+ return ALIASED_ATTR[name];
}
forEach({
@@ -3191,7 +3218,7 @@ forEach({
// in a way that survives minification.
// jqLiteEmpty takes no arguments but is a setter.
if (fn !== jqLiteEmpty &&
- (((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2) === undefined)) {
+ (isUndefined((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2))) {
if (isObject(arg1)) {
// we are a write, but the object properties are the key/values
@@ -3212,7 +3239,7 @@ forEach({
// TODO: do we still need this?
var value = fn.$dv;
// Only if we have $dv do we iterate over all, otherwise it is just the first element.
- var jj = (value === undefined) ? Math.min(nodeCount, 1) : nodeCount;
+ var jj = (isUndefined(value)) ? Math.min(nodeCount, 1) : nodeCount;
for (var j = 0; j < jj; j++) {
var nodeValue = fn(this[j], arg1, arg2);
value = value ? value + nodeValue : nodeValue;
@@ -3686,7 +3713,7 @@ var $$HashMapProvider = [function() {
* Implicit module which gets automatically added to each {@link auto.$injector $injector}.
*/
-var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
+var FN_ARGS = /^[^\(]*\(\s*([^\)]*)\)/m;
var FN_ARG_SPLIT = /,/;
var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
@@ -4342,6 +4369,7 @@ function createInjector(modulesToLoad, strictDi) {
// Module Loading
////////////////////////////////////
function loadModules(modulesToLoad) {
+ assertArg(isUndefined(modulesToLoad) || isArray(modulesToLoad), 'modulesToLoad', 'not an array');
var runBlocks = [], moduleFn;
forEach(modulesToLoad, function(module) {
if (loadedModules.get(module)) return;
@@ -4850,61 +4878,66 @@ var $$CoreAnimateQueueProvider = function() {
}
};
- function addRemoveClassesPostDigest(element, add, remove) {
- var data = postDigestQueue.get(element);
- var classVal;
- if (!data) {
- postDigestQueue.put(element, data = {});
- postDigestElements.push(element);
- }
-
- if (add) {
- forEach(add.split(' '), function(className) {
+ function updateData(data, classes, value) {
+ var changed = false;
+ if (classes) {
+ classes = isString(classes) ? classes.split(' ') :
+ isArray(classes) ? classes : [];
+ forEach(classes, function(className) {
if (className) {
- data[className] = true;
+ changed = true;
+ data[className] = value;
}
});
}
+ return changed;
+ }
- if (remove) {
- forEach(remove.split(' '), function(className) {
- if (className) {
- data[className] = false;
- }
- });
- }
-
- if (postDigestElements.length > 1) return;
-
- $rootScope.$$postDigest(function() {
- forEach(postDigestElements, function(element) {
- var data = postDigestQueue.get(element);
- if (data) {
- var existing = splitClasses(element.attr('class'));
- var toAdd = '';
- var toRemove = '';
- forEach(data, function(status, className) {
- var hasClass = !!existing[className];
- if (status !== hasClass) {
- if (status) {
- toAdd += (toAdd.length ? ' ' : '') + className;
- } else {
- toRemove += (toRemove.length ? ' ' : '') + className;
- }
+ function handleCSSClassChanges() {
+ forEach(postDigestElements, function(element) {
+ var data = postDigestQueue.get(element);
+ if (data) {
+ var existing = splitClasses(element.attr('class'));
+ var toAdd = '';
+ var toRemove = '';
+ forEach(data, function(status, className) {
+ var hasClass = !!existing[className];
+ if (status !== hasClass) {
+ if (status) {
+ toAdd += (toAdd.length ? ' ' : '') + className;
+ } else {
+ toRemove += (toRemove.length ? ' ' : '') + className;
}
- });
+ }
+ });
- forEach(element, function(elm) {
- toAdd && jqLiteAddClass(elm, toAdd);
- toRemove && jqLiteRemoveClass(elm, toRemove);
- });
- postDigestQueue.remove(element);
- }
- });
-
- postDigestElements.length = 0;
+ forEach(element, function(elm) {
+ toAdd && jqLiteAddClass(elm, toAdd);
+ toRemove && jqLiteRemoveClass(elm, toRemove);
+ });
+ postDigestQueue.remove(element);
+ }
});
+ postDigestElements.length = 0;
+ }
+
+
+ function addRemoveClassesPostDigest(element, add, remove) {
+ var data = postDigestQueue.get(element) || {};
+
+ var classesAdded = updateData(data, add, true);
+ var classesRemoved = updateData(data, remove, false);
+
+ if (classesAdded || classesRemoved) {
+
+ postDigestQueue.put(element, data);
+ postDigestElements.push(element);
+
+ if (postDigestElements.length === 1) {
+ $rootScope.$$postDigest(handleCSSClassChanges);
+ }
+ }
}
}];
};
@@ -5334,15 +5367,95 @@ var $AnimateProvider = ['$provide', function($provide) {
}];
}];
-function $$AsyncCallbackProvider() {
- this.$get = ['$$rAF', '$timeout', function($$rAF, $timeout) {
- return $$rAF.supported
- ? function(fn) { return $$rAF(fn); }
- : function(fn) {
- return $timeout(fn, 0, false);
+/**
+ * @ngdoc service
+ * @name $animateCss
+ * @kind object
+ *
+ * @description
+ * This is the core version of `$animateCss`. By default, only when the `ngAnimate` is included,
+ * then the `$animateCss` service will actually perform animations.
+ *
+ * Click here {@link ngAnimate.$animateCss to read the documentation for $animateCss}.
+ */
+var $CoreAnimateCssProvider = function() {
+ this.$get = ['$$rAF', '$q', function($$rAF, $q) {
+
+ var RAFPromise = function() {};
+ RAFPromise.prototype = {
+ done: function(cancel) {
+ this.defer && this.defer[cancel === true ? 'reject' : 'resolve']();
+ },
+ end: function() {
+ this.done();
+ },
+ cancel: function() {
+ this.done(true);
+ },
+ getPromise: function() {
+ if (!this.defer) {
+ this.defer = $q.defer();
+ }
+ return this.defer.promise;
+ },
+ then: function(f1,f2) {
+ return this.getPromise().then(f1,f2);
+ },
+ 'catch': function(f1) {
+ return this.getPromise()['catch'](f1);
+ },
+ 'finally': function(f1) {
+ return this.getPromise()['finally'](f1);
+ }
+ };
+
+ return function(element, options) {
+ // there is no point in applying the styles since
+ // there is no animation that goes on at all in
+ // this version of $animateCss.
+ if (options.cleanupStyles) {
+ options.from = options.to = null;
+ }
+
+ if (options.from) {
+ element.css(options.from);
+ options.from = null;
+ }
+
+ var closed, runner = new RAFPromise();
+ return {
+ start: run,
+ end: run
};
+
+ function run() {
+ $$rAF(function() {
+ close();
+ if (!closed) {
+ runner.done();
+ }
+ closed = true;
+ });
+ return runner;
+ }
+
+ function close() {
+ if (options.addClass) {
+ element.addClass(options.addClass);
+ options.addClass = null;
+ }
+ if (options.removeClass) {
+ element.removeClass(options.removeClass);
+ options.removeClass = null;
+ }
+ if (options.to) {
+ element.css(options.to);
+ options.to = null;
+ }
+ }
+ };
}];
-}
+};
/* global stripHash: true */
@@ -5432,7 +5545,7 @@ function Browser(window, document, $log, $sniffer) {
var cachedState, lastHistoryState,
lastBrowserUrl = location.href,
baseElement = document.find('base'),
- reloadLocation = null;
+ pendingLocation = null;
cacheState();
lastHistoryState = cachedState;
@@ -5492,8 +5605,8 @@ function Browser(window, document, $log, $sniffer) {
// Do the assignment again so that those two variables are referentially identical.
lastHistoryState = cachedState;
} else {
- if (!sameBase || reloadLocation) {
- reloadLocation = url;
+ if (!sameBase || pendingLocation) {
+ pendingLocation = url;
}
if (replace) {
location.replace(url);
@@ -5502,14 +5615,18 @@ function Browser(window, document, $log, $sniffer) {
} else {
location.hash = getHash(url);
}
+ if (location.href !== url) {
+ pendingLocation = url;
+ }
}
return self;
// getter
} else {
- // - reloadLocation is needed as browsers don't allow to read out
- // the new location.href if a reload happened.
+ // - pendingLocation is needed as browsers don't allow to read out
+ // the new location.href if a reload happened or if there is a bug like in iOS 9 (see
+ // https://openradar.appspot.com/22186109).
// - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
- return reloadLocation || location.href.replace(/%27/g,"'");
+ return pendingLocation || location.href.replace(/%27/g,"'");
}
};
@@ -5531,6 +5648,7 @@ function Browser(window, document, $log, $sniffer) {
urlChangeInit = false;
function cacheStateAndFireUrlChange() {
+ pendingLocation = null;
cacheState();
fireUrlChange();
}
@@ -5766,10 +5884,10 @@ function $BrowserProvider() {
$scope.keys = [];
$scope.cache = $cacheFactory('cacheId');
$scope.put = function(key, value) {
- if ($scope.cache.get(key) === undefined) {
+ if (angular.isUndefined($scope.cache.get(key))) {
$scope.keys.push(key);
}
- $scope.cache.put(key, value === undefined ? null : value);
+ $scope.cache.put(key, angular.isUndefined(value) ? null : value);
};
}]);
@@ -6245,18 +6363,24 @@ function $TemplateCacheProvider() {
* and other directives used in the directive's template will also be excluded from execution.
*
* #### `scope`
- * **If set to `true`,** then a new scope will be created for this directive. If multiple directives on the
- * same element request a new scope, only one new scope is created. The new scope rule does not
- * apply for the root of the template since the root of the template always gets a new scope.
+ * The scope property can be `true`, an object or a falsy value:
*
- * **If set to `{}` (object hash),** then a new "isolate" scope is created. The 'isolate' scope differs from
- * normal scope in that it does not prototypically inherit from the parent scope. This is useful
- * when creating reusable components, which should not accidentally read or modify data in the
- * parent scope.
+ * * **falsy:** No scope will be created for the directive. The directive will use its parent's scope.
*
- * The 'isolate' scope takes an object hash which defines a set of local scope properties
- * derived from the parent scope. These local properties are useful for aliasing values for
- * templates. Locals definition is a hash of local scope property to its source:
+ * * **`true`:** A new child scope that prototypically inherits from its parent will be created for
+ * the directive's element. If multiple directives on the same element request a new scope,
+ * only one new scope is created. The new scope rule does not apply for the root of the template
+ * since the root of the template always gets a new scope.
+ *
+ * * **`{...}` (an object hash):** A new "isolate" scope is created for the directive's element. The
+ * 'isolate' scope differs from normal scope in that it does not prototypically inherit from its parent
+ * scope. This is useful when creating reusable components, which should not accidentally read or modify
+ * data in the parent scope.
+ *
+ * The 'isolate' scope object hash defines a set of local scope properties derived from attributes on the
+ * directive's element. These local properties are useful for aliasing values for templates. The keys in
+ * the object hash map to the name of the property on the isolate scope; the values define how the property
+ * is bound to the parent scope, via matching attributes on the directive's element:
*
* * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is
* always a string since DOM attributes are strings. If no `attr` name is specified then the
@@ -6289,6 +6413,20 @@ function $TemplateCacheProvider() {
* For example, if the expression is `increment(amount)` then we can specify the amount value
* by calling the `localFn` as `localFn({amount: 22})`.
*
+ * In general it's possible to apply more than one directive to one element, but there might be limitations
+ * depending on the type of scope required by the directives. The following points will help explain these limitations.
+ * For simplicity only two directives are taken into account, but it is also applicable for several directives:
+ *
+ * * **no scope** + **no scope** => Two directives which don't require their own scope will use their parent's scope
+ * * **child scope** + **no scope** => Both directives will share one single child scope
+ * * **child scope** + **child scope** => Both directives will share one single child scope
+ * * **isolated scope** + **no scope** => The isolated directive will use it's own created isolated scope. The other directive will use
+ * its parent's scope
+ * * **isolated scope** + **child scope** => **Won't work!** Only one scope can be related to one element. Therefore these directives cannot
+ * be applied to the same element.
+ * * **isolated scope** + **isolated scope** => **Won't work!** Only one scope can be related to one element. Therefore these directives
+ * cannot be applied to the same element.
+ *
*
* #### `bindToController`
* When an isolate scope is used for a component (see above), and `controllerAs` is used, `bindToController: true` will
@@ -6297,7 +6435,7 @@ function $TemplateCacheProvider() {
*
* #### `controller`
* Controller constructor function. The controller is instantiated before the
- * pre-linking phase and it is shared with other directives (see
+ * pre-linking phase and can be accessed by other directives (see
* `require` attribute). This allows the directives to communicate with each other and augment
* each other's behavior. The controller is injectable (and supports bracket notation) with the following locals:
*
@@ -6337,9 +6475,10 @@ function $TemplateCacheProvider() {
*
* #### `controllerAs`
* Identifier name for a reference to the controller in the directive's scope.
- * This allows the controller to be referenced from the directive template. The directive
- * needs to define a scope for this configuration to be used. Useful in the case when
- * directive is used as component.
+ * This allows the controller to be referenced from the directive template. This is especially
+ * useful when a directive is used as component, i.e. with an `isolate` scope. It's also possible
+ * to use it in a directive without an `isolate` / `new` scope, but you need to be aware that the
+ * `controllerAs` reference might overwrite a property that already exists on the parent scope.
*
*
* #### `restrict`
@@ -6506,7 +6645,7 @@ function $TemplateCacheProvider() {
* otherwise the {@link error:$compile:ctreq Missing Required Controller} error is thrown.
*
* Note that you can also require the directive's own controller - it will be made available like
- * like any other controller.
+ * any other controller.
*
* * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope.
* This is the same as the `$transclude`
@@ -6532,7 +6671,7 @@ function $TemplateCacheProvider() {
*
* ### Transclusion
*
- * Transclusion is the process of extracting a collection of DOM element from one part of the DOM and
+ * Transclusion is the process of extracting a collection of DOM elements from one part of the DOM and
* copying them to another part of the DOM, while maintaining their connection to the original AngularJS
* scope from where they were taken.
*
@@ -7176,7 +7315,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
var node = this.$$element[0],
booleanKey = getBooleanAttrName(node, key),
- aliasedKey = getAliasedAttrName(node, key),
+ aliasedKey = getAliasedAttrName(key),
observer = key,
nodeName;
@@ -7243,7 +7382,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
}
if (writeAttr !== false) {
- if (value === null || value === undefined) {
+ if (value === null || isUndefined(value)) {
this.$$element.removeAttr(attrName);
} else {
this.$$element.attr(attrName, value);
@@ -7287,7 +7426,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
listeners.push(fn);
$rootScope.$evalAsync(function() {
- if (!listeners.$$inter && attrs.hasOwnProperty(key)) {
+ if (!listeners.$$inter && attrs.hasOwnProperty(key) && !isUndefined(attrs[key])) {
// no one registered attribute interpolation function, so lets call it manually
fn(attrs[key]);
}
@@ -8209,7 +8348,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
i = 0, ii = directives.length; i < ii; i++) {
try {
directive = directives[i];
- if ((maxPriority === undefined || maxPriority > directive.priority) &&
+ if ((isUndefined(maxPriority) || maxPriority > directive.priority) &&
directive.restrict.indexOf(location) != -1) {
if (startAttrName) {
directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName});
@@ -8505,7 +8644,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
compile: function() {
return {
pre: function attrInterpolatePreLinkFn(scope, element, attr) {
- var $$observers = (attr.$$observers || (attr.$$observers = {}));
+ var $$observers = (attr.$$observers || (attr.$$observers = createMap()));
if (EVENT_HANDLER_ATTR_REGEXP.test(name)) {
throw $compileMinErr('nodomevents',
@@ -8666,24 +8805,19 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
lastValue,
parentGet, parentSet, compare;
- if (!hasOwnProperty.call(attrs, attrName)) {
- // In the case of user defined a binding with the same name as a method in Object.prototype but didn't set
- // the corresponding attribute. We need to make sure subsequent code won't access to the prototype function
- attrs[attrName] = undefined;
- }
-
switch (mode) {
case '@':
- if (!attrs[attrName] && !optional) {
- destination[scopeName] = undefined;
+ if (!optional && !hasOwnProperty.call(attrs, attrName)) {
+ destination[scopeName] = attrs[attrName] = void 0;
}
-
attrs.$observe(attrName, function(value) {
- destination[scopeName] = value;
+ if (isString(value)) {
+ destination[scopeName] = value;
+ }
});
attrs.$$observers[attrName].$$scope = scope;
- if (attrs[attrName]) {
+ if (isString(attrs[attrName])) {
// If the attribute has been provided then we trigger an interpolation to ensure
// the value is there for use in the link fn
destination[scopeName] = $interpolate(attrs[attrName])(scope);
@@ -8691,11 +8825,13 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
break;
case '=':
- if (optional && !attrs[attrName]) {
- return;
+ if (!hasOwnProperty.call(attrs, attrName)) {
+ if (optional) break;
+ attrs[attrName] = void 0;
}
- parentGet = $parse(attrs[attrName]);
+ if (optional && !attrs[attrName]) break;
+ parentGet = $parse(attrs[attrName]);
if (parentGet.literal) {
compare = equals;
} else {
@@ -8734,7 +8870,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
break;
case '&':
- parentGet = $parse(attrs[attrName]);
+ // Don't assign Object.prototype method to scope
+ parentGet = attrs.hasOwnProperty(attrName) ? $parse(attrs[attrName]) : noop;
// Don't assign noop to destination if expression is not valid
if (parentGet === noop && optional) break;
@@ -9111,6 +9248,29 @@ function $ExceptionHandlerProvider() {
}];
}
+var $$ForceReflowProvider = function() {
+ this.$get = ['$document', function($document) {
+ return function(domNode) {
+ //the line below will force the browser to perform a repaint so
+ //that all the animated elements within the animation frame will
+ //be properly updated and drawn on screen. This is required to
+ //ensure that the preparation animation is properly flushed so that
+ //the active state picks up from there. DO NOT REMOVE THIS LINE.
+ //DO NOT OPTIMIZE THIS LINE. THE MINIFIER WILL REMOVE IT OTHERWISE WHICH
+ //WILL RESULT IN AN UNPREDICTABLE BUG THAT IS VERY HARD TO TRACK DOWN AND
+ //WILL TAKE YEARS AWAY FROM YOUR LIFE.
+ if (domNode) {
+ if (!domNode.nodeType && domNode instanceof jqLite) {
+ domNode = domNode[0];
+ }
+ } else {
+ domNode = $document[0].body;
+ }
+ return domNode.offsetWidth + 1;
+ };
+ }];
+};
+
var APPLICATION_JSON = 'application/json';
var CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'};
var JSON_START = /^\[|^\{(?!\{)/;
@@ -9119,6 +9279,12 @@ var JSON_ENDS = {
'{': /}$/
};
var JSON_PROTECTION_PREFIX = /^\)\]\}',?\n/;
+var $httpMinErr = minErr('$http');
+var $httpMinErrLegacyFn = function(method) {
+ return function() {
+ throw $httpMinErr('legacy', 'The method `{0}` on the promise returned from `$http` has been disabled.', method);
+ };
+};
function serializeValue(v) {
if (isObject(v)) {
@@ -9219,8 +9385,8 @@ function $HttpParamSerializerJQLikeProvider() {
function serialize(toSerialize, prefix, topLevel) {
if (toSerialize === null || isUndefined(toSerialize)) return;
if (isArray(toSerialize)) {
- forEach(toSerialize, function(value) {
- serialize(value, prefix + '[]');
+ forEach(toSerialize, function(value, index) {
+ serialize(value, prefix + '[' + (isObject(value) ? index : '') + ']');
});
} else if (isObject(toSerialize) && !isDate(toSerialize)) {
forEachSorted(toSerialize, function(value, key) {
@@ -9441,6 +9607,30 @@ function $HttpProvider() {
return useApplyAsync;
};
+ var useLegacyPromise = true;
+ /**
+ * @ngdoc method
+ * @name $httpProvider#useLegacyPromiseExtensions
+ * @description
+ *
+ * Configure `$http` service to return promises without the shorthand methods `success` and `error`.
+ * This should be used to make sure that applications work without these methods.
+ *
+ * Defaults to false. If no value is specified, returns the current configured value.
+ *
+ * @param {boolean=} value If true, `$http` will return a normal promise without the `success` and `error` methods.
+ *
+ * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
+ * otherwise, returns the current configured value.
+ **/
+ this.useLegacyPromiseExtensions = function(value) {
+ if (isDefined(value)) {
+ useLegacyPromise = !!value;
+ return this;
+ }
+ return useLegacyPromise;
+ };
+
/**
* @ngdoc property
* @name $httpProvider#interceptors
@@ -9506,66 +9696,47 @@ function $HttpProvider() {
*
*
* ## General usage
- * The `$http` service is a function which takes a single argument — a configuration object —
- * that is used to generate an HTTP request and returns a {@link ng.$q promise}
- * with two $http specific methods: `success` and `error`.
+ * The `$http` service is a function which takes a single argument — a {@link $http#usage configuration object} —
+ * that is used to generate an HTTP request and returns a {@link ng.$q promise}.
*
* ```js
- * // Simple GET request example :
- * $http.get('/someUrl').
- * success(function(data, status, headers, config) {
+ * // Simple GET request example:
+ * $http({
+ * method: 'GET',
+ * url: '/someUrl'
+ * }).then(function successCallback(response) {
* // this callback will be called asynchronously
* // when the response is available
- * }).
- * error(function(data, status, headers, config) {
+ * }, function errorCallback(response) {
* // called asynchronously if an error occurs
* // or server returns response with an error status.
* });
* ```
*
- * ```js
- * // Simple POST request example (passing data) :
- * $http.post('/someUrl', {msg:'hello word!'}).
- * success(function(data, status, headers, config) {
- * // this callback will be called asynchronously
- * // when the response is available
- * }).
- * error(function(data, status, headers, config) {
- * // called asynchronously if an error occurs
- * // or server returns response with an error status.
- * });
- * ```
+ * The response object has these properties:
*
- *
- * Since the returned value of calling the $http function is a `promise`, you can also use
- * the `then` method to register callbacks, and these callbacks will receive a single argument –
- * an object representing the response. See the API signature and type info below for more
- * details.
+ * - **data** – `{string|Object}` – The response body transformed with the transform
+ * functions.
+ * - **status** – `{number}` – HTTP status code of the response.
+ * - **headers** – `{function([headerName])}` – Header getter function.
+ * - **config** – `{Object}` – The configuration object that was used to generate the request.
+ * - **statusText** – `{string}` – HTTP status text of the response.
*
* A response status code between 200 and 299 is considered a success status and
* will result in the success callback being called. Note that if the response is a redirect,
* XMLHttpRequest will transparently follow it, meaning that the error callback will not be
* called for such responses.
*
- * ## Writing Unit Tests that use $http
- * When unit testing (using {@link ngMock ngMock}), it is necessary to call
- * {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending
- * request using trained responses.
- *
- * ```
- * $httpBackend.expectGET(...);
- * $http.get(...);
- * $httpBackend.flush();
- * ```
*
* ## Shortcut methods
*
* Shortcut methods are also available. All shortcut methods require passing in the URL, and
- * request data must be passed in for POST/PUT requests.
+ * request data must be passed in for POST/PUT requests. An optional config can be passed as the
+ * last argument.
*
* ```js
- * $http.get('/someUrl').success(successCallback);
- * $http.post('/someUrl', data).success(successCallback);
+ * $http.get('/someUrl', config).then(successCallback, errorCallback);
+ * $http.post('/someUrl', data, config).then(successCallback, errorCallback);
* ```
*
* Complete list of shortcut methods:
@@ -9579,6 +9750,25 @@ function $HttpProvider() {
* - {@link ng.$http#patch $http.patch}
*
*
+ * ## Writing Unit Tests that use $http
+ * When unit testing (using {@link ngMock ngMock}), it is necessary to call
+ * {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending
+ * request using trained responses.
+ *
+ * ```
+ * $httpBackend.expectGET(...);
+ * $http.get(...);
+ * $httpBackend.flush();
+ * ```
+ *
+ * ## Deprecation Notice
+ *
+ * The `$http` legacy promise methods `success` and `error` have been deprecated.
+ * Use the standard `then` method instead.
+ * If {@link $httpProvider#useLegacyPromiseExtensions `$httpProvider.useLegacyPromiseExtensions`} is set to
+ * `false` then these methods will throw {@link $http:legacy `$http/legacy`} error.
+ *
+ *
* ## Setting HTTP Headers
*
* The $http service will automatically add certain HTTP headers to all requests. These defaults
@@ -9622,7 +9812,7 @@ function $HttpProvider() {
* data: { test: 'test' }
* }
*
- * $http(req).success(function(){...}).error(function(){...});
+ * $http(req).then(function(){...}, function(){...});
* ```
*
* ## Transforming Requests and Responses
@@ -9728,7 +9918,7 @@ function $HttpProvider() {
*
* There are two kinds of interceptors (and two kinds of rejection interceptors):
*
- * * `request`: interceptors get called with a http `config` object. The function is free to
+ * * `request`: interceptors get called with a http {@link $http#usage config} object. The function is free to
* modify the `config` object or create a new one. The function needs to return the `config`
* object directly, or a promise containing the `config` or a new `config` object.
* * `requestError`: interceptor gets called when a previous interceptor threw an error or
@@ -9854,7 +10044,6 @@ function $HttpProvider() {
* In order to prevent collisions in environments where multiple Angular apps share the
* same domain or subdomain, we recommend that each application uses unique cookie name.
*
- *
* @param {object} config Object describing the request to be made and how it should be
* processed. The object has following properties:
*
@@ -9899,20 +10088,9 @@ function $HttpProvider() {
* - **responseType** - `{string}` - see
* [XMLHttpRequest.responseType](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#xmlhttprequest-responsetype).
*
- * @returns {HttpPromise} Returns a {@link ng.$q promise} object with the
- * standard `then` method and two http specific methods: `success` and `error`. The `then`
- * method takes two arguments a success and an error callback which will be called with a
- * response object. The `success` and `error` methods take a single argument - a function that
- * will be called when the request succeeds or fails respectively. The arguments passed into
- * these functions are destructured representation of the response object passed into the
- * `then` method. The response object has these properties:
+ * @returns {HttpPromise} Returns a {@link ng.$q `Promise}` that will be resolved to a response object
+ * when the request succeeds or fails.
*
- * - **data** – `{string|Object}` – The response body transformed with the transform
- * functions.
- * - **status** – `{number}` – HTTP status code of the response.
- * - **headers** – `{function([headerName])}` – Header getter function.
- * - **config** – `{Object}` – The configuration object that was used to generate the request.
- * - **statusText** – `{string}` – HTTP status text of the response.
*
* @property {Array.
+ * @param {Function} factory If the first argument was a string, a factory function for the filter to be registered.
* @returns {Object} Registered filter instance, or if a map of filters was provided then a map
* of the registered filter instances.
*/
@@ -18415,9 +18555,9 @@ function getTypeForFilter(val) {
}
element(by.model('amount')).clear();
element(by.model('amount')).sendKeys('-1234');
- expect(element(by.id('currency-default')).getText()).toBe('($1,234.00)');
- expect(element(by.id('currency-custom')).getText()).toBe('(USD$1,234.00)');
- expect(element(by.id('currency-no-fractions')).getText()).toBe('(USD$1,234)');
+ expect(element(by.id('currency-default')).getText()).toBe('-$1,234.00');
+ expect(element(by.id('currency-custom')).getText()).toBe('-USD$1,234.00');
+ expect(element(by.id('currency-no-fractions')).getText()).toBe('-USD$1,234');
});
@@ -18584,6 +18724,7 @@ function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
if (fractionSize > 0 && number < 1) {
formatedText = number.toFixed(fractionSize);
number = parseFloat(formatedText);
+ formatedText = formatedText.replace(DECIMAL_SEP, decimalSep);
}
}
@@ -19257,6 +19398,10 @@ function orderByFilter($parse) {
if (sortPredicate.length === 0) { sortPredicate = ['+']; }
var predicates = processPredicates(sortPredicate, reverseOrder);
+ // Add a predicate at the end that evaluates to the element index. This makes the
+ // sort stable as it works as a tie-breaker when all the input predicates cannot
+ // distinguish between two elements.
+ predicates.push({ get: function() { return {}; }, descending: reverseOrder ? -1 : 1});
// The next three lines are a version of a Swartzian Transform idiom from Perl
// (sometimes called the Decorate-Sort-Undecorate idiom)
@@ -19878,6 +20023,7 @@ function nullFormRenameControl(control, name) {
* @property {boolean} $dirty True if user has already interacted with the form.
* @property {boolean} $valid True if all of the containing forms and controls are valid.
* @property {boolean} $invalid True if at least one containing control or form is invalid.
+ * @property {boolean} $pending True if at least one containing control or form is pending.
* @property {boolean} $submitted True if user has submitted the form even if its invalid.
*
* @property {Object} $error Is an object hash, containing references to controls or
@@ -19917,8 +20063,6 @@ function FormController(element, attrs, $scope, $animate, $interpolate) {
var form = this,
controls = [];
- var parentForm = form.$$parentForm = element.parent().controller('form') || nullFormCtrl;
-
// init state
form.$error = {};
form.$$success = {};
@@ -19929,8 +20073,7 @@ function FormController(element, attrs, $scope, $animate, $interpolate) {
form.$valid = true;
form.$invalid = false;
form.$submitted = false;
-
- parentForm.$addControl(form);
+ form.$$parentForm = nullFormCtrl;
/**
* @ngdoc method
@@ -19969,11 +20112,23 @@ function FormController(element, attrs, $scope, $animate, $interpolate) {
/**
* @ngdoc method
* @name form.FormController#$addControl
+ * @param {object} control control object, either a {@link form.FormController} or an
+ * {@link ngModel.NgModelController}
*
* @description
- * Register a control with the form.
+ * Register a control with the form. Input elements using ngModelController do this automatically
+ * when they are linked.
*
- * Input elements using ngModelController do this automatically when they are linked.
+ * Note that the current state of the control will not be reflected on the new parent form. This
+ * is not an issue with normal use, as freshly compiled and linked controls are in a `$pristine`
+ * state.
+ *
+ * However, if the method is used programmatically, for example by adding dynamically created controls,
+ * or controls that have been previously removed without destroying their corresponding DOM element,
+ * it's the developers responsiblity to make sure the current state propagates to the parent form.
+ *
+ * For example, if an input control is added that is already `$dirty` and has `$error` properties,
+ * calling `$setDirty()` and `$validate()` afterwards will propagate the state to the parent form.
*/
form.$addControl = function(control) {
// Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored
@@ -19984,6 +20139,8 @@ function FormController(element, attrs, $scope, $animate, $interpolate) {
if (control.$name) {
form[control.$name] = control;
}
+
+ control.$$parentForm = form;
};
// Private API: rename a form control
@@ -20000,11 +20157,18 @@ function FormController(element, attrs, $scope, $animate, $interpolate) {
/**
* @ngdoc method
* @name form.FormController#$removeControl
+ * @param {object} control control object, either a {@link form.FormController} or an
+ * {@link ngModel.NgModelController}
*
* @description
* Deregister a control from the form.
*
* Input elements using ngModelController do this automatically when they are destroyed.
+ *
+ * Note that only the removed control's validation state (`$errors`etc.) will be removed from the
+ * form. `$dirty`, `$submitted` states will not be changed, because the expected behavior can be
+ * different from case to case. For example, removing the only `$dirty` control from a form may or
+ * may not mean that the form is still `$dirty`.
*/
form.$removeControl = function(control) {
if (control.$name && form[control.$name] === control) {
@@ -20021,6 +20185,7 @@ function FormController(element, attrs, $scope, $animate, $interpolate) {
});
arrayRemove(controls, control);
+ control.$$parentForm = nullFormCtrl;
};
@@ -20057,7 +20222,6 @@ function FormController(element, attrs, $scope, $animate, $interpolate) {
delete object[property];
}
},
- parentForm: parentForm,
$animate: $animate
});
@@ -20076,7 +20240,7 @@ function FormController(element, attrs, $scope, $animate, $interpolate) {
$animate.addClass(element, DIRTY_CLASS);
form.$dirty = true;
form.$pristine = false;
- parentForm.$setDirty();
+ form.$$parentForm.$setDirty();
};
/**
@@ -20132,7 +20296,7 @@ function FormController(element, attrs, $scope, $animate, $interpolate) {
form.$setSubmitted = function() {
$animate.addClass(element, SUBMITTED_CLASS);
form.$submitted = true;
- parentForm.$setSubmitted();
+ form.$$parentForm.$setSubmitted();
};
}
@@ -20182,6 +20346,7 @@ function FormController(element, attrs, $scope, $animate, $interpolate) {
* # CSS classes
* - `ng-valid` is set if the form is valid.
* - `ng-invalid` is set if the form is invalid.
+ * - `ng-pending` is set if the form is pending.
* - `ng-pristine` is set if the form is pristine.
* - `ng-dirty` is set if the form is dirty.
* - `ng-submitted` is set if the form was submitted.
@@ -20257,7 +20422,6 @@ function FormController(element, attrs, $scope, $animate, $interpolate) {
');
\ No newline at end of file
+!window.angular.$$csp().noInlineStyle && window.angular.element(document.head).prepend('');
\ No newline at end of file
diff --git a/awx/ui/client/lib/angular/angular.min.js b/awx/ui/client/lib/angular/angular.min.js
index 2c28ef96f2..272101ec7c 100644
--- a/awx/ui/client/lib/angular/angular.min.js
+++ b/awx/ui/client/lib/angular/angular.min.js
@@ -1,290 +1,294 @@
/*
- AngularJS v1.4.3
+ AngularJS v1.4.7
(c) 2010-2015 Google, Inc. http://angularjs.org
License: MIT
*/
-(function(O,U,t){'use strict';function J(b){return function(){var a=arguments[0],c;c="["+(b?b+":":"")+a+"] http://errors.angularjs.org/1.4.3/"+(b?b+"/":"")+a;for(a=1;a").append(b).html();try{return b[0].nodeType===Na?M(c):c.match(/^(<[^>]+>)/)[1].replace(/^<([\w\-]+)/,function(a,b){return"<"+M(b)})}catch(d){return M(c)}}function yc(b){try{return decodeURIComponent(b)}catch(a){}}function zc(b){var a={},c,d;m((b||"").split("&"),function(b){b&&(c=b.replace(/\+/g,
-"%20").split("="),d=yc(c[0]),w(d)&&(b=w(c[1])?yc(c[1]):!0,Xa.call(a,d)?G(a[d])?a[d].push(b):a[d]=[a[d],b]:a[d]=b))});return a}function Qb(b){var a=[];m(b,function(b,d){G(b)?m(b,function(b){a.push(ma(d,!0)+(!0===b?"":"="+ma(b,!0)))}):a.push(ma(d,!0)+(!0===b?"":"="+ma(b,!0)))});return a.length?a.join("&"):""}function ob(b){return ma(b,!0).replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi,"+")}function ma(b,a){return encodeURIComponent(b).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,
-"$").replace(/%2C/gi,",").replace(/%3B/gi,";").replace(/%20/g,a?"%20":"+")}function Yd(b,a){var c,d,e=Oa.length;for(d=0;d/,">"));}a=a||[];a.unshift(["$provide",function(a){a.value("$rootElement",b)}]);c.debugInfoEnabled&&a.push(["$compileProvider",function(a){a.debugInfoEnabled(!0)}]);a.unshift("ng");d=eb(a,c.strictDi);d.invoke(["$rootScope","$rootElement","$compile","$injector",function(a,b,c,d){a.$apply(function(){b.data("$injector",d);c(b)(a)})}]);return d},e=
-/^NG_ENABLE_DEBUG_INFO!/,f=/^NG_DEFER_BOOTSTRAP!/;O&&e.test(O.name)&&(c.debugInfoEnabled=!0,O.name=O.name.replace(e,""));if(O&&!f.test(O.name))return d();O.name=O.name.replace(f,"");ca.resumeBootstrap=function(b){m(b,function(b){a.push(b)});return d()};z(ca.resumeDeferredBootstrap)&&ca.resumeDeferredBootstrap()}function $d(){O.name="NG_ENABLE_DEBUG_INFO!"+O.name;O.location.reload()}function ae(b){b=ca.element(b).injector();if(!b)throw Fa("test");return b.get("$$testability")}function Bc(b,a){a=a||
-"_";return b.replace(be,function(b,d){return(d?a:"")+b.toLowerCase()})}function ce(){var b;if(!Cc){var a=pb();la=O.jQuery;w(a)&&(la=null===a?t:O[a]);la&&la.fn.on?(y=la,P(la.fn,{scope:Pa.scope,isolateScope:Pa.isolateScope,controller:Pa.controller,injector:Pa.injector,inheritedData:Pa.inheritedData}),b=la.cleanData,la.cleanData=function(a){var d;if(Rb)Rb=!1;else for(var e=0,f;null!=(f=a[e]);e++)(d=la._data(f,"events"))&&d.$destroy&&la(f).triggerHandler("$destroy");b(a)}):y=Q;ca.element=y;Cc=!0}}function Sb(b,
-a,c){if(!b)throw Fa("areq",a||"?",c||"required");return b}function Qa(b,a,c){c&&G(b)&&(b=b[b.length-1]);Sb(z(b),a,"not a function, got "+(b&&"object"===typeof b?b.constructor.name||"Object":typeof b));return b}function Ra(b,a){if("hasOwnProperty"===b)throw Fa("badname",a);}function Dc(b,a,c){if(!a)return b;a=a.split(".");for(var d,e=b,f=a.length,g=0;g$2>")+d[2];for(d=d[0];d--;)c=c.lastChild;f=cb(f,c.childNodes);c=e.firstChild;c.textContent=""}else f.push(a.createTextNode(b));e.textContent="";e.innerHTML="";m(f,function(a){e.appendChild(a)});return e}function Q(b){if(b instanceof Q)return b;var a;L(b)&&(b=R(b),a=!0);if(!(this instanceof Q)){if(a&&"<"!=b.charAt(0))throw Ub("nosel");return new Q(b)}if(a){a=U;
-var c;b=(c=Cf.exec(b))?[a.createElement(c[1])]:(c=Nc(b,a))?c.childNodes:[]}Oc(this,b)}function Vb(b){return b.cloneNode(!0)}function tb(b,a){a||ub(b);if(b.querySelectorAll)for(var c=b.querySelectorAll("*"),d=0,e=c.length;dk&&this.remove(s.key);return b}},get:function(a){if(k").parent()[0])});var f=S(a,b,a,c,d,e);Z.$$addScopeClass(a);
-var g=null;return function(b,c,d){Sb(b,"scope");d=d||{};var e=d.parentBoundTranscludeFn,h=d.transcludeControllers;d=d.futureParentElement;e&&e.$$boundTransclude&&(e=e.$$boundTransclude);g||(g=(d=d&&d[0])?"foreignobject"!==ta(d)&&d.toString().match(/SVG/)?"svg":"html":"html");d="html"!==g?y(Yb(g,y("