From 67f1bab200637ce78f5baefb9e42ef6ef59a542f Mon Sep 17 00:00:00 2001 From: Bernhard Posselt Date: Thu, 28 May 2015 17:25:33 +0200 Subject: downgrade angular because of regressions --- js/vendor/angular/.bower.json | 10 +- js/vendor/angular/angular-csp.css | 8 - js/vendor/angular/angular.js | 7682 +++++++++++++-------------------- js/vendor/angular/angular.min.js | 532 ++- js/vendor/angular/angular.min.js.gzip | Bin 51446 -> 50407 bytes js/vendor/angular/angular.min.js.map | 6 +- js/vendor/angular/bower.json | 2 +- js/vendor/angular/package.json | 2 +- 8 files changed, 3186 insertions(+), 5056 deletions(-) (limited to 'js/vendor/angular') diff --git a/js/vendor/angular/.bower.json b/js/vendor/angular/.bower.json index a433da9a5..a382c17ad 100644 --- a/js/vendor/angular/.bower.json +++ b/js/vendor/angular/.bower.json @@ -1,17 +1,17 @@ { "name": "angular", - "version": "1.4.0", + "version": "1.3.15", "main": "./angular.js", "ignore": [], "dependencies": {}, "homepage": "https://github.com/angular/bower-angular", - "_release": "1.4.0", + "_release": "1.3.15", "_resolution": { "type": "version", - "tag": "v1.4.0", - "commit": "e2f2cf7dc4a3ef1859ab28e657eca0e9edb588ba" + "tag": "v1.3.15", + "commit": "ba7abcfa409ba852146e6ba206693cf7bac3e359" }, "_source": "git://github.com/angular/bower-angular.git", - "_target": "~1.4.*", + "_target": "1.3.15", "_originalSource": "angular" } \ No newline at end of file diff --git a/js/vendor/angular/angular-csp.css b/js/vendor/angular/angular-csp.css index f3cd926cb..0ce9d864c 100644 --- a/js/vendor/angular/angular-csp.css +++ b/js/vendor/angular/angular-csp.css @@ -11,11 +11,3 @@ ng\:form { display: block; } - -.ng-animate-shim { - visibility:hidden; -} - -.ng-anchor { - position:absolute; -} diff --git a/js/vendor/angular/angular.js b/js/vendor/angular/angular.js index af94745a9..e488352b2 100644 --- a/js/vendor/angular/angular.js +++ b/js/vendor/angular/angular.js @@ -1,6 +1,6 @@ /** - * @license AngularJS v1.4.0 - * (c) 2010-2015 Google, Inc. http://angularjs.org + * @license AngularJS v1.3.15 + * (c) 2010-2014 Google, Inc. http://angularjs.org * License: MIT */ (function(window, document, undefined) {'use strict'; @@ -38,33 +38,28 @@ function minErr(module, ErrorConstructor) { ErrorConstructor = ErrorConstructor || Error; return function() { - var SKIP_INDEXES = 2; + var code = arguments[0], + prefix = '[' + (module ? module + ':' : '') + code + '] ', + template = arguments[1], + templateArgs = arguments, - var templateArgs = arguments, - code = templateArgs[0], - message = '[' + (module ? module + ':' : '') + code + '] ', - template = templateArgs[1], - paramPrefix, i; + message, i; - message += template.replace(/\{\d+\}/g, function(match) { - var index = +match.slice(1, -1), - shiftedIndex = index + SKIP_INDEXES; + message = prefix + template.replace(/\{\d+\}/g, function(match) { + var index = +match.slice(1, -1), arg; - if (shiftedIndex < templateArgs.length) { - return toDebugString(templateArgs[shiftedIndex]); + if (index + 2 < templateArgs.length) { + return toDebugString(templateArgs[index + 2]); } - return match; }); - message += '\nhttp://errors.angularjs.org/1.4.0/' + + message = message + '\nhttp://errors.angularjs.org/1.3.15/' + (module ? module + '/' : '') + code; - - for (i = SKIP_INDEXES, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') { - message += paramPrefix + 'p' + (i - SKIP_INDEXES) + '=' + - encodeURIComponent(toDebugString(templateArgs[i])); + for (i = 2; i < arguments.length; i++) { + message = message + (i == 2 ? '?' : '&') + 'p' + (i - 2) + '=' + + encodeURIComponent(toDebugString(arguments[i])); } - return new ErrorConstructor(message); }; } @@ -91,21 +86,20 @@ function minErr(module, ErrorConstructor) { nodeName_: true, isArrayLike: true, forEach: true, + sortedKeys: true, forEachSorted: true, reverseParams: true, nextUid: true, setHashKey: true, extend: true, - toInt: true, + int: true, inherit: true, - merge: true, noop: true, identity: true, valueFn: true, isUndefined: true, isDefined: true, isObject: true, - isBlankObject: true, isString: true, isNumber: true, isDate: true, @@ -129,15 +123,12 @@ function minErr(module, ErrorConstructor) { shallowCopy: true, equals: true, csp: true, - jq: true, concat: true, sliceArgs: true, bind: true, toJsonReplacer: true, toJson: true, fromJson: true, - convertTimezoneToLocal: true, - timezoneToOffset: true, startingTag: true, tryDecodeURIComponent: true, parseKeyValue: true, @@ -158,7 +149,6 @@ function minErr(module, ErrorConstructor) { createMap: true, NODE_TYPE_ELEMENT: true, - NODE_TYPE_ATTRIBUTE: true, NODE_TYPE_TEXT: true, NODE_TYPE_COMMENT: true, NODE_TYPE_DOCUMENT: true, @@ -245,7 +235,6 @@ var splice = [].splice, push = [].push, toString = Object.prototype.toString, - getPrototypeOf = Object.getPrototypeOf, ngMinErr = minErr('ng'), /** @name angular */ @@ -271,9 +260,7 @@ function isArrayLike(obj) { return false; } - // Support: iOS 8.2 (not reproducible in simulator) - // "length" in obj used to prevent JIT error (gh-11508) - var length = "length" in Object(obj) && obj.length; + var length = obj.length; if (obj.nodeType === NODE_TYPE_ELEMENT && length) { return true; @@ -338,22 +325,9 @@ function forEach(obj, iterator, context) { } } else if (obj.forEach && obj.forEach !== forEach) { obj.forEach(iterator, context, obj); - } else if (isBlankObject(obj)) { - // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty - for (key in obj) { - iterator.call(context, obj[key], key, obj); - } - } else if (typeof obj.hasOwnProperty === 'function') { - // Slow path for objects inheriting Object.prototype, hasOwnProperty check needed - for (key in obj) { - if (obj.hasOwnProperty(key)) { - iterator.call(context, obj[key], key, obj); - } - } } else { - // Slow path for objects which do not have a method `hasOwnProperty` for (key in obj) { - if (hasOwnProperty.call(obj, key)) { + if (obj.hasOwnProperty(key)) { iterator.call(context, obj[key], key, obj); } } @@ -362,8 +336,12 @@ function forEach(obj, iterator, context) { return obj; } +function sortedKeys(obj) { + return Object.keys(obj).sort(); +} + function forEachSorted(obj, iterator, context) { - var keys = Object.keys(obj).sort(); + var keys = sortedKeys(obj); for (var i = 0; i < keys.length; i++) { iterator.call(context, obj[keys[i]], keys[i]); } @@ -408,31 +386,6 @@ function setHashKey(obj, h) { } } - -function baseExtend(dst, objs, deep) { - var h = dst.$$hashKey; - - for (var i = 0, ii = objs.length; i < ii; ++i) { - var obj = objs[i]; - if (!isObject(obj) && !isFunction(obj)) continue; - var keys = Object.keys(obj); - for (var j = 0, jj = keys.length; j < jj; j++) { - var key = keys[j]; - var src = obj[key]; - - if (deep && isObject(src)) { - if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {}; - baseExtend(dst[key], [src], true); - } else { - dst[key] = src; - } - } - } - - setHashKey(dst, h); - return dst; -} - /** * @ngdoc function * @name angular.extend @@ -443,44 +396,31 @@ function baseExtend(dst, objs, deep) { * Extends the destination object `dst` by copying own enumerable properties from the `src` object(s) * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so * by passing an empty object as the target: `var object = angular.extend({}, object1, object2)`. - * - * **Note:** Keep in mind that `angular.extend` does not support recursive merge (deep copy). Use - * {@link angular.merge} for this. + * Note: Keep in mind that `angular.extend` does not support recursive merge (deep copy). * * @param {Object} dst Destination object. * @param {...Object} src Source object(s). * @returns {Object} Reference to `dst`. */ function extend(dst) { - return baseExtend(dst, slice.call(arguments, 1), false); -} + var h = dst.$$hashKey; + for (var i = 1, ii = arguments.length; i < ii; i++) { + var obj = arguments[i]; + if (obj) { + var keys = Object.keys(obj); + for (var j = 0, jj = keys.length; j < jj; j++) { + var key = keys[j]; + dst[key] = obj[key]; + } + } + } -/** -* @ngdoc function -* @name angular.merge -* @module ng -* @kind function -* -* @description -* Deeply extends the destination object `dst` by copying own enumerable properties from the `src` object(s) -* to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so -* by passing an empty object as the target: `var object = angular.merge({}, object1, object2)`. -* -* Unlike {@link angular.extend extend()}, `merge()` recursively descends into object properties of source -* objects, performing a deep copy. -* -* @param {Object} dst Destination object. -* @param {...Object} src Source object(s). -* @returns {Object} Reference to `dst`. -*/ -function merge(dst) { - return baseExtend(dst, slice.call(arguments, 1), true); + setHashKey(dst, h); + return dst; } - - -function toInt(str) { +function int(str) { return parseInt(str, 10); } @@ -582,16 +522,6 @@ function isObject(value) { } -/** - * Determine if a value is an object with a null prototype - * - * @returns {boolean} True if `value` is an `Object` with a null prototype - */ -function isBlankObject(value) { - return value !== null && typeof value === 'object' && !getPrototypeOf(value); -} - - /** * @ngdoc function * @name angular.isString @@ -728,12 +658,6 @@ function isPromiseLike(obj) { } -var TYPED_ARRAY_REGEXP = /^\[object (Uint8(Clamped)?)|(Uint16)|(Uint32)|(Int8)|(Int16)|(Int32)|(Float(32)|(64))Array\]$/; -function isTypedArray(value) { - return TYPED_ARRAY_REGEXP.test(toString.call(value)); -} - - var trim = function(value) { return isString(value) ? value.trim() : value; }; @@ -771,9 +695,8 @@ function isElement(node) { */ function makeMap(str) { var obj = {}, items = str.split(","), i; - for (i = 0; i < items.length; i++) { + for (i = 0; i < items.length; i++) obj[items[i]] = true; - } return obj; } @@ -788,10 +711,9 @@ function includes(array, obj) { function arrayRemove(array, value) { var index = array.indexOf(value); - if (index >= 0) { + if (index >= 0) array.splice(index, 1); - } - return index; + return value; } /** @@ -857,25 +779,19 @@ function copy(source, destination, stackSource, stackDest) { throw ngMinErr('cpws', "Can't copy! Making copies of Window or Scope instances is not supported."); } - if (isTypedArray(destination)) { - throw ngMinErr('cpta', - "Can't copy! TypedArray destination cannot be mutated."); - } if (!destination) { destination = source; if (source) { if (isArray(source)) { destination = copy(source, [], stackSource, stackDest); - } else if (isTypedArray(source)) { - destination = new source.constructor(source); } else if (isDate(source)) { destination = new Date(source.getTime()); } else if (isRegExp(source)) { destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]); destination.lastIndex = source.lastIndex; } else if (isObject(source)) { - var emptyObject = Object.create(getPrototypeOf(source)); + var emptyObject = Object.create(Object.getPrototypeOf(source)); destination = copy(source, emptyObject, stackSource, stackDest); } } @@ -894,7 +810,7 @@ function copy(source, destination, stackSource, stackDest) { stackDest.push(destination); } - var result, key; + var result; if (isArray(source)) { destination.length = 0; for (var i = 0; i < source.length; i++) { @@ -914,40 +830,21 @@ function copy(source, destination, stackSource, stackDest) { delete destination[key]; }); } - if (isBlankObject(source)) { - // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty - for (key in source) { - putValue(key, source[key], destination, stackSource, stackDest); - } - } else if (source && typeof source.hasOwnProperty === 'function') { - // Slow path, which must rely on hasOwnProperty - for (key in source) { - if (source.hasOwnProperty(key)) { - putValue(key, source[key], destination, stackSource, stackDest); - } - } - } else { - // Slowest path --- hasOwnProperty can't be called as a method - for (key in source) { - if (hasOwnProperty.call(source, key)) { - putValue(key, source[key], destination, stackSource, stackDest); + for (var key in source) { + if (source.hasOwnProperty(key)) { + result = copy(source[key], null, stackSource, stackDest); + if (isObject(source[key])) { + stackSource.push(source[key]); + stackDest.push(result); } + destination[key] = result; } } setHashKey(destination,h); } - } - return destination; - function putValue(key, val, destination, stackSource, stackDest) { - // No context allocation, trivial outer scope, easily inlined - var result = copy(val, null, stackSource, stackDest); - if (isObject(val)) { - stackSource.push(val); - stackDest.push(result); - } - destination[key] = result; } + return destination; } /** @@ -1028,14 +925,14 @@ function equals(o1, o2) { } else { if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) || isArray(o2) || isDate(o2) || isRegExp(o2)) return false; - keySet = createMap(); + keySet = {}; for (key in o1) { if (key.charAt(0) === '$' || isFunction(o1[key])) continue; if (!equals(o1[key], o2[key])) return false; keySet[key] = true; } for (key in o2) { - if (!(key in keySet) && + if (!keySet.hasOwnProperty(key) && key.charAt(0) !== '$' && o2[key] !== undefined && !isFunction(o2[key])) return false; @@ -1066,58 +963,7 @@ var csp = function() { return (csp.isActive_ = active); }; -/** - * @ngdoc directive - * @module ng - * @name ngJq - * - * @element ANY - * @param {string=} ngJq the name of the library available under `window` - * to be used for angular.element - * @description - * Use this directive to force the angular.element library. This should be - * used to force either jqLite by leaving ng-jq blank or setting the name of - * the jquery variable under window (eg. jQuery). - * - * Since angular looks for this directive when it is loaded (doesn't wait for the - * DOMContentLoaded event), it must be placed on an element that comes before the script - * which loads angular. Also, only the first instance of `ng-jq` will be used and all - * others ignored. - * - * @example - * This example shows how to force jqLite using the `ngJq` directive to the `html` tag. - ```html - - - ... - ... - - ``` - * @example - * This example shows how to use a jQuery based library of a different name. - * The library name must be available at the top most 'window'. - ```html - - - ... - ... - - ``` - */ -var jq = function() { - if (isDefined(jq.name_)) return jq.name_; - var el; - var i, ii = ngAttrPrefixes.length, prefix, name; - for (i = 0; i < ii; ++i) { - prefix = ngAttrPrefixes[i]; - if (el = document.querySelector('[' + prefix.replace(':', '\\:') + 'jq]')) { - name = el.getAttribute(prefix + 'jq'); - break; - } - } - return (jq.name_ = name); -}; function concat(array1, array2, index) { return array1.concat(slice.call(array2, index)); @@ -1196,8 +1042,8 @@ function toJsonReplacer(key, value) { * stripped since angular uses this notation internally. * * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON. - * @param {boolean|number} [pretty=2] If set to true, the JSON output will contain newlines and whitespace. - * If set to an integer, the JSON output will contain that many spaces per indentation. + * @param {boolean|number=} pretty If set to true, the JSON output will contain newlines and whitespace. + * If set to an integer, the JSON output will contain that many spaces per indentation (the default is 2). * @returns {string|undefined} JSON-ified string representing `obj`. */ function toJson(obj, pretty) { @@ -1228,26 +1074,6 @@ function fromJson(json) { } -function timezoneToOffset(timezone, fallback) { - var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000; - return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset; -} - - -function addDateMinutes(date, minutes) { - date = new Date(date.getTime()); - date.setMinutes(date.getMinutes() + minutes); - return date; -} - - -function convertTimezoneToLocal(date, timezone, reverse) { - reverse = reverse ? -1 : 1; - var timezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset()); - return addDateMinutes(date, reverse * (timezoneOffset - date.getTimezoneOffset())); -} - - /** * @returns {string} Returns the string representation of the element. */ @@ -1376,9 +1202,10 @@ var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-']; function getNgAttribute(element, ngAttr) { var attr, i, ii = ngAttrPrefixes.length; + element = jqLite(element); for (i = 0; i < ii; ++i) { attr = ngAttrPrefixes[i] + ngAttr; - if (isString(attr = element.getAttribute(attr))) { + if (isString(attr = element.attr(attr))) { return attr; } } @@ -1709,12 +1536,7 @@ 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 = window.jQuery; // Use jQuery if it exists with proper functionality, otherwise default to us. // Angular 1.2+ requires jQuery 1.7+ for on()/off() support. // Angular 1.3+ technically requires at least jQuery 2.1+ but it may work with older @@ -1853,7 +1675,6 @@ function createMap() { } var NODE_TYPE_ELEMENT = 1; -var NODE_TYPE_ATTRIBUTE = 2; var NODE_TYPE_TEXT = 3; var NODE_TYPE_COMMENT = 8; var NODE_TYPE_DOCUMENT = 9; @@ -2052,18 +1873,6 @@ function setupModuleLoader(window) { */ constant: invokeLater('$provide', 'constant', 'unshift'), - /** - * @ngdoc method - * @name angular.Module#decorator - * @module ng - * @param {string} The name of the service to decorate. - * @param {Function} This function will be invoked when the service needs to be - * instantiated and should return the decorated service instance. - * @description - * See {@link auto.$provide#decorator $provide.decorator()}. - */ - decorator: invokeLater('$provide', 'decorator'), - /** * @ngdoc method * @name angular.Module#animation @@ -2077,7 +1886,7 @@ function setupModuleLoader(window) { * * * Defines an animation hook that can be later used with - * {@link $animate $animate} service and directives that use this service. + * {@link ngAnimate.$animate $animate} service and directives that use this service. * * ```js * module.animation('.animation-name', function($inject1, $inject2) { @@ -2102,17 +1911,10 @@ function setupModuleLoader(window) { * @ngdoc method * @name angular.Module#filter * @module ng - * @param {string} name Filter name - this must be a valid angular expression identifier + * @param {string} name Filter name. * @param {Function} filterFactory Factory function for creating new instance of filter. * @description * See {@link ng.$filterProvider#register $filterProvider.register()}. - * - *
- * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`. - * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace - * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores - * (`myapp_subsection_filterx`). - *
*/ filter: invokeLater('$filterProvider', 'register'), @@ -2280,8 +2082,6 @@ function toDebugString(obj) { $AnchorScrollProvider, $AnimateProvider, - $$CoreAnimateQueueProvider, - $$CoreAnimateRunnerProvider, $BrowserProvider, $CacheFactoryProvider, $ControllerProvider, @@ -2290,10 +2090,7 @@ function toDebugString(obj) { $FilterProvider, $InterpolateProvider, $IntervalProvider, - $$HashMapProvider, $HttpProvider, - $HttpParamSerializerProvider, - $HttpParamSerializerJQLikeProvider, $HttpBackendProvider, $LocationProvider, $LogProvider, @@ -2312,8 +2109,7 @@ function toDebugString(obj) { $$RAFProvider, $$AsyncCallbackProvider, $WindowProvider, - $$jqLiteProvider, - $$CookieReaderProvider + $$jqLiteProvider */ @@ -2332,11 +2128,11 @@ function toDebugString(obj) { * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat". */ var version = { - full: '1.4.0', // all of these placeholder strings will be replaced by grunt's + full: '1.3.15', // all of these placeholder strings will be replaced by grunt's major: 1, // package task - minor: 4, - dot: 0, - codeName: 'jaracimrman-existence' + minor: 3, + dot: 15, + codeName: 'locality-filtration' }; @@ -2345,7 +2141,6 @@ function publishExternalAPI(angular) { 'bootstrap': bootstrap, 'copy': copy, 'extend': extend, - 'merge': merge, 'equals': equals, 'element': jqLite, 'forEach': forEach, @@ -2442,8 +2237,6 @@ function publishExternalAPI(angular) { $provide.provider({ $anchorScroll: $AnchorScrollProvider, $animate: $AnimateProvider, - $$animateQueue: $$CoreAnimateQueueProvider, - $$AnimateRunner: $$CoreAnimateRunnerProvider, $browser: $BrowserProvider, $cacheFactory: $CacheFactoryProvider, $controller: $ControllerProvider, @@ -2453,8 +2246,6 @@ function publishExternalAPI(angular) { $interpolate: $InterpolateProvider, $interval: $IntervalProvider, $http: $HttpProvider, - $httpParamSerializer: $HttpParamSerializerProvider, - $httpParamSerializerJQLike: $HttpParamSerializerJQLikeProvider, $httpBackend: $HttpBackendProvider, $location: $LocationProvider, $log: $LogProvider, @@ -2472,9 +2263,7 @@ function publishExternalAPI(angular) { $window: $WindowProvider, $$rAF: $$RAFProvider, $$asyncCallback: $$AsyncCallbackProvider, - $$jqLite: $$jqLiteProvider, - $$HashMap: $$HashMapProvider, - $$cookieReader: $$CookieReaderProvider + $$jqLite: $$jqLiteProvider }); } ]); @@ -2519,7 +2308,7 @@ function publishExternalAPI(angular) { * Angular to manipulate the DOM in a cross-browser compatible way. **jqLite** implements only the most * commonly needed functionality with the goal of having a very small footprint. * - * To use `jQuery`, simply ensure it is loaded before the `angular.js` file. + * To use jQuery, simply load it before `DOMContentLoaded` event fired. * *
**Note:** all element references in Angular are always wrapped with jQuery or * jqLite; they are never raw DOM references.
@@ -2535,7 +2324,7 @@ function publishExternalAPI(angular) { * - [`children()`](http://api.jquery.com/children/) - Does not support selectors * - [`clone()`](http://api.jquery.com/clone/) * - [`contents()`](http://api.jquery.com/contents/) - * - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyle()`. As a setter, does not convert numbers to strings or append 'px'. + * - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyle()` * - [`data()`](http://api.jquery.com/data/) * - [`detach()`](http://api.jquery.com/detach/) * - [`empty()`](http://api.jquery.com/empty/) @@ -3078,10 +2867,6 @@ forEach({ }, attr: function(element, name, value) { - var nodeType = element.nodeType; - if (nodeType === NODE_TYPE_TEXT || nodeType === NODE_TYPE_ATTRIBUTE || nodeType === NODE_TYPE_COMMENT) { - return; - } var lowercasedName = lowercase(name); if (BOOLEAN_ATTR[lowercasedName]) { if (isDefined(value)) { @@ -3346,9 +3131,8 @@ forEach({ children: function(element) { var children = []; forEach(element.childNodes, function(element) { - if (element.nodeType === NODE_TYPE_ELEMENT) { + if (element.nodeType === NODE_TYPE_ELEMENT) children.push(element); - } }); return children; }, @@ -3594,12 +3378,6 @@ HashMap.prototype = { } }; -var $$HashMapProvider = [function() { - this.$get = [function() { - return HashMap; - }]; -}]; - /** * @ngdoc function * @module ng @@ -3779,7 +3557,7 @@ function annotate(fn, strictDi, name) { * Return an instance of the service. * * @param {string} name The name of the instance to retrieve. - * @param {string=} caller An optional string to provide the origin of the function call for error messages. + * @param {string} caller An optional string to provide the origin of the function call for error messages. * @return {*} The instance. */ @@ -3790,8 +3568,8 @@ function annotate(fn, strictDi, name) { * @description * Invoke the method and supply the method arguments from the `$injector`. * - * @param {Function|Array.} fn The injectable function to invoke. Function parameters are - * injected according to the {@link guide/di $inject Annotation} rules. + * @param {!Function} fn The function to invoke. Function parameters are injected according to the + * {@link guide/di $inject Annotation} rules. * @param {Object=} self The `this` for the invoked method. * @param {Object=} locals Optional object. If preset then any argument names are read from this * object first, before the `$injector` is consulted. @@ -4058,8 +3836,8 @@ function annotate(fn, strictDi, name) { * configure your service in a provider. * * @param {string} name The name of the instance. - * @param {Function|Array.} $getFn The injectable $getFn for the instance creation. - * Internally this is a short hand for `$provide.provider(name, {$get: $getFn})`. + * @param {function()} $getFn The $getFn for the instance creation. Internally this is a short hand + * for `$provide.provider(name, {$get: $getFn})`. * @returns {Object} registered provider instance * * @example @@ -4094,8 +3872,7 @@ function annotate(fn, strictDi, name) { * as a type/class. * * @param {string} name The name of the instance. - * @param {Function|Array.} constructor An injectable class (constructor function) - * that will be instantiated. + * @param {Function} constructor A class (constructor function) that will be instantiated. * @returns {Object} registered provider instance * * @example @@ -4194,7 +3971,7 @@ function annotate(fn, strictDi, name) { * object which replaces or wraps and delegates to the original service. * * @param {string} name The name of the service to decorate. - * @param {Function|Array.} decorator This function will be invoked when the service needs to be + * @param {function()} decorator This function will be invoked when the service needs to be * instantiated and should return the decorated service instance. The function is called using * the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable. * Local injection arguments: @@ -4488,10 +4265,9 @@ function $AnchorScrollProvider() { * @requires $rootScope * * @description - * When called, it scrolls to the element related to the specified `hash` or (if omitted) to the - * current value of {@link ng.$location#hash $location.hash()}, according to the rules specified - * in the - * [HTML5 spec](http://dev.w3.org/html5/spec/Overview.html#the-indicated-part-of-the-document). + * When called, it checks the current value of {@link ng.$location#hash $location.hash()} and + * scrolls to the related element, according to the rules specified in the + * [Html5 spec](http://dev.w3.org/html5/spec/Overview.html#the-indicated-part-of-the-document). * * It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to * match any anchor whenever it changes. This can be disabled by calling @@ -4500,9 +4276,6 @@ function $AnchorScrollProvider() { * Additionally, you can use its {@link ng.$anchorScroll#yOffset yOffset} property to specify a * vertical scroll-offset (either fixed or dynamic). * - * @param {string=} hash The hash specifying the element to scroll to. If omitted, the value of - * {@link ng.$location#hash $location.hash()} will be used. - * * @property {(number|function|jqLite)} yOffset * If set, specifies a vertical scroll-offset. This is often useful when there are fixed * positioned elements at the top of the page, such as navbars, headers etc. @@ -4686,9 +4459,8 @@ function $AnchorScrollProvider() { } } - function scroll(hash) { - hash = isString(hash) ? hash : $location.hash(); - var elm; + function scroll() { + var hash = $location.hash(), elm; // empty hash, scroll to the top of the page if (!hash) scrollTo(null); @@ -4722,168 +4494,6 @@ function $AnchorScrollProvider() { } var $animateMinErr = minErr('$animate'); -var ELEMENT_NODE = 1; -var NG_ANIMATE_CLASSNAME = 'ng-animate'; - -function mergeClasses(a,b) { - if (!a && !b) return ''; - if (!a) return b; - if (!b) return a; - if (isArray(a)) a = a.join(' '); - if (isArray(b)) b = b.join(' '); - return a + ' ' + b; -} - -function extractElementNode(element) { - for (var i = 0; i < element.length; i++) { - var elm = element[i]; - if (elm.nodeType === ELEMENT_NODE) { - return elm; - } - } -} - -function splitClasses(classes) { - if (isString(classes)) { - classes = classes.split(' '); - } - - // Use createMap() to prevent class assumptions involving property names in - // Object.prototype - var obj = createMap(); - forEach(classes, function(klass) { - // sometimes the split leaves empty string values - // incase extra spaces were applied to the options - if (klass.length) { - obj[klass] = true; - } - }); - return obj; -} - -// if any other type of options value besides an Object value is -// passed into the $animate.method() animation then this helper code -// will be run which will ignore it. While this patch is not the -// greatest solution to this, a lot of existing plugins depend on -// $animate to either call the callback (< 1.2) or return a promise -// that can be changed. This helper function ensures that the options -// are wiped clean incase a callback function is provided. -function prepareAnimateOptions(options) { - return isObject(options) - ? options - : {}; -} - -var $$CoreAnimateRunnerProvider = function() { - this.$get = ['$q', '$$rAF', function($q, $$rAF) { - function AnimateRunner() {} - AnimateRunner.all = noop; - AnimateRunner.chain = noop; - AnimateRunner.prototype = { - end: noop, - cancel: noop, - resume: noop, - pause: noop, - complete: noop, - then: function(pass, fail) { - return $q(function(resolve) { - $$rAF(function() { - resolve(); - }); - }).then(pass, fail); - } - }; - return AnimateRunner; - }]; -}; - -// this is prefixed with Core since it conflicts with -// the animateQueueProvider defined in ngAnimate/animateQueue.js -var $$CoreAnimateQueueProvider = function() { - var postDigestQueue = new HashMap(); - var postDigestElements = []; - - this.$get = ['$$AnimateRunner', '$rootScope', - function($$AnimateRunner, $rootScope) { - return { - enabled: noop, - on: noop, - off: noop, - pin: noop, - - push: function(element, event, options, domOperation) { - domOperation && domOperation(); - - options = options || {}; - options.from && element.css(options.from); - options.to && element.css(options.to); - - if (options.addClass || options.removeClass) { - addRemoveClassesPostDigest(element, options.addClass, options.removeClass); - } - - return new $$AnimateRunner(); // jshint ignore:line - } - }; - - 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) { - if (className) { - data[className] = true; - } - }); - } - - 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; - } - } - }); - - forEach(element, function(elm) { - toAdd && jqLiteAddClass(elm, toAdd); - toRemove && jqLiteRemoveClass(elm, toRemove); - }); - postDigestQueue.remove(element); - } - }); - - postDigestElements.length = 0; - }); - } - }]; -}; /** * @ngdoc provider @@ -4891,18 +4501,20 @@ var $$CoreAnimateQueueProvider = function() { * * @description * Default implementation of $animate that doesn't perform any animations, instead just - * synchronously performs DOM updates and resolves the returned runner promise. + * synchronously performs DOM + * updates and calls done() callbacks. * - * In order to enable animations the `ngAnimate` module has to be loaded. + * In order to enable animations the ngAnimate module has to be loaded. * - * To see the functional implementation check out `src/ngAnimate/animate.js`. + * To see the functional implementation check out src/ngAnimate/animate.js */ var $AnimateProvider = ['$provide', function($provide) { - var provider = this; - this.$$registeredAnimations = Object.create(null); - /** + this.$$selectors = {}; + + + /** * @ngdoc method * @name $animateProvider#register * @@ -4911,43 +4523,33 @@ var $AnimateProvider = ['$provide', function($provide) { * animation object which contains callback functions for each event that is expected to be * animated. * - * * `eventFn`: `function(element, ... , doneFunction, options)` - * The element to animate, the `doneFunction` and the options fed into the animation. Depending - * on the type of animation additional arguments will be injected into the animation function. The - * list below explains the function signatures for the different animation methods: - * - * - setClass: function(element, addedClasses, removedClasses, doneFunction, options) - * - addClass: function(element, addedClasses, doneFunction, options) - * - removeClass: function(element, removedClasses, doneFunction, options) - * - enter, leave, move: function(element, doneFunction, options) - * - animate: function(element, fromStyles, toStyles, doneFunction, options) + * * `eventFn`: `function(Element, doneFunction)` The element to animate, the `doneFunction` + * must be called once the element animation is complete. If a function is returned then the + * animation service will use this function to cancel the animation whenever a cancel event is + * triggered. * - * Make sure to trigger the `doneFunction` once the animation is fully complete. * * ```js * return { - * //enter, leave, move signature - * eventFn : function(element, done, options) { - * //code to run the animation - * //once complete, then run done() - * return function endFunction(wasCancelled) { - * //code to cancel the animation - * } - * } - * } + * eventFn : function(element, done) { + * //code to run the animation + * //once complete, then run done() + * return function cancellationFunction() { + * //code to cancel the animation + * } + * } + * } * ``` * - * @param {string} name The name of the animation (this is what the class-based CSS value will be compared to). + * @param {string} name The name of the animation. * @param {Function} factory The factory function that will be executed to return the animation * object. */ this.register = function(name, factory) { - if (name && name.charAt(0) !== '.') { - throw $animateMinErr('notcsel', "Expecting class selector starting with '.' got '{0}'.", name); - } - var key = name + '-animation'; - provider.$$registeredAnimations[name.substr(1)] = key; + if (name && name.charAt(0) != '.') throw $animateMinErr('notcsel', + "Expecting class selector starting with '.' got '{0}'.", name); + this.$$selectors[name.substr(1)] = key; $provide.factory(key, factory); }; @@ -4958,8 +4560,8 @@ var $AnimateProvider = ['$provide', function($provide) { * @description * Sets and/or returns the CSS class regular expression that is checked when performing * an animation. Upon bootstrap the classNameFilter value is not set at all and will - * therefore enable $animate to attempt to perform an animation on any element that is triggered. - * When setting the `classNameFilter` value, animations will only be performed on elements + * therefore enable $animate to attempt to perform an animation on any element. + * When setting the classNameFilter value, animations will only be performed on elements * that successfully match the filter expression. This in turn can boost performance * for low-powered devices as well as applications containing a lot of structural operations. * @param {RegExp=} expression The className expression which will be checked against all animations @@ -4968,344 +4570,296 @@ var $AnimateProvider = ['$provide', function($provide) { this.classNameFilter = function(expression) { if (arguments.length === 1) { this.$$classNameFilter = (expression instanceof RegExp) ? expression : null; - if (this.$$classNameFilter) { - var reservedRegex = new RegExp("(\\s+|\\/)" + NG_ANIMATE_CLASSNAME + "(\\s+|\\/)"); - if (reservedRegex.test(this.$$classNameFilter.toString())) { - throw $animateMinErr('nongcls','$animateProvider.classNameFilter(regex) prohibits accepting a regex value which matches/contains the "{0}" CSS class.', NG_ANIMATE_CLASSNAME); - - } - } } return this.$$classNameFilter; }; - this.$get = ['$$animateQueue', function($$animateQueue) { - function domInsert(element, parentElement, afterElement) { - // if for some reason the previous element was removed - // from the dom sometime before this code runs then let's - // just stick to using the parent element as the anchor - if (afterElement) { - var afterNode = extractElementNode(afterElement); - if (afterNode && !afterNode.parentNode && !afterNode.previousElementSibling) { - afterElement = null; + this.$get = ['$$q', '$$asyncCallback', '$rootScope', function($$q, $$asyncCallback, $rootScope) { + + var currentDefer; + + function runAnimationPostDigest(fn) { + var cancelFn, defer = $$q.defer(); + defer.promise.$$cancelFn = function ngAnimateMaybeCancel() { + cancelFn && cancelFn(); + }; + + $rootScope.$$postDigest(function ngAnimatePostDigest() { + cancelFn = fn(function ngAnimateNotifyComplete() { + defer.resolve(); + }); + }); + + return defer.promise; + } + + function resolveElementClasses(element, classes) { + var toAdd = [], toRemove = []; + + var hasClasses = createMap(); + forEach((element.attr('class') || '').split(/\s+/), function(className) { + hasClasses[className] = true; + }); + + forEach(classes, function(status, className) { + var hasClass = hasClasses[className]; + + // If the most recent class manipulation (via $animate) was to remove the class, and the + // element currently has the class, the class is scheduled for removal. Otherwise, if + // the most recent class manipulation (via $animate) was to add the class, and the + // element does not currently have the class, the class is scheduled to be added. + if (status === false && hasClass) { + toRemove.push(className); + } else if (status === true && !hasClass) { + toAdd.push(className); } + }); + + return (toAdd.length + toRemove.length) > 0 && + [toAdd.length ? toAdd : null, toRemove.length ? toRemove : null]; + } + + function cachedClassManipulation(cache, classes, op) { + for (var i=0, ii = classes.length; i < ii; ++i) { + var className = classes[i]; + cache[className] = op; + } + } + + function asyncPromise() { + // only serve one instance of a promise in order to save CPU cycles + if (!currentDefer) { + currentDefer = $$q.defer(); + $$asyncCallback(function() { + currentDefer.resolve(); + currentDefer = null; + }); + } + return currentDefer.promise; + } + + function applyStyles(element, options) { + if (angular.isObject(options)) { + var styles = extend(options.from || {}, options.to || {}); + element.css(styles); } - afterElement ? afterElement.after(element) : parentElement.prepend(element); } /** + * * @ngdoc service * @name $animate - * @description The $animate service exposes a series of DOM utility methods that provide support - * for animation hooks. The default behavior is the application of DOM operations, however, - * when an animation is detected (and animations are enabled), $animate will do the heavy lifting - * to ensure that animation runs with the triggered DOM operation. - * - * By default $animate doesn't trigger an animations. This is because the `ngAnimate` module isn't - * included and only when it is active then the animation hooks that `$animate` triggers will be - * functional. Once active then all structural `ng-` directives will trigger animations as they perform - * their DOM-related operations (enter, leave and move). Other directives such as `ngClass`, - * `ngShow`, `ngHide` and `ngMessages` also provide support for animations. + * @description The $animate service provides rudimentary DOM manipulation functions to + * insert, remove and move elements within the DOM, as well as adding and removing classes. + * This service is the core service used by the ngAnimate $animator service which provides + * high-level animation hooks for CSS and JavaScript. * - * It is recommended that the`$animate` service is always used when executing DOM-related procedures within directives. + * $animate is available in the AngularJS core, however, the ngAnimate module must be included + * to enable full out animation support. Otherwise, $animate will only perform simple DOM + * manipulation operations. * - * To learn more about enabling animation support, click here to visit the - * {@link ngAnimate ngAnimate module page}. + * To learn more about enabling animation support, click here to visit the {@link ngAnimate + * ngAnimate module page} as well as the {@link ngAnimate.$animate ngAnimate $animate service + * page}. */ return { - // we don't call it directly since non-existant arguments may - // be interpreted as null within the sub enabled function + animate: function(element, from, to) { + applyStyles(element, { from: from, to: to }); + return asyncPromise(); + }, /** * * @ngdoc method - * @name $animate#on + * @name $animate#enter * @kind function - * @description Sets up an event listener to fire whenever the animation event (enter, leave, move, etc...) - * has fired on the given element or among any of its children. Once the listener is fired, the provided callback - * is fired with the following params: - * - * ```js - * $animate.on('enter', container, - * function callback(element, phase) { - * // cool we detected an enter animation within the container - * } - * ); - * ``` - * - * @param {string} event the animation event that will be captured (e.g. enter, leave, move, addClass, removeClass, etc...) - * @param {DOMElement} container the container element that will capture each of the animation events that are fired on itself - * as well as among its children - * @param {Function} callback the callback function that will be fired when the listener is triggered - * - * The arguments present in the callback function are: - * * `element` - The captured DOM element that the animation was fired on. - * * `phase` - The phase of the animation. The two possible phases are **start** (when the animation starts) and **close** (when it ends). + * @description Inserts the element into the DOM either after the `after` element or + * as the first child within the `parent` element. When the function is called a promise + * is returned that will be resolved at a later time. + * @param {DOMElement} element the element which will be inserted into the DOM + * @param {DOMElement} parent the parent element which will append the element as + * a child (if the after element is not present) + * @param {DOMElement} after the sibling element which will append the element + * after itself + * @param {object=} options an optional collection of styles that will be applied to the element. + * @return {Promise} the animation callback promise */ - on: $$animateQueue.on, + enter: function(element, parent, after, options) { + applyStyles(element, options); + after ? after.after(element) + : parent.prepend(element); + return asyncPromise(); + }, /** * * @ngdoc method - * @name $animate#off + * @name $animate#leave * @kind function - * @description Deregisters an event listener based on the event which has been associated with the provided element. This method - * can be used in three different ways depending on the arguments: - * - * ```js - * // remove all the animation event listeners listening for `enter` - * $animate.off('enter'); - * - * // remove all the animation event listeners listening for `enter` on the given element and its children - * $animate.off('enter', container); - * - * // remove the event listener function provided by `listenerFn` that is set - * // to listen for `enter` on the given `element` as well as its children - * $animate.off('enter', container, callback); - * ``` - * - * @param {string} event the animation event (e.g. enter, leave, move, addClass, removeClass, etc...) - * @param {DOMElement=} container the container element the event listener was placed on - * @param {Function=} callback the callback function that was registered as the listener + * @description Removes the element from the DOM. When the function is called a promise + * is returned that will be resolved at a later time. + * @param {DOMElement} element the element which will be removed from the DOM + * @param {object=} options an optional collection of options that will be applied to the element. + * @return {Promise} the animation callback promise */ - off: $$animateQueue.off, + leave: function(element, options) { + applyStyles(element, options); + element.remove(); + return asyncPromise(); + }, /** + * * @ngdoc method - * @name $animate#pin + * @name $animate#move * @kind function - * @description Associates the provided element with a host parent element to allow the element to be animated even if it exists - * outside of the DOM structure of the Angular application. By doing so, any animation triggered via `$animate` can be issued on the - * element despite being outside the realm of the application or within another application. Say for example if the application - * was bootstrapped on an element that is somewhere inside of the `` tag, but we wanted to allow for an element to be situated - * as a direct child of `document.body`, then this can be achieved by pinning the element via `$animate.pin(element)`. Keep in mind - * that calling `$animate.pin(element, parentElement)` will not actually insert into the DOM anywhere; it will just create the association. - * - * Note that this feature is only active when the `ngAnimate` module is used. + * @description Moves the position of the provided element within the DOM to be placed + * either after the `after` element or inside of the `parent` element. When the function + * is called a promise is returned that will be resolved at a later time. * - * @param {DOMElement} element the external element that will be pinned - * @param {DOMElement} parentElement the host parent element that will be associated with the external element + * @param {DOMElement} element the element which will be moved around within the + * DOM + * @param {DOMElement} parent the parent element where the element will be + * inserted into (if the after element is not present) + * @param {DOMElement} after the sibling element where the element will be + * positioned next to + * @param {object=} options an optional collection of options that will be applied to the element. + * @return {Promise} the animation callback promise */ - pin: $$animateQueue.pin, + move: function(element, parent, after, options) { + // Do not remove element before insert. Removing will cause data associated with the + // element to be dropped. Insert will implicitly do the remove. + return this.enter(element, parent, after, options); + }, /** * * @ngdoc method - * @name $animate#enabled + * @name $animate#addClass * @kind function - * @description Used to get and set whether animations are enabled or not on the entire application or on an element and its children. This - * function can be called in four ways: - * - * ```js - * // returns true or false - * $animate.enabled(); - * - * // changes the enabled state for all animations - * $animate.enabled(false); - * $animate.enabled(true); - * - * // returns true or false if animations are enabled for an element - * $animate.enabled(element); - * - * // changes the enabled state for an element and its children - * $animate.enabled(element, true); - * $animate.enabled(element, false); - * ``` - * - * @param {DOMElement=} element the element that will be considered for checking/setting the enabled state - * @param {boolean=} enabled whether or not the animations will be enabled for the element - * - * @return {boolean} whether or not animations are enabled + * @description Adds the provided className CSS class value to the provided element. + * When the function is called a promise is returned that will be resolved at a later time. + * @param {DOMElement} element the element which will have the className value + * added to it + * @param {string} className the CSS class which will be added to the element + * @param {object=} options an optional collection of options that will be applied to the element. + * @return {Promise} the animation callback promise */ - enabled: $$animateQueue.enabled, + addClass: function(element, className, options) { + return this.setClass(element, className, [], options); + }, - /** - * @ngdoc method - * @name $animate#cancel - * @kind function - * @description Cancels the provided animation. - * - * @param {Promise} animationPromise The animation promise that is returned when an animation is started. - */ - cancel: function(runner) { - runner.end && runner.end(); + $$addClassImmediately: function(element, className, options) { + element = jqLite(element); + className = !isString(className) + ? (isArray(className) ? className.join(' ') : '') + : className; + forEach(element, function(element) { + jqLiteAddClass(element, className); + }); + applyStyles(element, options); + return asyncPromise(); }, /** * * @ngdoc method - * @name $animate#enter + * @name $animate#removeClass * @kind function - * @description Inserts the element into the DOM either after the `after` element (if provided) or - * as the first child within the `parent` element and then triggers an animation. - * A promise is returned that will be resolved during the next digest once the animation - * has completed. - * - * @param {DOMElement} element the element which will be inserted into the DOM - * @param {DOMElement} parent the parent element which will append the element as - * a child (so long as the after element is not present) - * @param {DOMElement=} after the sibling element after which the element will be appended - * @param {object=} options an optional collection of options/styles that will be applied to the element - * + * @description Removes the provided className CSS class value from the provided element. + * When the function is called a promise is returned that will be resolved at a later time. + * @param {DOMElement} element the element which will have the className value + * removed from it + * @param {string} className the CSS class which will be removed from the element + * @param {object=} options an optional collection of options that will be applied to the element. * @return {Promise} the animation callback promise */ - enter: function(element, parent, after, options) { - parent = parent && jqLite(parent); - after = after && jqLite(after); - parent = parent || after.parent(); - domInsert(element, parent, after); - return $$animateQueue.push(element, 'enter', prepareAnimateOptions(options)); + removeClass: function(element, className, options) { + return this.setClass(element, [], className, options); + }, + + $$removeClassImmediately: function(element, className, options) { + element = jqLite(element); + className = !isString(className) + ? (isArray(className) ? className.join(' ') : '') + : className; + forEach(element, function(element) { + jqLiteRemoveClass(element, className); + }); + applyStyles(element, options); + return asyncPromise(); }, /** * * @ngdoc method - * @name $animate#move + * @name $animate#setClass * @kind function - * @description Inserts (moves) the element into its new position in the DOM either after - * the `after` element (if provided) or as the first child within the `parent` element - * and then triggers an animation. A promise is returned that will be resolved - * during the next digest once the animation has completed. - * - * @param {DOMElement} element the element which will be moved into the new DOM position - * @param {DOMElement} parent the parent element which will append the element as - * a child (so long as the after element is not present) - * @param {DOMElement=} after the sibling element after which the element will be appended - * @param {object=} options an optional collection of options/styles that will be applied to the element - * + * @description Adds and/or removes the given CSS classes to and from the element. + * When the function is called a promise is returned that will be resolved at a later time. + * @param {DOMElement} element the element which will have its CSS classes changed + * removed from it + * @param {string} add the CSS classes which will be added to the element + * @param {string} remove the CSS class which will be removed from the element + * @param {object=} options an optional collection of options that will be applied to the element. * @return {Promise} the animation callback promise */ - move: function(element, parent, after, options) { - parent = parent && jqLite(parent); - after = after && jqLite(after); - parent = parent || after.parent(); - domInsert(element, parent, after); - return $$animateQueue.push(element, 'move', prepareAnimateOptions(options)); - }, + setClass: function(element, add, remove, options) { + var self = this; + var STORAGE_KEY = '$$animateClasses'; + var createdCache = false; + element = jqLite(element); + + var cache = element.data(STORAGE_KEY); + if (!cache) { + cache = { + classes: {}, + options: options + }; + createdCache = true; + } else if (options && cache.options) { + cache.options = angular.extend(cache.options || {}, options); + } - /** - * @ngdoc method - * @name $animate#leave - * @kind function - * @description Triggers an animation and then removes the element from the DOM. - * When the function is called a promise is returned that will be resolved during the next - * digest once the animation has completed. - * - * @param {DOMElement} element the element which will be removed from the DOM - * @param {object=} options an optional collection of options/styles that will be applied to the element - * - * @return {Promise} the animation callback promise - */ - leave: function(element, options) { - return $$animateQueue.push(element, 'leave', prepareAnimateOptions(options), function() { - element.remove(); - }); - }, + var classes = cache.classes; + + add = isArray(add) ? add : add.split(' '); + remove = isArray(remove) ? remove : remove.split(' '); + cachedClassManipulation(classes, add, true); + cachedClassManipulation(classes, remove, false); + + if (createdCache) { + cache.promise = runAnimationPostDigest(function(done) { + var cache = element.data(STORAGE_KEY); + element.removeData(STORAGE_KEY); + + // in the event that the element is removed before postDigest + // is run then the cache will be undefined and there will be + // no need anymore to add or remove and of the element classes + if (cache) { + var classes = resolveElementClasses(element, cache.classes); + if (classes) { + self.$$setClassImmediately(element, classes[0], classes[1], cache.options); + } + } - /** - * @ngdoc method - * @name $animate#addClass - * @kind function - * - * @description Triggers an addClass animation surrounding the addition of the provided CSS class(es). Upon - * execution, the addClass operation will only be handled after the next digest and it will not trigger an - * animation if element already contains the CSS class or if the class is removed at a later step. - * Note that class-based animations are treated differently compared to structural animations - * (like enter, move and leave) since the CSS classes may be added/removed at different points - * depending if CSS or JavaScript animations are used. - * - * @param {DOMElement} element the element which the CSS classes will be applied to - * @param {string} className the CSS class(es) that will be added (multiple classes are separated via spaces) - * @param {object=} options an optional collection of options/styles that will be applied to the element - * - * @return {Promise} the animation callback promise - */ - addClass: function(element, className, options) { - options = prepareAnimateOptions(options); - options.addClass = mergeClasses(options.addclass, className); - return $$animateQueue.push(element, 'addClass', options); - }, + done(); + }); + element.data(STORAGE_KEY, cache); + } - /** - * @ngdoc method - * @name $animate#removeClass - * @kind function - * - * @description Triggers a removeClass animation surrounding the removal of the provided CSS class(es). Upon - * execution, the removeClass operation will only be handled after the next digest and it will not trigger an - * animation if element does not contain the CSS class or if the class is added at a later step. - * Note that class-based animations are treated differently compared to structural animations - * (like enter, move and leave) since the CSS classes may be added/removed at different points - * depending if CSS or JavaScript animations are used. - * - * @param {DOMElement} element the element which the CSS classes will be applied to - * @param {string} className the CSS class(es) that will be removed (multiple classes are separated via spaces) - * @param {object=} options an optional collection of options/styles that will be applied to the element - * - * @return {Promise} the animation callback promise - */ - removeClass: function(el