From 984630723e9f9dca5525ec6d81f00e464ce5d983 Mon Sep 17 00:00:00 2001 From: Rob Cresswell Date: Tue, 28 Jun 2016 16:34:09 +0100 Subject: Update XStatic-Angular to 1.5.8.0 Change-Id: I94e5ada8bac3d29513e83c9d7e58c7b73ed154a8 --- xstatic/pkg/angular/__init__.py | 4 +- xstatic/pkg/angular/data/angular-animate.js | 341 +- xstatic/pkg/angular/data/angular-aria.js | 115 +- xstatic/pkg/angular/data/angular-cookies.js | 6 +- xstatic/pkg/angular/data/angular-loader.js | 27 +- xstatic/pkg/angular/data/angular-message-format.js | 172 +- xstatic/pkg/angular/data/angular-messages.js | 777 +- xstatic/pkg/angular/data/angular-mocks.js | 846 +- xstatic/pkg/angular/data/angular-parse-ext.js | 1271 +++ xstatic/pkg/angular/data/angular-resource.js | 269 +- xstatic/pkg/angular/data/angular-route.js | 152 +- xstatic/pkg/angular/data/angular-sanitize.js | 813 +- xstatic/pkg/angular/data/angular-scenario.js | 10878 ++++++++++++------- xstatic/pkg/angular/data/angular-touch.js | 150 +- xstatic/pkg/angular/data/angular.js | 4339 ++++++-- xstatic/pkg/angular/data/errors.json | 2 +- xstatic/pkg/angular/data/version.json | 2 +- xstatic/pkg/angular/data/version.txt | 2 +- 18 files changed, 13772 insertions(+), 6394 deletions(-) create mode 100644 xstatic/pkg/angular/data/angular-parse-ext.js diff --git a/xstatic/pkg/angular/__init__.py b/xstatic/pkg/angular/__init__.py index 88b1526..8fb08de 100644 --- a/xstatic/pkg/angular/__init__.py +++ b/xstatic/pkg/angular/__init__.py @@ -11,9 +11,9 @@ NAME = __name__.split('.')[-1] # package name (e.g. 'foo' or 'foo_bar') # please use a all-lowercase valid python # package name -VERSION = '1.4.10' # version of the packaged files, please use the upstream +VERSION = '1.5.8' # version of the packaged files, please use the upstream # version number -BUILD = '1' # our package build number, so we can release new builds +BUILD = '0' # our package build number, so we can release new builds # with fixes for xstatic stuff. PACKAGE_VERSION = VERSION + '.' + BUILD # version used for PyPi diff --git a/xstatic/pkg/angular/data/angular-animate.js b/xstatic/pkg/angular/data/angular-animate.js index 7c0677e..b18b828 100644 --- a/xstatic/pkg/angular/data/angular-animate.js +++ b/xstatic/pkg/angular/data/angular-animate.js @@ -1,23 +1,9 @@ /** - * @license AngularJS v1.4.10 - * (c) 2010-2015 Google, Inc. http://angularjs.org + * @license AngularJS v1.5.8 + * (c) 2010-2016 Google, Inc. http://angularjs.org * License: MIT */ -(function(window, angular, undefined) {'use strict'; - -/* jshint ignore:start */ -var noop = angular.noop; -var copy = angular.copy; -var extend = angular.extend; -var jqLite = angular.element; -var forEach = angular.forEach; -var isArray = angular.isArray; -var isString = angular.isString; -var isObject = angular.isObject; -var isUndefined = angular.isUndefined; -var isDefined = angular.isDefined; -var isFunction = angular.isFunction; -var isElement = angular.isElement; +(function(window, angular) {'use strict'; var ELEMENT_NODE = 1; var COMMENT_NODE = 8; @@ -43,7 +29,7 @@ var CSS_PREFIX = '', TRANSITION_PROP, TRANSITIONEND_EVENT, ANIMATION_PROP, ANIMA // 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)) { +if ((window.ontransitionend === void 0) && (window.onwebkittransitionend !== void 0)) { CSS_PREFIX = '-webkit-'; TRANSITION_PROP = 'WebkitTransition'; TRANSITIONEND_EVENT = 'webkitTransitionEnd transitionend'; @@ -52,7 +38,7 @@ if (isUndefined(window.ontransitionend) && isDefined(window.onwebkittransitionen TRANSITIONEND_EVENT = 'transitionend'; } -if (isUndefined(window.onanimationend) && isDefined(window.onwebkitanimationend)) { +if ((window.onanimationend === void 0) && (window.onwebkitanimationend !== void 0)) { CSS_PREFIX = '-webkit-'; ANIMATION_PROP = 'WebkitAnimation'; ANIMATIONEND_EVENT = 'webkitAnimationEnd animationend'; @@ -74,10 +60,6 @@ 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; -}; - var ngMinErr = angular.$$minErr('ng'); function assertArg(arg, name, reason) { if (!arg) { @@ -132,8 +114,7 @@ function stripCommentsFromElement(element) { if (element instanceof jqLite) { switch (element.length) { case 0: - return []; - break; + return element; case 1: // there is no point of stripping anything if the element @@ -146,7 +127,6 @@ function stripCommentsFromElement(element) { default: return jqLite(extractElementNode(element)); - break; } } @@ -187,7 +167,7 @@ function applyAnimationClassesFactory($$jqLite) { $$removeClass($$jqLite, element, options.removeClass); options.removeClass = null; } - } + }; } function prepareAnimationOptions(options) { @@ -290,10 +270,10 @@ function resolveElementClasses(existing, toAdd, toRemove) { var prop, allow; if (val === ADD_CLASS) { prop = 'addClass'; - allow = !existing[klass]; + allow = !existing[klass] || existing[klass + REMOVE_CLASS_SUFFIX]; } else if (val === REMOVE_CLASS) { prop = 'removeClass'; - allow = existing[klass]; + allow = existing[klass] || existing[klass + ADD_CLASS_SUFFIX]; } if (allow) { if (classes[prop].length) { @@ -323,7 +303,7 @@ function resolveElementClasses(existing, toAdd, toRemove) { } function getDomNode(element) { - return (element instanceof angular.element) ? element[0] : element; + return (element instanceof jqLite) ? element[0] : element; } function applyGeneratedPreparationClasses(element, event, options) { @@ -396,7 +376,7 @@ var $$rAFSchedulerFactory = ['$$rAF', function($$rAF) { queue = scheduler.queue = []; /* waitUntilQuiet does two things: - * 1. It will run the FINAL `fn` value only when an uncancelled RAF has passed through + * 1. It will run the FINAL `fn` value only when an uncanceled RAF has passed through * 2. It will delay the next wave of tasks from running until the quiet `fn` has run. * * The motivation here is that animation code can request more time from the scheduler @@ -513,7 +493,7 @@ var $$AnimateChildrenDirective = ['$interpolate', function($interpolate) { return { link: function(scope, element, attrs) { var val = attrs.ngAnimateChildren; - if (angular.isString(val) && val.length === 0) { //empty attribute + if (isString(val) && val.length === 0) { //empty attribute element.data(NG_ANIMATE_CHILDREN_DATA, true); } else { // Interpolate and set the value, so that it is available to @@ -697,7 +677,7 @@ var ANIMATE_TIMER_KEY = '$$animateCss'; * ``` * * To actually start the animation we need to run `animation.start()` which will then return a promise that we can hook into to detect when the animation ends. - * If we choose not to run the animation then we MUST run `animation.end()` to perform a cleanup on the element (since some CSS classes and stlyes may have been + * If we choose not to run the animation then we MUST run `animation.end()` to perform a cleanup on the element (since some CSS classes and styles may have been * applied to the element during the preparation phase). Note that all other properties such as duration, delay, transitions and keyframes are just properties * and that changing them will not reconfigure the parameters of the animation. * @@ -734,11 +714,11 @@ var ANIMATE_TIMER_KEY = '$$animateCss'; * * `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 occurring 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). + * the animation and do not have a lasting visual effect on the element (e.g. a collapse 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. @@ -791,7 +771,7 @@ function computeCssStyles($window, element, properties) { } // by setting this to null in the event that the delay is not set or is set directly as 0 - // then we can still allow for zegative values to be used later on and not mistake this + // then we can still allow for negative values to be used later on and not mistake this // value for being greater than any other negative value. if (val === 0) { val = null; @@ -907,7 +887,7 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) { } // we keep putting this in multiple times even though the value and the cacheKey are the same - // because we're keeping an interal tally of how many duplicate animations are detected. + // because we're keeping an internal tally of how many duplicate animations are detected. gcsLookup.put(cacheKey, timings); return timings; } @@ -1381,9 +1361,9 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) { } }; - // checking the stagger duration prevents an accidently cascade of the CSS delay style + // checking the stagger duration prevents an accidentally cascade of the CSS delay style // being inherited from the parent. If the transition duration is zero then we can safely - // rely that the delay value is an intential stagger delay style. + // rely that the delay value is an intentional stagger delay style. var maxStagger = itemIndex > 0 && ((timings.transitionDuration && stagger.transitionDuration === 0) || (timings.animationDuration && stagger.animationDuration === 0)) @@ -1556,7 +1536,7 @@ var $$AnimateCssDriverProvider = ['$$animationProvider', function($$animationPro 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 + // we also special case the doc fragment 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 ); @@ -1656,7 +1636,7 @@ var $$AnimateCssDriverProvider = ['$$animationProvider', function($$animationPro var coords = getDomNode(anchor).getBoundingClientRect(); // we iterate directly since safari messes up and doesn't return - // all the keys for the coods object when iterated + // all the keys for the coords object when iterated forEach(['width','height','top','left'], function(key) { var value = coords[key]; switch (key) { @@ -2225,6 +2205,11 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) { }); rules.cancel.push(function(element, newAnimation, currentAnimation) { + // cancel the animation if classes added / removed in both animation cancel each other out, + // but only if the current animation isn't structural + + if (currentAnimation.structural) return false; + var nA = newAnimation.addClass; var nR = newAnimation.removeClass; var cA = currentAnimation.addClass; @@ -2294,7 +2279,7 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) { } ); - var callbackRegistry = {}; + var callbackRegistry = Object.create(null); // remember that the classNameFilter is set during the provider/config // stage therefore we can optimize here and setup a helper function @@ -2312,7 +2297,7 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) { } // IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259. - var contains = Node.prototype.contains || function(arg) { + var contains = window.Node.prototype.contains || function(arg) { // jshint bitwise: false return this === arg || !!(this.compareDocumentPosition(arg) & 16); // jshint bitwise: true @@ -2337,7 +2322,24 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) { return matches; } - return { + function filterFromRegistry(list, matchContainer, matchCallback) { + var containerNode = extractElementNode(matchContainer); + return list.filter(function(entry) { + var isMatch = entry.node === containerNode && + (!matchCallback || entry.callback === matchCallback); + return !isMatch; + }); + } + + function cleanupEventListeners(phase, element) { + if (phase === 'close' && !element[0].parentNode) { + // If the element is not attached to a parentNode, it has been removed by + // the domOperation, and we can safely remove the event callbacks + $animate.off(element); + } + } + + var $animate = { on: function(event, container, callback) { var node = extractElementNode(container); callbackRegistry[event] = callbackRegistry[event] || []; @@ -2345,24 +2347,36 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) { node: node, callback: callback }); + + // Remove the callback when the element is removed from the DOM + jqLite(container).on('$destroy', function() { + var animationDetails = activeAnimationsLookup.get(node); + + if (!animationDetails) { + // If there's an animation ongoing, the callback calling code will remove + // the event listeners. If we'd remove here, the callbacks would be removed + // before the animation ends + $animate.off(event, container, callback); + } + }); }, off: function(event, container, callback) { + if (arguments.length === 1 && !isString(arguments[0])) { + container = arguments[0]; + for (var eventType in callbackRegistry) { + callbackRegistry[eventType] = filterFromRegistry(callbackRegistry[eventType], container); + } + + return; + } + var entries = callbackRegistry[event]; if (!entries) return; callbackRegistry[event] = arguments.length === 1 ? null : filterFromRegistry(entries, container, callback); - - function filterFromRegistry(list, matchContainer, matchCallback) { - var containerNode = extractElementNode(matchContainer); - return list.filter(function(entry) { - var isMatch = entry.node === containerNode && - (!matchCallback || entry.callback === matchCallback); - return !isMatch; - }); - } }, pin: function(element, parentElement) { @@ -2396,11 +2410,10 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) { bool = animationsEnabled = !!element; } else { var node = getDomNode(element); - var recordExists = disabledElementsLookup.get(node); if (argCount === 1) { // (element) - Element getter - bool = !recordExists; + bool = !disabledElementsLookup.get(node); } else { // (element, bool) - Element setter disabledElementsLookup.put(node, !bool); @@ -2412,6 +2425,8 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) { } }; + return $animate; + function queueAnimation(element, event, initialOptions) { // we always make a copy of the options since // there should never be any side effects on @@ -2474,12 +2489,14 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) { var isStructural = ['enter', 'move', 'leave'].indexOf(event) >= 0; + var documentHidden = $document[0].hidden; + // this is a hard disable of all animations for the application or on // the element itself, therefore there is no need to continue further // past this point if not enabled // Animations are also disabled if the document is currently hidden (page is not visible // to the user), because browsers slow down or do not flush calls to requestAnimationFrame - var skipAnimations = !animationsEnabled || $document[0].hidden || disabledElementsLookup.get(node); + var skipAnimations = !animationsEnabled || documentHidden || disabledElementsLookup.get(node); var existingAnimation = (!skipAnimations && activeAnimationsLookup.get(node)) || {}; var hasExistingAnimation = !!existingAnimation.state; @@ -2490,7 +2507,10 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) { } if (skipAnimations) { + // Callbacks should fire even if the document is hidden (regression fix for issue #14120) + if (documentHidden) notifyProgress(runner, event, 'start'); close(); + if (documentHidden) notifyProgress(runner, event, 'close'); return runner; } @@ -2640,6 +2660,11 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) { markElementAnimationState(element, RUNNING_STATE); var realRunner = $$animation(element, event, animationDetails.options); + // this will update the runner's flow-control events based on + // the `realRunner` object. + runner.setHost(realRunner); + notifyProgress(runner, event, 'start', {}); + realRunner.done(function(status) { close(!status); var animationDetails = activeAnimationsLookup.get(node); @@ -2648,11 +2673,6 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) { } notifyProgress(runner, event, 'close', {}); }); - - // this will update the runner's flow-control events based on - // the `realRunner` object. - runner.setHost(realRunner); - notifyProgress(runner, event, 'start', {}); }); return runner; @@ -2669,7 +2689,10 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) { forEach(callbacks, function(callback) { callback(element, phase, data); }); + cleanupEventListeners(phase, element); }); + } else { + cleanupEventListeners(phase, element); } }); runner.progress(event, phase, data); @@ -3126,7 +3149,7 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) { }; // the anchor animations require that the from and to elements both have at least - // one shared CSS class which effictively marries the two elements together to use + // one shared CSS class which effectively marries the two elements together to use // the same animation driver and to properly sequence the anchor animation. if (group.classes.length) { preparedAnimations.push(group); @@ -3169,8 +3192,6 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) { // may attempt more elements, but custom drivers are more particular for (var i = drivers.length - 1; i >= 0; i--) { var driverName = drivers[i]; - if (!$injector.has(driverName)) continue; // TODO(matsko): remove this check - var factory = $injector.get(driverName); var driver = factory(animationDetails); if (driver) { @@ -3199,7 +3220,8 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) { } function update(element) { - getRunner(element).setHost(newRunner); + var runner = getRunner(element); + if (runner) runner.setHost(newRunner); } } @@ -3229,18 +3251,120 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) { }]; }]; -/* global angularAnimateModule: true, - - $$AnimateAsyncRunFactory, - $$rAFSchedulerFactory, - $$AnimateChildrenDirective, - $$AnimateQueueProvider, - $$AnimationProvider, - $AnimateCssProvider, - $$AnimateCssDriverProvider, - $$AnimateJsProvider, - $$AnimateJsDriverProvider, -*/ +/** + * @ngdoc directive + * @name ngAnimateSwap + * @restrict A + * @scope + * + * @description + * + * ngAnimateSwap is a animation-oriented directive that allows for the container to + * be removed and entered in whenever the associated expression changes. A + * common usecase for this directive is a rotating banner or slider component which + * contains one image being present at a time. When the active image changes + * then the old image will perform a `leave` animation and the new element + * will be inserted via an `enter` animation. + * + * @animations + * | Animation | Occurs | + * |----------------------------------|--------------------------------------| + * | {@link ng.$animate#enter enter} | when the new element is inserted to the DOM | + * | {@link ng.$animate#leave leave} | when the old element is removed from the DOM | + * + * @example + * + * + *
+ *
+ * {{ number }} + *
+ *
+ *
+ * + * angular.module('ngAnimateSwapExample', ['ngAnimate']) + * .controller('AppCtrl', ['$scope', '$interval', function($scope, $interval) { + * $scope.number = 0; + * $interval(function() { + * $scope.number++; + * }, 1000); + * + * var colors = ['red','blue','green','yellow','orange']; + * $scope.colorClass = function(number) { + * return colors[number % colors.length]; + * }; + * }]); + * + * + * .container { + * height:250px; + * width:250px; + * position:relative; + * overflow:hidden; + * border:2px solid black; + * } + * .container .cell { + * font-size:150px; + * text-align:center; + * line-height:250px; + * position:absolute; + * top:0; + * left:0; + * right:0; + * border-bottom:2px solid black; + * } + * .swap-animation.ng-enter, .swap-animation.ng-leave { + * transition:0.5s linear all; + * } + * .swap-animation.ng-enter { + * top:-250px; + * } + * .swap-animation.ng-enter-active { + * top:0px; + * } + * .swap-animation.ng-leave { + * top:0px; + * } + * .swap-animation.ng-leave-active { + * top:250px; + * } + * .red { background:red; } + * .green { background:green; } + * .blue { background:blue; } + * .yellow { background:yellow; } + * .orange { background:orange; } + * + *
+ */ +var ngAnimateSwapDirective = ['$animate', '$rootScope', function($animate, $rootScope) { + return { + restrict: 'A', + transclude: 'element', + terminal: true, + priority: 600, // we use 600 here to ensure that the directive is caught before others + link: function(scope, $element, attrs, ctrl, $transclude) { + var previousElement, previousScope; + scope.$watchCollection(attrs.ngAnimateSwap || attrs['for'], function(value) { + if (previousElement) { + $animate.leave(previousElement); + } + if (previousScope) { + previousScope.$destroy(); + previousScope = null; + } + if (value || value === 0) { + previousScope = scope.$new(); + $transclude(previousScope, function(element) { + previousElement = element; + $animate.enter(element, null, $element); + }); + } + }); + } + }; +}]; /** * @ngdoc module @@ -3358,7 +3482,7 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) { *
* Show and hide me *
- * + * * * + *
+ *
+ *
+ * + * {{title}} + *

{{text}}

+ *
+ *
+ * + * + * angular.module('multiSlotTranscludeExample', []) + * .directive('pane', function(){ + * return { + * restrict: 'E', + * transclude: { + * 'title': '?paneTitle', + * 'body': 'paneBody', + * 'footer': '?paneFooter' + * }, + * template: '
' + + * '
Fallback Title
' + + * '
' + + * '' + + * '
' + * }; + * }) + * .controller('ExampleController', ['$scope', function($scope) { + * $scope.title = 'Lorem Ipsum'; + * $scope.link = "https://google.com"; + * $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...'; + * }]); + *
+ * + * it('should have transcluded the title and the body', function() { + * var titleElement = element(by.model('title')); + * titleElement.clear(); + * titleElement.sendKeys('TITLE'); + * var textElement = element(by.model('text')); + * textElement.clear(); + * textElement.sendKeys('TEXT'); + * expect(element(by.css('.title')).getText()).toEqual('TITLE'); + * expect(element(by.binding('text')).getText()).toEqual('TEXT'); + * expect(element(by.css('.footer')).getText()).toEqual('Fallback Footer'); + * }); + * + * */ -var ngTranscludeDirective = ngDirective({ - restrict: 'EAC', - link: function($scope, $element, $attrs, controller, $transclude) { - if (!$transclude) { - throw minErr('ngTransclude')('orphan', - 'Illegal use of ngTransclude directive in the template! ' + - 'No parent directive that requires a transclusion found. ' + - 'Element: {0}', - startingTag($element)); - } +var ngTranscludeMinErr = minErr('ngTransclude'); +var ngTranscludeDirective = ['$compile', function($compile) { + return { + restrict: 'EAC', + terminal: true, + compile: function ngTranscludeCompile(tElement) { + + // Remove and cache any original content to act as a fallback + var fallbackLinkFn = $compile(tElement.contents()); + tElement.empty(); + + return function ngTranscludePostLink($scope, $element, $attrs, controller, $transclude) { + + if (!$transclude) { + throw ngTranscludeMinErr('orphan', + 'Illegal use of ngTransclude directive in the template! ' + + 'No parent directive that requires a transclusion found. ' + + 'Element: {0}', + startingTag($element)); + } - $transclude(function(clone) { - $element.empty(); - $element.append(clone); - }); - } -}); + + // If the attribute is of the form: `ng-transclude="ng-transclude"` then treat it like the default + if ($attrs.ngTransclude === $attrs.$attr.ngTransclude) { + $attrs.ngTransclude = ''; + } + var slotName = $attrs.ngTransclude || $attrs.ngTranscludeSlot; + + // If the slot is required and no transclusion content is provided then this call will throw an error + $transclude(ngTranscludeCloneAttachFn, null, slotName); + + // If the slot is optional and no transclusion content is provided then use the fallback content + if (slotName && !$transclude.isSlotFilled(slotName)) { + useFallbackContent(); + } + + function ngTranscludeCloneAttachFn(clone, transcludedScope) { + if (clone.length) { + $element.append(clone); + } else { + useFallbackContent(); + // There is nothing linked against the transcluded scope since no content was available, + // so it should be safe to clean up the generated scope. + transcludedScope.$destroy(); + } + } + + function useFallbackContent() { + // Since this is the fallback content rather than the transcluded content, + // we link against the scope of this directive rather than the transcluded scope + fallbackLinkFn($scope, function(clone) { + $element.append(clone); + }); + } + }; + } + }; +}]; /** * @ngdoc directive @@ -38025,7 +40854,7 @@ function chromeHack(optionElement) { * added `