summaryrefslogtreecommitdiff
path: root/www/lib/ionic/js/ionic.bundle.js
diff options
context:
space:
mode:
authorPliable Pixels <pliablepixels@gmail.com>2017-09-21 12:49:18 -0400
committerPliable Pixels <pliablepixels@gmail.com>2017-09-21 12:49:18 -0400
commitb28028ac4082842143b0f528d6bc539da6ccb419 (patch)
tree1e26ea969a781ed8e323fca4e3c76345113fc694 /www/lib/ionic/js/ionic.bundle.js
parent676270d21beed31d767a06c89522198c77d5d865 (diff)
mega changes, including updates and X
Diffstat (limited to 'www/lib/ionic/js/ionic.bundle.js')
-rw-r--r--www/lib/ionic/js/ionic.bundle.js8890
1 files changed, 2847 insertions, 6043 deletions
diff --git a/www/lib/ionic/js/ionic.bundle.js b/www/lib/ionic/js/ionic.bundle.js
index 091bb875..75fbaecf 100644
--- a/www/lib/ionic/js/ionic.bundle.js
+++ b/www/lib/ionic/js/ionic.bundle.js
@@ -9,7 +9,7 @@
* Copyright 2015 Drifty Co.
* http://drifty.com/
*
- * Ionic, v1.3.3
+ * Ionic, v1.2.4-nightly-1917
* A powerful HTML5 mobile app framework.
* http://ionicframework.com/
*
@@ -25,7 +25,7 @@
// build processes may have already created an ionic obj
window.ionic = window.ionic || {};
window.ionic.views = {};
-window.ionic.version = '1.3.3';
+window.ionic.version = '1.2.4-nightly-1917';
(function (ionic) {
@@ -580,7 +580,7 @@ window.ionic.version = '1.3.3';
*
* `hold`, `tap`, `doubletap`, `drag`, `dragstart`, `dragend`, `dragup`, `dragdown`, <br/>
* `dragleft`, `dragright`, `swipe`, `swipeup`, `swipedown`, `swipeleft`, `swiperight`, <br/>
- * `transform`, `transformstart`, `transformend`, `rotate`, `pinch`, `pinchin`, `pinchout`, <br/>
+ * `transform`, `transformstart`, `transformend`, `rotate`, `pinch`, `pinchin`, `pinchout`, </br>
* `touch`, `release`
*
* @param {string} eventType The gesture event to listen for.
@@ -2113,11 +2113,6 @@ window.ionic.version = '1.3.3';
* @returns {string} What grade the current platform is.
*/
grade: null,
- /**
- * @ngdoc property
- * @name ionic.Platform#ua
- * @returns {string} What User Agent is.
- */
ua: navigator.userAgent,
/**
@@ -2196,7 +2191,7 @@ window.ionic.version = '1.3.3';
self.platforms.push('webview');
if (!(!window.cordova && !window.PhoneGap && !window.phonegap)) {
self.platforms.push('cordova');
- } else if (typeof window.forge === 'object') {
+ } else if (window.forge) {
self.platforms.push('trigger');
}
} else {
@@ -2236,7 +2231,7 @@ window.ionic.version = '1.3.3';
* @returns {boolean} Check if we are running within a WebView (such as Cordova).
*/
isWebView: function() {
- return !(!window.cordova && !window.PhoneGap && !window.phonegap && window.forge !== 'object');
+ return !(!window.cordova && !window.PhoneGap && !window.phonegap && !window.forge);
},
/**
* @ngdoc method
@@ -2397,7 +2392,7 @@ window.ionic.version = '1.3.3';
/**
* @ngdoc method
* @name ionic.Platform#showStatusBar
- * @description Shows or hides the device status bar (in Cordova). Requires `ionic plugin add cordova-plugin-statusbar`
+ * @description Shows or hides the device status bar (in Cordova). Requires `cordova plugin add org.apache.cordova.statusbar`
* @param {boolean} shouldShow Whether or not to show the status bar.
*/
showStatusBar: function(val) {
@@ -2424,7 +2419,7 @@ window.ionic.version = '1.3.3';
* @name ionic.Platform#fullScreen
* @description
* Sets whether the app is fullscreen or not (in Cordova).
- * @param {boolean=} showFullScreen Whether or not to set the app to fullscreen. Defaults to true. Requires `ionic plugin add cordova-plugin-statusbar`
+ * @param {boolean=} showFullScreen Whether or not to set the app to fullscreen. Defaults to true. Requires `cordova plugin add org.apache.cordova.statusbar`
* @param {boolean=} showStatusBar Whether or not to show the device's status bar. Defaults to false.
*/
fullScreen: function(showFullScreen, showStatusBar) {
@@ -2673,7 +2668,7 @@ window.ionic.version = '1.3.3';
* - Works with labels surrounding inputs
* - Does not fire off a click if the user moves the pointer too far
* - Adds and removes an 'activated' css class
- * - Multiple [unit tests](https://github.com/driftyco/ionic/blob/1.x/test/unit/utils/tap.unit.js) for each scenario
+ * - Multiple [unit tests](https://github.com/driftyco/ionic/blob/master/test/unit/utils/tap.unit.js) for each scenario
*
*/
/*
@@ -2884,7 +2879,7 @@ ionic.tap = {
if (ele && ele.nodeType === 1) {
var element = ele;
while (element) {
- if (element.getAttribute && element.getAttribute('data-tap-disabled') == 'true') {
+ if ((element.dataset ? element.dataset.tapDisabled : element.getAttribute && element.getAttribute('data-tap-disabled')) == 'true') {
return true;
}
element = element.parentElement;
@@ -2984,7 +2979,7 @@ function tapMouseDown(e) {
e.stopPropagation();
if (!ionic.Platform.isEdge() && (!ionic.tap.isTextInput(e.target) || tapLastTouchTarget !== e.target) &&
- !isSelectOrOption(e.target.tagName) && !e.target.isContentEditable && !ionic.tap.isVideo(e.target)) {
+ !isSelectOrOption(e.target.tagName) && !ionic.tap.isVideo(e.target)) {
// If you preventDefault on a text input then you cannot move its text caret/cursor.
// Allow through only the text input default. However, without preventDefault on an
// input the 300ms delay can change focus on inputs after the keyboard shows up.
@@ -3108,10 +3103,6 @@ function tapIgnoreEvent(e) {
return true;
}
- if(e.target.tagName == 'SELECT') {
- return true;
- }
-
if (ionic.scroll.isScrolling && ionic.tap.containsOrIsTextInput(e.target)) {
e.preventDefault();
return true;
@@ -3280,7 +3271,7 @@ ionic.DomUtil.ready(function() {
eleToActivate = ele;
break;
}
- if (ele.classList && ele.classList.contains('button')) {
+ if (ele.classList.contains('button')) {
eleToActivate = ele;
break;
}
@@ -7012,8 +7003,6 @@ ionic.scroll = {
if(options.startY >= 0 || options.startX >= 0) {
ionic.requestAnimationFrame(function() {
- self.__originalContainerHeight = self.el.getBoundingClientRect().height;
-
self.el.scrollTop = options.startY || 0;
self.el.scrollLeft = options.startX || 0;
@@ -7511,18 +7500,11 @@ ionic.scroll = {
};
self.handleTouchMove = function(e) {
- if (self.__frozenShut) {
+ if(self.__frozenShut) {
e.preventDefault();
e.stopPropagation();
return false;
-
- } else if ( self.__frozen ){
- e.preventDefault();
- // let it propagate so other events such as drag events can happen,
- // but don't let it actually scroll
- return false;
}
- return true;
};
container.addEventListener('scroll', self.onScroll);
@@ -7546,14 +7528,15 @@ ionic.scroll = {
var self = this;
var container = self.__container;
+ container.removeEventListener('resetScrollView', self.resetScrollView);
container.removeEventListener('scroll', self.onScroll);
+
container.removeEventListener('scrollChildIntoView', self.scrollChildIntoView);
+ container.removeEventListener('resetScrollView', self.resetScrollView);
container.removeEventListener(ionic.EVENTS.touchstart, self.handleTouchMove);
container.removeEventListener(ionic.EVENTS.touchmove, self.handleTouchMove);
- document.removeEventListener('resetScrollView', self.resetScrollView);
-
ionic.tap.removeClonedInputs(container, self);
delete self.__container;
@@ -10761,11 +10744,6 @@ ionic.views.Slider = ionic.views.View.inherit({
s.emit('onTransitionStart', s);
if (s.activeIndex !== s.previousIndex) {
s.emit('onSlideChangeStart', s);
- _scope.$emit("$ionicSlides.slideChangeStart", {
- slider: s,
- activeIndex: s.getSlideDataIndex(s.activeIndex),
- previousIndex: s.getSlideDataIndex(s.previousIndex)
- });
if (s.activeIndex > s.previousIndex) {
s.emit('onSlideNextStart', s);
}
@@ -10785,11 +10763,6 @@ ionic.views.Slider = ionic.views.View.inherit({
s.emit('onTransitionEnd', s);
if (s.activeIndex !== s.previousIndex) {
s.emit('onSlideChangeEnd', s);
- _scope.$emit("$ionicSlides.slideChangeEnd", {
- slider: s,
- activeIndex: s.getSlideDataIndex(s.activeIndex),
- previousIndex: s.getSlideDataIndex(s.previousIndex)
- });
if (s.activeIndex > s.previousIndex) {
s.emit('onSlideNextEnd', s);
}
@@ -11004,49 +10977,14 @@ ionic.views.Slider = ionic.views.View.inherit({
}
s.observers = [];
};
-
- s.updateLoop = function(){
- var currentSlide = s.slides.eq(s.activeIndex);
- if ( angular.element(currentSlide).hasClass(s.params.slideDuplicateClass) ){
- // we're on a duplicate, so slide to the non-duplicate
- var swiperSlideIndex = angular.element(currentSlide).attr("data-swiper-slide-index");
- var slides = s.wrapper.children('.' + s.params.slideClass);
- for ( var i = 0; i < slides.length; i++ ){
- if ( !angular.element(slides[i]).hasClass(s.params.slideDuplicateClass) && angular.element(slides[i]).attr("data-swiper-slide-index") === swiperSlideIndex ){
- s.slideTo(i, 0, false, true);
- break;
- }
- }
- // if we needed to switch slides, we did that. So, now call the createLoop function internally
- setTimeout(function(){
- s.createLoop();
- }, 50);
- }
- }
-
- s.getSlideDataIndex = function(slideIndex){
- // this is an Ionic custom function
- // Swiper loops utilize duplicate DOM elements for slides when in a loop
- // which means that we cannot rely on the actual slide index for our events
- // because index 0 does not necessarily point to index 0
- // and index n+1 does not necessarily point to the expected piece of data
- // therefore, rather than using the actual slide index we should
- // use the data index that swiper includes as an attribute on the dom elements
- // because this is what will be meaningful to the consumer of our events
- var slide = s.slides.eq(slideIndex);
- var attributeIndex = angular.element(slide).attr("data-swiper-slide-index");
- return parseInt(attributeIndex);
- }
-
/*=========================
Loop
===========================*/
// Create looped slides
s.createLoop = function () {
- //console.log("Slider create loop method");
- //var toRemove = s.wrapper.children('.' + s.params.slideClass + '.' + s.params.slideDuplicateClass);
- //angular.element(toRemove).remove();
- s.wrapper.children('.' + s.params.slideClass + '.' + s.params.slideDuplicateClass).remove();
+
+ var toRemove = s.wrapper.children('.' + s.params.slideClass + '.' + s.params.slideDuplicateClass);
+ angular.element(toRemove).remove();
var slides = s.wrapper.children('.' + s.params.slideClass);
@@ -11066,17 +11004,16 @@ ionic.views.Slider = ionic.views.View.inherit({
slide.attr('data-swiper-slide-index', index);
});
for (i = 0; i < appendSlides.length; i++) {
-
newNode = angular.element(appendSlides[i]).clone().addClass(s.params.slideDuplicateClass);
newNode.removeAttr('ng-transclude');
newNode.removeAttr('ng-repeat');
scope = angular.element(appendSlides[i]).scope();
newNode = $compile(newNode)(scope);
angular.element(s.wrapper).append(newNode);
- //s.wrapper.append($(appendSlides[i].cloneNode(true)).addClass(s.params.slideDuplicateClass));
+ //s.wrapper.append($(appendSlides[i].cloneNode(true)).addClass(s.params.slideDuplicateClass));
}
for (i = prependSlides.length - 1; i >= 0; i--) {
- //s.wrapper.prepend($(prependSlides[i].cloneNode(true)).addClass(s.params.slideDuplicateClass));
+ //s.wrapper.prepend($(prependSlides[i].cloneNode(true)).addClass(s.params.slideDuplicateClass));
newNode = angular.element(prependSlides[i]).clone().addClass(s.params.slideDuplicateClass);
newNode.removeAttr('ng-transclude');
@@ -13374,8 +13311,8 @@ ionic.views.Slider = ionic.views.View.inherit({
*/
/**
- * @license AngularJS v1.5.3
- * (c) 2010-2016 Google, Inc. http://angularjs.org
+ * @license AngularJS v1.4.3
+ * (c) 2010-2015 Google, Inc. http://angularjs.org
* License: MIT
*/
(function(window, document, undefined) {'use strict';
@@ -13432,7 +13369,7 @@ function minErr(module, ErrorConstructor) {
return match;
});
- message += '\nhttp://errors.angularjs.org/1.5.3/' +
+ message += '\nhttp://errors.angularjs.org/1.4.3/' +
(module ? module + '/' : '') + code;
for (i = SKIP_INDEXES, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
@@ -13563,9 +13500,29 @@ var REGEX_STRING_REGEXP = /^\/(.+)\/([a-z]*)$/;
// This is used so that it's possible for internal tests to create mock ValidityStates.
var VALIDITY_STATE_PROPERTY = 'validity';
+/**
+ * @ngdoc function
+ * @name angular.lowercase
+ * @module ng
+ * @kind function
+ *
+ * @description Converts the specified string to lowercase.
+ * @param {string} string String to be converted to lowercase.
+ * @returns {string} Lowercased string.
+ */
+var lowercase = function(string) {return isString(string) ? string.toLowerCase() : string;};
var hasOwnProperty = Object.prototype.hasOwnProperty;
-var lowercase = function(string) {return isString(string) ? string.toLowerCase() : string;};
+/**
+ * @ngdoc function
+ * @name angular.uppercase
+ * @module ng
+ * @kind function
+ *
+ * @description Converts the specified string to uppercase.
+ * @param {string} string String to be converted to uppercase.
+ * @returns {string} Uppercased string.
+ */
var uppercase = function(string) {return isString(string) ? string.toUpperCase() : string;};
@@ -13585,7 +13542,7 @@ var manualUppercase = function(s) {
// String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish
// locale, for this reason we need to detect this case and redefine lowercase/uppercase methods
-// with correct but slower alternatives. See https://github.com/angular/angular.js/issues/11387
+// with correct but slower alternatives.
if ('i' !== 'I'.toLowerCase()) {
lowercase = manualLowercase;
uppercase = manualUppercase;
@@ -13622,25 +13579,20 @@ msie = document.documentMode;
* String ...)
*/
function isArrayLike(obj) {
-
- // `null`, `undefined` and `window` are not array-like
- if (obj == null || isWindow(obj)) return false;
-
- // arrays, strings and jQuery/jqLite objects are array like
- // * jqLite is either the jQuery or jqLite constructor function
- // * we have to check the existence of jqLite first as this method is called
- // via the forEach method when constructing the jqLite object in the first place
- if (isArray(obj) || isString(obj) || (jqLite && obj instanceof jqLite)) return true;
+ if (obj == null || isWindow(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;
- // NodeList objects (with `item` method) and
- // other objects with suitable length characteristics are array-like
- return isNumber(length) &&
- (length >= 0 && ((length - 1) in obj || obj instanceof Array) || typeof obj.item == 'function');
+ if (obj.nodeType === NODE_TYPE_ELEMENT && length) {
+ return true;
+ }
+ return isString(obj) || isArray(obj) || length === 0 ||
+ typeof length === 'number' && length > 0 && (length - 1) in obj;
}
/**
@@ -13660,7 +13612,7 @@ function isArrayLike(obj) {
*
* Unlike ES262's
* [Array.prototype.forEach](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18),
- * providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just
+ * Providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just
* return the value provided.
*
```js
@@ -13737,7 +13689,7 @@ function forEachSorted(obj, iterator, context) {
* @returns {function(*, string)}
*/
function reverseParams(iteratorFn) {
- return function(value, key) {iteratorFn(key, value);};
+ return function(value, key) { iteratorFn(key, value); };
}
/**
@@ -13783,12 +13735,6 @@ function baseExtend(dst, objs, deep) {
if (deep && isObject(src)) {
if (isDate(src)) {
dst[key] = new Date(src.valueOf());
- } else if (isRegExp(src)) {
- dst[key] = new RegExp(src);
- } else if (src.nodeName) {
- dst[key] = src.cloneNode(true);
- } else if (isElement(src)) {
- dst[key] = src.clone();
} else {
if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {};
baseExtend(dst[key], [src], true);
@@ -13901,10 +13847,10 @@ function identity($) {return $;}
identity.$inject = [];
-function valueFn(value) {return function valueRef() {return value;};}
+function valueFn(value) {return function() {return value;};}
function hasCustomToString(obj) {
- return isFunction(obj.toString) && obj.toString !== toString;
+ return isFunction(obj.toString) && obj.toString !== Object.prototype.toString;
}
@@ -14103,13 +14049,9 @@ function isPromiseLike(obj) {
}
-var TYPED_ARRAY_REGEXP = /^\[object (?:Uint8|Uint8Clamped|Uint16|Uint32|Int8|Int16|Int32|Float32|Float64)Array\]$/;
+var TYPED_ARRAY_REGEXP = /^\[object (Uint8(Clamped)?)|(Uint16)|(Uint32)|(Int8)|(Int16)|(Int32)|(Float(32)|(64))Array\]$/;
function isTypedArray(value) {
- return value && isNumber(value.length) && TYPED_ARRAY_REGEXP.test(toString.call(value));
-}
-
-function isArrayBuffer(obj) {
- return toString.call(obj) === '[object ArrayBuffer]';
+ return TYPED_ARRAY_REGEXP.test(toString.call(value));
}
@@ -14149,7 +14091,7 @@ function isElement(node) {
* @returns {object} in the form of {key1:true, key2:true, ...}
*/
function makeMap(str) {
- var obj = {}, items = str.split(','), i;
+ var obj = {}, items = str.split(","), i;
for (i = 0; i < items.length; i++) {
obj[items[i]] = true;
}
@@ -14231,141 +14173,98 @@ function arrayRemove(array, value) {
</file>
</example>
*/
-function copy(source, destination) {
- var stackSource = [];
- var stackDest = [];
-
- if (destination) {
- if (isTypedArray(destination) || isArrayBuffer(destination)) {
- throw ngMinErr('cpta', "Can't copy! TypedArray destination cannot be mutated.");
- }
- if (source === destination) {
- throw ngMinErr('cpi', "Can't copy! Source and destination are identical.");
- }
+function copy(source, destination, stackSource, stackDest) {
+ if (isWindow(source) || isScope(source)) {
+ 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 (isObject(source)) {
+ var index;
+ if (stackSource && (index = stackSource.indexOf(source)) !== -1) {
+ return stackDest[index];
+ }
+
+ // TypedArray, Date and RegExp have specific copy functionality and must be
+ // pushed onto the stack before returning.
+ // Array and other objects create the base object and recurse to copy child
+ // objects. The array/object will be pushed onto the stack when recursed.
+ if (isArray(source)) {
+ return 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 {
+ var emptyObject = Object.create(getPrototypeOf(source));
+ return copy(source, emptyObject, stackSource, stackDest);
+ }
- // Empty the destination object
- if (isArray(destination)) {
- destination.length = 0;
- } else {
- forEach(destination, function(value, key) {
- if (key !== '$$hashKey') {
- delete destination[key];
- }
- });
+ if (stackDest) {
+ stackSource.push(source);
+ stackDest.push(destination);
+ }
}
+ } else {
+ if (source === destination) throw ngMinErr('cpi',
+ "Can't copy! Source and destination are identical.");
- stackSource.push(source);
- stackDest.push(destination);
- return copyRecurse(source, destination);
- }
+ stackSource = stackSource || [];
+ stackDest = stackDest || [];
- return copyElement(source);
+ if (isObject(source)) {
+ stackSource.push(source);
+ stackDest.push(destination);
+ }
- function copyRecurse(source, destination) {
- var h = destination.$$hashKey;
- var key;
+ var result, key;
if (isArray(source)) {
- for (var i = 0, ii = source.length; i < ii; i++) {
- destination.push(copyElement(source[i]));
+ destination.length = 0;
+ for (var i = 0; i < source.length; i++) {
+ destination.push(copy(source[i], null, stackSource, stackDest));
}
- } else if (isBlankObject(source)) {
- // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
- for (key in source) {
- destination[key] = copyElement(source[key]);
+ } else {
+ var h = destination.$$hashKey;
+ if (isArray(destination)) {
+ destination.length = 0;
+ } else {
+ forEach(destination, function(value, key) {
+ delete destination[key];
+ });
}
- } else if (source && typeof source.hasOwnProperty === 'function') {
- // Slow path, which must rely on hasOwnProperty
- for (key in source) {
- if (source.hasOwnProperty(key)) {
- destination[key] = copyElement(source[key]);
+ if (isBlankObject(source)) {
+ // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
+ for (key in source) {
+ destination[key] = copy(source[key], null, stackSource, stackDest);
}
- }
- } else {
- // Slowest path --- hasOwnProperty can't be called as a method
- for (key in source) {
- if (hasOwnProperty.call(source, key)) {
- destination[key] = copyElement(source[key]);
+ } else if (source && typeof source.hasOwnProperty === 'function') {
+ // Slow path, which must rely on hasOwnProperty
+ for (key in source) {
+ if (source.hasOwnProperty(key)) {
+ destination[key] = copy(source[key], null, stackSource, stackDest);
+ }
+ }
+ } else {
+ // Slowest path --- hasOwnProperty can't be called as a method
+ for (key in source) {
+ if (hasOwnProperty.call(source, key)) {
+ destination[key] = copy(source[key], null, stackSource, stackDest);
+ }
}
}
- }
- setHashKey(destination, h);
- return destination;
- }
-
- function copyElement(source) {
- // Simple values
- if (!isObject(source)) {
- return source;
- }
-
- // Already copied values
- var index = stackSource.indexOf(source);
- if (index !== -1) {
- return stackDest[index];
- }
-
- if (isWindow(source) || isScope(source)) {
- throw ngMinErr('cpws',
- "Can't copy! Making copies of Window or Scope instances is not supported.");
- }
-
- var needsRecurse = false;
- var destination = copyType(source);
-
- if (destination === undefined) {
- destination = isArray(source) ? [] : Object.create(getPrototypeOf(source));
- needsRecurse = true;
- }
-
- stackSource.push(source);
- stackDest.push(destination);
-
- return needsRecurse
- ? copyRecurse(source, destination)
- : destination;
- }
-
- function copyType(source) {
- switch (toString.call(source)) {
- case '[object Int8Array]':
- case '[object Int16Array]':
- case '[object Int32Array]':
- case '[object Float32Array]':
- case '[object Float64Array]':
- case '[object Uint8Array]':
- case '[object Uint8ClampedArray]':
- case '[object Uint16Array]':
- case '[object Uint32Array]':
- return new source.constructor(copyElement(source.buffer));
-
- case '[object ArrayBuffer]':
- //Support: IE10
- if (!source.slice) {
- var copied = new ArrayBuffer(source.byteLength);
- new Uint8Array(copied).set(new Uint8Array(source));
- return copied;
- }
- return source.slice(0);
-
- case '[object Boolean]':
- case '[object Number]':
- case '[object String]':
- case '[object Date]':
- return new source.constructor(source.valueOf());
-
- case '[object RegExp]':
- var re = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
- re.lastIndex = source.lastIndex;
- return re;
-
- case '[object Blob]':
- return new source.constructor([source], {type: source.type});
- }
-
- if (isFunction(source.cloneNode)) {
- return source.cloneNode(true);
+ setHashKey(destination,h);
}
}
+ return destination;
}
/**
@@ -14428,76 +14327,60 @@ function equals(o1, o2) {
if (o1 === null || o2 === null) return false;
if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
- if (t1 == t2 && t1 == 'object') {
- if (isArray(o1)) {
- if (!isArray(o2)) return false;
- if ((length = o1.length) == o2.length) {
- for (key = 0; key < length; key++) {
+ if (t1 == t2) {
+ if (t1 == 'object') {
+ if (isArray(o1)) {
+ if (!isArray(o2)) return false;
+ if ((length = o1.length) == o2.length) {
+ for (key = 0; key < length; key++) {
+ if (!equals(o1[key], o2[key])) return false;
+ }
+ return true;
+ }
+ } else if (isDate(o1)) {
+ if (!isDate(o2)) return false;
+ return equals(o1.getTime(), o2.getTime());
+ } else if (isRegExp(o1)) {
+ return isRegExp(o2) ? o1.toString() == o2.toString() : false;
+ } else {
+ if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) ||
+ isArray(o2) || isDate(o2) || isRegExp(o2)) return false;
+ keySet = createMap();
+ 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) &&
+ key.charAt(0) !== '$' &&
+ o2[key] !== undefined &&
+ !isFunction(o2[key])) return false;
}
return true;
}
- } else if (isDate(o1)) {
- if (!isDate(o2)) return false;
- return equals(o1.getTime(), o2.getTime());
- } else if (isRegExp(o1)) {
- if (!isRegExp(o2)) return false;
- return o1.toString() == o2.toString();
- } else {
- if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) ||
- isArray(o2) || isDate(o2) || isRegExp(o2)) return false;
- keySet = createMap();
- 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) &&
- key.charAt(0) !== '$' &&
- isDefined(o2[key]) &&
- !isFunction(o2[key])) return false;
- }
- return true;
}
}
return false;
}
var csp = function() {
- if (!isDefined(csp.rules)) {
-
-
- var ngCspElement = (document.querySelector('[ng-csp]') ||
- document.querySelector('[data-ng-csp]'));
-
- if (ngCspElement) {
- var ngCspAttribute = ngCspElement.getAttribute('ng-csp') ||
- ngCspElement.getAttribute('data-ng-csp');
- csp.rules = {
- noUnsafeEval: !ngCspAttribute || (ngCspAttribute.indexOf('no-unsafe-eval') !== -1),
- noInlineStyle: !ngCspAttribute || (ngCspAttribute.indexOf('no-inline-style') !== -1)
- };
- } else {
- csp.rules = {
- noUnsafeEval: noUnsafeEval(),
- noInlineStyle: false
- };
- }
- }
+ if (isDefined(csp.isActive_)) return csp.isActive_;
- return csp.rules;
+ var active = !!(document.querySelector('[ng-csp]') ||
+ document.querySelector('[data-ng-csp]'));
- function noUnsafeEval() {
+ if (!active) {
try {
/* jshint -W031, -W054 */
new Function('');
/* jshint +W031, +W054 */
- return false;
} catch (e) {
- return true;
+ active = true;
}
}
+
+ return (csp.isActive_ = active);
};
/**
@@ -14635,7 +14518,7 @@ function toJsonReplacer(key, value) {
* @returns {string|undefined} JSON-ified string representing `obj`.
*/
function toJson(obj, pretty) {
- if (isUndefined(obj)) return undefined;
+ if (typeof obj === 'undefined') return undefined;
if (!isNumber(pretty)) {
pretty = pretty ? 2 : null;
}
@@ -14662,10 +14545,7 @@ function fromJson(json) {
}
-var ALL_COLONS = /:/g;
function timezoneToOffset(timezone, fallback) {
- // IE/Edge do not "understand" colon (`:`) in timezone
- timezone = timezone.replace(ALL_COLONS, '');
var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;
return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset;
}
@@ -14680,9 +14560,8 @@ function addDateMinutes(date, minutes) {
function convertTimezoneToLocal(date, timezone, reverse) {
reverse = reverse ? -1 : 1;
- var dateTimezoneOffset = date.getTimezoneOffset();
- var timezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset);
- return addDateMinutes(date, reverse * (timezoneOffset - dateTimezoneOffset));
+ var timezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset());
+ return addDateMinutes(date, reverse * (timezoneOffset - date.getTimezoneOffset()));
}
@@ -14701,7 +14580,7 @@ function startingTag(element) {
return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) :
elemHtml.
match(/^(<[^>]+>)/)[1].
- replace(/^<([\w\-]+)/, function(match, nodeName) {return '<' + lowercase(nodeName);});
+ replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
} catch (e) {
return lowercase(elemHtml);
}
@@ -14733,19 +14612,13 @@ function tryDecodeURIComponent(value) {
* @returns {Object.<string,boolean|Array>}
*/
function parseKeyValue(/**string*/keyValue) {
- var obj = {};
+ var obj = {}, key_value, key;
forEach((keyValue || "").split('&'), function(keyValue) {
- var splitPoint, key, val;
if (keyValue) {
- 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);
+ key_value = keyValue.replace(/\+/g,'%20').split('=');
+ key = tryDecodeURIComponent(key_value[0]);
if (isDefined(key)) {
- val = isDefined(val) ? tryDecodeURIComponent(val) : true;
+ var val = isDefined(key_value[1]) ? tryDecodeURIComponent(key_value[1]) : true;
if (!hasOwnProperty.call(obj, key)) {
obj[key] = val;
} else if (isArray(obj[key])) {
@@ -14849,17 +14722,10 @@ function getNgAttribute(element, ngAttr) {
* designates the **root element** of the application and is typically placed near the root element
* of the page - e.g. on the `<body>` or `<html>` tags.
*
- * There are a few things to keep in mind when using `ngApp`:
- * - only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp`
- * found in the document will be used to define the root element to auto-bootstrap as an
- * application. To run multiple applications in an HTML document you must manually bootstrap them using
- * {@link angular.bootstrap} instead.
- * - AngularJS applications cannot be nested within each other.
- * - Do not use a directive that uses {@link ng.$compile#transclusion transclusion} on the same element as `ngApp`.
- * This includes directives such as {@link ng.ngIf `ngIf`}, {@link ng.ngInclude `ngInclude`} and
- * {@link ngRoute.ngView `ngView`}.
- * Doing this misplaces the app {@link ng.$rootElement `$rootElement`} and the app's {@link auto.$injector injector},
- * causing animations to stop working and making the injector inaccessible from outside the app.
+ * Only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp`
+ * found in the document will be used to define the root element to auto-bootstrap as an
+ * application. To run multiple applications in an HTML document you must manually bootstrap them using
+ * {@link angular.bootstrap} instead. AngularJS applications cannot be nested within each other.
*
* You can specify an **AngularJS module** to be used as the root module for the application. This
* module will be loaded into the {@link auto.$injector} when the application is bootstrapped. It
@@ -14999,25 +14865,16 @@ function angularInit(element, bootstrap) {
* @description
* Use this function to manually start up angular application.
*
- * For more information, see the {@link guide/bootstrap Bootstrap guide}.
+ * See: {@link guide/bootstrap Bootstrap}
+ *
+ * Note that Protractor based end-to-end tests cannot use this function to bootstrap manually.
+ * They must use {@link ng.directive:ngApp ngApp}.
*
* Angular will detect if it has been loaded into the browser more than once and only allow the
* first loaded script to be bootstrapped and will report a warning to the browser console for
* each of the subsequent scripts. This prevents strange results in applications, where otherwise
* multiple instances of Angular try to work on the DOM.
*
- * <div class="alert alert-warning">
- * **Note:** Protractor based end-to-end tests cannot use this function to bootstrap manually.
- * They must use {@link ng.directive:ngApp ngApp}.
- * </div>
- *
- * <div class="alert alert-warning">
- * **Note:** Do not bootstrap the app on an element with a directive that uses {@link ng.$compile#transclusion transclusion},
- * such as {@link ng.ngIf `ngIf`}, {@link ng.ngInclude `ngInclude`} and {@link ngRoute.ngView `ngView`}.
- * Doing this misplaces the app {@link ng.$rootElement `$rootElement`} and the app's {@link auto.$injector injector},
- * causing animations to stop working and making the injector inaccessible from outside the app.
- * </div>
- *
* ```html
* <!doctype html>
* <html>
@@ -15160,6 +15017,7 @@ function snake_case(name, separator) {
}
var bindJQueryFired = false;
+var skipDestroyOnNextJQueryCleanData;
function bindJQuery() {
var originalCleanData;
@@ -15169,9 +15027,10 @@ function bindJQuery() {
// bind to jQuery if present;
var jqName = jq();
- jQuery = isUndefined(jqName) ? window.jQuery : // use jQuery (if present)
- !jqName ? undefined : // use jqLite
- window[jqName]; // use jQuery specified by `ngJq`
+ 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`.
+ }
// Use jQuery if it exists with proper functionality, otherwise default to us.
// Angular 1.2+ requires jQuery 1.7+ for on()/off() support.
@@ -15193,11 +15052,15 @@ function bindJQuery() {
originalCleanData = jQuery.cleanData;
jQuery.cleanData = function(elems) {
var events;
- for (var i = 0, elem; (elem = elems[i]) != null; i++) {
- events = jQuery._data(elem, "events");
- if (events && events.$destroy) {
- jQuery(elem).triggerHandler('$destroy');
+ if (!skipDestroyOnNextJQueryCleanData) {
+ for (var i = 0, elem; (elem = elems[i]) != null; i++) {
+ events = jQuery._data(elem, "events");
+ if (events && events.$destroy) {
+ jQuery(elem).triggerHandler('$destroy');
+ }
}
+ } else {
+ skipDestroyOnNextJQueryCleanData = false;
}
originalCleanData(elems);
};
@@ -15272,24 +15135,22 @@ 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 {Array} the inputted object or a jqLite collection containing the nodes
+ * @returns {jqLite} jqLite collection containing the nodes
*/
function getBlockNodes(nodes) {
- // TODO(perf): update `nodes` instead of creating a new object?
+ // TODO(perf): just check if all items in `nodes` are siblings and if they are return the original
+ // collection, otherwise update the original collection.
var node = nodes[0];
var endNode = nodes[nodes.length - 1];
- var blockNodes;
+ var blockNodes = [node];
- 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);
- }
- }
+ do {
+ node = node.nextSibling;
+ if (!node) break;
+ blockNodes.push(node);
+ } while (node !== endNode);
- return blockNodes || nodes;
+ return jqLite(blockNodes);
}
@@ -15353,8 +15214,8 @@ function setupModuleLoader(window) {
* All modules (angular core or 3rd party) that should be available to an application must be
* registered using this mechanism.
*
- * Passing one argument retrieves an existing {@link angular.Module},
- * whereas passing more than one argument creates a new {@link angular.Module}
+ * 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.
*
*
* # Module
@@ -15391,7 +15252,7 @@ function setupModuleLoader(window) {
* unspecified then the module is being retrieved for further configuration.
* @param {Function=} configFn Optional configuration function for the module. Same as
* {@link angular.Module#config Module#config()}.
- * @returns {angular.Module} new module with the {@link angular.Module} api.
+ * @returns {module} new module with the {@link angular.Module} api.
*/
return function module(name, requires, configFn) {
var assertNotHasOwnProperty = function(name, context) {
@@ -15503,7 +15364,7 @@ function setupModuleLoader(window) {
* @param {string} name constant name
* @param {*} object Constant value.
* @description
- * Because the constants are fixed, they get applied before other provide methods.
+ * Because the constant are fixed, they get applied before other provide methods.
* See {@link auto.$provide#constant $provide.constant()}.
*/
constant: invokeLater('$provide', 'constant', 'unshift'),
@@ -15599,19 +15460,6 @@ function setupModuleLoader(window) {
/**
* @ngdoc method
- * @name angular.Module#component
- * @module ng
- * @param {string} name Name of the component in camel-case (i.e. myComp which will match as my-comp)
- * @param {Object} options Component definition object (a simplified
- * {@link ng.$compile#directive-definition-object directive definition object})
- *
- * @description
- * See {@link ng.$compileProvider#component $compileProvider.component()}.
- */
- component: invokeLaterAndSetModuleName('$compileProvider', 'component'),
-
- /**
- * @ngdoc method
* @name angular.Module#config
* @module ng
* @param {Function} configFn Execute this function on module load. Useful for service
@@ -15686,7 +15534,7 @@ function serializeObject(obj) {
val = toJsonReplacer(key, val);
if (isObject(val)) {
- if (seen.indexOf(val) >= 0) return '...';
+ if (seen.indexOf(val) >= 0) return '<<already seen>>';
seen.push(val);
}
@@ -15697,7 +15545,7 @@ function serializeObject(obj) {
function toDebugString(obj) {
if (typeof obj === 'function') {
return obj.toString().replace(/ \{[\s\S]*$/, '');
- } else if (isUndefined(obj)) {
+ } else if (typeof obj === 'undefined') {
return 'undefined';
} else if (typeof obj !== 'string') {
return serializeObject(obj);
@@ -15708,6 +15556,7 @@ function toDebugString(obj) {
/* global angularModule: true,
version: true,
+ $LocaleProvider,
$CompileProvider,
htmlAnchorDirective,
@@ -15724,6 +15573,7 @@ function toDebugString(obj) {
ngClassDirective,
ngClassEvenDirective,
ngClassOddDirective,
+ ngCspDirective,
ngCloakDirective,
ngControllerDirective,
ngFormDirective,
@@ -15760,19 +15610,14 @@ function toDebugString(obj) {
$AnchorScrollProvider,
$AnimateProvider,
- $CoreAnimateCssProvider,
- $$CoreAnimateJsProvider,
$$CoreAnimateQueueProvider,
- $$AnimateRunnerFactoryProvider,
- $$AnimateAsyncRunFactoryProvider,
+ $$CoreAnimateRunnerProvider,
$BrowserProvider,
$CacheFactoryProvider,
$ControllerProvider,
- $DateProvider,
$DocumentProvider,
$ExceptionHandlerProvider,
$FilterProvider,
- $$ForceReflowProvider,
$InterpolateProvider,
$IntervalProvider,
$$HashMapProvider,
@@ -15780,7 +15625,6 @@ function toDebugString(obj) {
$HttpParamSerializerProvider,
$HttpParamSerializerJQLikeProvider,
$HttpBackendProvider,
- $xhrFactoryProvider,
$LocationProvider,
$LogProvider,
$ParseProvider,
@@ -15807,9 +15651,8 @@ 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".
@@ -15818,11 +15661,11 @@ function toDebugString(obj) {
* - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
*/
var version = {
- full: '1.5.3', // all of these placeholder strings will be replaced by grunt's
+ full: '1.4.3', // all of these placeholder strings will be replaced by grunt's
major: 1, // package task
- minor: 5,
+ minor: 4,
dot: 3,
- codeName: 'diplohaplontic-meiosis'
+ codeName: 'foam-acceleration'
};
@@ -15861,6 +15704,11 @@ function publishExternalAPI(angular) {
});
angularModule = setupModuleLoader(window);
+ try {
+ angularModule('ngLocale');
+ } catch (e) {
+ angularModule('ngLocale', []).provider('$locale', $LocaleProvider);
+ }
angularModule('ng', ['ngLocale'], ['$provide',
function ngModule($provide) {
@@ -15923,25 +15771,20 @@ function publishExternalAPI(angular) {
$provide.provider({
$anchorScroll: $AnchorScrollProvider,
$animate: $AnimateProvider,
- $animateCss: $CoreAnimateCssProvider,
- $$animateJs: $$CoreAnimateJsProvider,
$$animateQueue: $$CoreAnimateQueueProvider,
- $$AnimateRunner: $$AnimateRunnerFactoryProvider,
- $$animateAsyncRun: $$AnimateAsyncRunFactoryProvider,
+ $$AnimateRunner: $$CoreAnimateRunnerProvider,
$browser: $BrowserProvider,
$cacheFactory: $CacheFactoryProvider,
$controller: $ControllerProvider,
$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,
@@ -15998,22 +15841,16 @@ function publishExternalAPI(angular) {
*
* If jQuery is available, `angular.element` is an alias for the
* [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element`
- * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or **jqLite**.
- *
- * jqLite is a tiny, API-compatible subset of jQuery that allows
- * 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.
+ * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or "jqLite."
*
- * To use `jQuery`, simply ensure it is loaded before the `angular.js` file. You can also use the
- * {@link ngJq `ngJq`} directive to specify that jqlite should be used over jQuery, or to use a
- * specific version of jQuery if multiple versions exist on the page.
+ * <div class="alert alert-success">jqLite is a tiny, API-compatible subset of jQuery that allows
+ * 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.</div>
*
- * <div class="alert alert-info">**Note:** All element references in Angular are always wrapped with jQuery or
- * jqLite (such as the element argument in a directive's compile / link function). They are never raw DOM references.</div>
+ * To use `jQuery`, simply ensure it is loaded before the `angular.js` file.
*
- * <div class="alert alert-warning">**Note:** Keep in mind that this function will not find elements
- * by tag name / CSS selector. For lookups by tag name, try instead `angular.element(document).find(...)`
- * or `$document.find()`, or use the standard DOM APIs, e.g. `document.querySelectorAll()`.</div>
+ * <div class="alert">**Note:** all element references in Angular are always wrapped with jQuery or
+ * jqLite; they are never raw DOM references.</div>
*
* ## Angular's jqLite
* jqLite provides only the following jQuery methods:
@@ -16026,8 +15863,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', and also does not have automatic property prefixing.
+ * - [`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'.
* - [`data()`](http://api.jquery.com/data/)
* - [`detach()`](http://api.jquery.com/detach/)
* - [`empty()`](http://api.jquery.com/empty/)
@@ -16037,7 +15873,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, selectors or event object as parameter
+ * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces or selectors
* - [`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/)
@@ -16051,7 +15887,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 or event object as parameter
+ * - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces
* - [`val()`](http://api.jquery.com/val/)
* - [`wrap()`](http://api.jquery.com/wrap/)
*
@@ -16123,10 +15959,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, '<select multiple="multiple">', '</select>'],
@@ -16161,12 +15997,6 @@ function jqLiteHasData(node) {
return false;
}
-function jqLiteCleanData(nodes) {
- for (var i = 0, ii = nodes.length; i < ii; i++) {
- jqLiteRemoveData(nodes[i]);
- }
-}
-
function jqLiteBuildFragment(html, context) {
var tmp, tag, wrap,
fragment = context.createDocumentFragment(),
@@ -16219,24 +16049,6 @@ function jqLiteParseHTML(html, context) {
return [];
}
-function jqLiteWrapNode(node, wrapper) {
- var parent = node.parentNode;
-
- if (parent) {
- parent.replaceChild(wrapper, node);
- }
-
- wrapper.appendChild(node);
-}
-
-
-// IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259.
-var jqLiteContains = Node.prototype.contains || function(arg) {
- // jshint bitwise: false
- return !!(this.compareDocumentPosition(arg) & 16);
- // jshint bitwise: true
-};
-
/////////////////////////////////////////////
function JQLite(element) {
if (element instanceof JQLite) {
@@ -16295,23 +16107,17 @@ function jqLiteOff(element, type, fn, unsupported) {
delete events[type];
}
} else {
-
- var removeHandler = function(type) {
- var listenerFns = events[type];
+ forEach(type.split(' '), function(type) {
if (isDefined(fn)) {
+ var listenerFns = events[type];
arrayRemove(listenerFns || [], fn);
+ if (listenerFns && listenerFns.length > 0) {
+ return;
+ }
}
- if (!(isDefined(fn) && listenerFns && listenerFns.length > 0)) {
- removeEventListenerFn(element, type, handle);
- delete events[type];
- }
- };
- forEach(type.split(' '), function(type) {
- removeHandler(type);
- if (MOUSE_EVENT_MAP[type]) {
- removeHandler(MOUSE_EVENT_MAP[type]);
- }
+ removeEventListenerFn(element, type, handle);
+ delete events[type];
});
}
}
@@ -16452,7 +16258,7 @@ function jqLiteInheritedData(element, name, value) {
while (element) {
for (var i = 0, ii = names.length; i < ii; i++) {
- if (isDefined(value = jqLite.data(element, names[i]))) return value;
+ if ((value = jqLite.data(element, names[i])) !== undefined) return value;
}
// If dealing with a document fragment node with a host element, and no parent, use the host
@@ -16479,7 +16285,7 @@ function jqLiteRemove(element, keepData) {
function jqLiteDocumentLoaded(action, win) {
win = win || window;
if (win.document.readyState === 'complete') {
- // Force the action to be run async for consistent behavior
+ // Force the action to be run async for consistent behaviour
// from the action's point of view
// i.e. it will definitely not be in a $apply
win.setTimeout(action);
@@ -16558,15 +16364,15 @@ function getBooleanAttrName(element, name) {
return booleanAttr && BOOLEAN_ELEMENTS[nodeName_(element)] && booleanAttr;
}
-function getAliasedAttrName(name) {
- return ALIASED_ATTR[name];
+function getAliasedAttrName(element, name) {
+ var nodeName = element.nodeName;
+ return (nodeName === 'INPUT' || nodeName === 'TEXTAREA') && ALIASED_ATTR[name];
}
forEach({
data: jqLiteData,
removeData: jqLiteRemoveData,
- hasData: jqLiteHasData,
- cleanData: jqLiteCleanData
+ hasData: jqLiteHasData
}, function(fn, name) {
JQLite[name] = fn;
});
@@ -16697,7 +16503,7 @@ forEach({
// in a way that survives minification.
// jqLiteEmpty takes no arguments but is a setter.
if (fn !== jqLiteEmpty &&
- (isUndefined((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2))) {
+ (((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2) === undefined)) {
if (isObject(arg1)) {
// we are a write, but the object properties are the key/values
@@ -16718,7 +16524,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 = (isUndefined(value)) ? Math.min(nodeCount, 1) : nodeCount;
+ var jj = (value === undefined) ? Math.min(nodeCount, 1) : nodeCount;
for (var j = 0; j < jj; j++) {
var nodeValue = fn(this[j], arg1, arg2);
value = value ? value + nodeValue : nodeValue;
@@ -16767,9 +16573,6 @@ function createEventHandler(element, events) {
return event.immediatePropagationStopped === true;
};
- // Some events have special handlers that wrap the real handler
- var handlerWrapper = eventFns.specialHandlerWrapper || defaultHandlerWrapper;
-
// Copy event handlers in case event handlers array is modified during execution.
if ((eventFnsLength > 1)) {
eventFns = shallowCopy(eventFns);
@@ -16777,7 +16580,7 @@ function createEventHandler(element, events) {
for (var i = 0; i < eventFnsLength; i++) {
if (!event.isImmediatePropagationStopped()) {
- handlerWrapper(element, event, eventFns[i]);
+ eventFns[i].call(element, event);
}
}
};
@@ -16788,22 +16591,6 @@ function createEventHandler(element, events) {
return eventHandler;
}
-function defaultHandlerWrapper(element, event, handler) {
- handler.call(element, event);
-}
-
-function specialMouseHandlerWrapper(target, event, handler) {
- // Refer to jQuery's implementation of mouseenter & mouseleave
- // Read about mouseenter and mouseleave:
- // http://www.quirksmode.org/js/events_mouse.html#link8
- var related = event.relatedTarget;
- // For mousenter/leave call the handler if related is outside the target.
- // NB: No relatedTarget if the mouse left/entered the browser window
- if (!related || (related !== target && !jqLiteContains.call(target, related))) {
- handler.call(target, event);
- }
-}
-
//////////////////////////////////////////
// Functions iterating traversal.
// These functions chain results into a single
@@ -16832,28 +16619,35 @@ forEach({
var types = type.indexOf(' ') >= 0 ? type.split(' ') : [type];
var i = types.length;
- var addHandler = function(type, specialHandlerWrapper, noEventListener) {
+ while (i--) {
+ type = types[i];
var eventFns = events[type];
if (!eventFns) {
- eventFns = events[type] = [];
- eventFns.specialHandlerWrapper = specialHandlerWrapper;
- if (type !== '$destroy' && !noEventListener) {
- addEventListenerFn(element, type, handle);
- }
- }
+ events[type] = [];
- eventFns.push(fn);
- };
+ if (type === 'mouseenter' || type === 'mouseleave') {
+ // Refer to jQuery's implementation of mouseenter & mouseleave
+ // Read about mouseenter and mouseleave:
+ // http://www.quirksmode.org/js/events_mouse.html#link8
- while (i--) {
- type = types[i];
- if (MOUSE_EVENT_MAP[type]) {
- addHandler(MOUSE_EVENT_MAP[type], specialMouseHandlerWrapper);
- addHandler(type, undefined, true);
- } else {
- addHandler(type);
+ jqLiteOn(element, MOUSE_EVENT_MAP[type], function(event) {
+ var target = this, related = event.relatedTarget;
+ // For mousenter/leave call the handler if related is outside the target.
+ // NB: No relatedTarget if the mouse left/entered the browser window
+ if (!related || (related !== target && !target.contains(related))) {
+ handle(event, type);
+ }
+ });
+
+ } else {
+ if (type !== '$destroy') {
+ addEventListenerFn(element, type, handle);
+ }
+ }
+ eventFns = events[type];
}
+ eventFns.push(fn);
}
},
@@ -16921,7 +16715,12 @@ forEach({
},
wrap: function(element, wrapNode) {
- jqLiteWrapNode(element, jqLite(wrapNode).eq(0).clone()[0]);
+ wrapNode = jqLite(wrapNode).eq(0).clone()[0];
+ var parent = element.parentNode;
+ if (parent) {
+ parent.replaceChild(wrapNode, element);
+ }
+ wrapNode.appendChild(element);
},
remove: jqLiteRemove,
@@ -17199,23 +16998,17 @@ var $$HashMapProvider = [function() {
* Implicit module which gets automatically added to each {@link auto.$injector $injector}.
*/
-var ARROW_ARG = /^([^\(]+?)=>/;
-var FN_ARGS = /^[^\(]*\(\s*([^\)]*)\)/m;
+var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
var FN_ARG_SPLIT = /,/;
var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
var $injectorMinErr = minErr('$injector');
-function extractArgs(fn) {
- var fnText = fn.toString().replace(STRIP_COMMENTS, ''),
- args = fnText.match(ARROW_ARG) || fnText.match(FN_ARGS);
- return args;
-}
-
function anonFn(fn) {
// For anonymous functions, showing at the very least the function signature can help in
// debugging.
- var args = extractArgs(fn);
+ var fnText = fn.toString().replace(STRIP_COMMENTS, ''),
+ args = fnText.match(FN_ARGS);
if (args) {
return 'function(' + (args[1] || '').replace(/[\s\r\n]+/, ' ') + ')';
}
@@ -17224,6 +17017,7 @@ function anonFn(fn) {
function annotate(fn, strictDi, name) {
var $inject,
+ fnText,
argDecl,
last;
@@ -17238,7 +17032,8 @@ function annotate(fn, strictDi, name) {
throw $injectorMinErr('strictdi',
'{0} is not using explicit annotation and cannot be invoked in strict mode', name);
}
- argDecl = extractArgs(fn);
+ fnText = fn.toString().replace(STRIP_COMMENTS, '');
+ argDecl = fnText.match(FN_ARGS);
forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) {
arg.replace(FN_ARG, function(all, underscore, name) {
$inject.push(name);
@@ -17628,20 +17423,8 @@ function annotate(fn, strictDi, name) {
*
* Register a **service constructor**, which will be invoked with `new` to create the service
* instance.
- * This is short for registering a service where its provider's `$get` property is a factory
- * function that returns an instance instantiated by the injector from the service constructor
- * function.
- *
- * Internally it looks a bit like this:
- *
- * ```
- * {
- * $get: function() {
- * return $injector.instantiate(constructor);
- * }
- * }
- * ```
- *
+ * This is short for registering a service where its provider's `$get` property is the service
+ * constructor function that will be used to instantiate the service instance.
*
* You should use {@link auto.$provide#service $provide.service(class)} if you define your service
* as a type/class.
@@ -17681,13 +17464,14 @@ function annotate(fn, strictDi, name) {
* @description
*
* Register a **value service** with the {@link auto.$injector $injector}, such as a string, a
- * number, an array, an object or a function. This is short for registering a service where its
+ * number, an array, an object or a function. This is short for registering a service where its
* provider's `$get` property is a factory function that takes no arguments and returns the **value
- * service**. That also means it is not possible to inject other services into a value service.
+ * service**.
*
* Value services are similar to constant services, except that they cannot be injected into a
* module configuration function (see {@link angular.Module#config}) but they can be overridden by
- * an Angular {@link auto.$provide#decorator decorator}.
+ * an Angular
+ * {@link auto.$provide#decorator decorator}.
*
* @param {string} name The name of the instance.
* @param {*} value The value.
@@ -17712,11 +17496,8 @@ function annotate(fn, strictDi, name) {
* @name $provide#constant
* @description
*
- * Register a **constant service** with the {@link auto.$injector $injector}, such as a string,
- * a number, an array, an object or a function. Like the {@link auto.$provide#value value}, it is not
- * possible to inject other services into a constant.
- *
- * But unlike {@link auto.$provide#value value}, a constant can be
+ * Register a **constant service**, such as a string, a number, an array, an object or a function,
+ * with the {@link auto.$injector $injector}. Unlike {@link auto.$provide#value value} it can be
* injected into a module configuration function (see {@link angular.Module#config}) and it cannot
* be overridden by an Angular {@link auto.$provide#decorator decorator}.
*
@@ -17744,7 +17525,7 @@ function annotate(fn, strictDi, name) {
* @description
*
* Register a **service decorator** with the {@link auto.$injector $injector}. A service decorator
- * intercepts the creation of a service, allowing it to override or modify the behavior of the
+ * intercepts the creation of a service, allowing it to override or modify the behaviour of the
* service. The object returned by the decorator may be the original service, or a new service
* object which replaces or wraps and delegates to the original service.
*
@@ -17793,19 +17574,14 @@ function createInjector(modulesToLoad, strictDi) {
throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
})),
instanceCache = {},
- protoInstanceInjector =
+ instanceInjector = (instanceCache.$injector =
createInternalInjector(instanceCache, function(serviceName, caller) {
var provider = providerInjector.get(serviceName + providerSuffix, caller);
- return instanceInjector.invoke(
- provider.$get, provider, undefined, serviceName);
- }),
- instanceInjector = protoInstanceInjector;
+ return instanceInjector.invoke(provider.$get, provider, undefined, serviceName);
+ }));
+
- providerCache['$injector' + providerSuffix] = { $get: valueFn(protoInstanceInjector) };
- var runBlocks = loadModules(modulesToLoad);
- instanceInjector = protoInstanceInjector.get('$injector');
- instanceInjector.strictDi = strictDi;
- forEach(runBlocks, function(fn) { if (fn) instanceInjector.invoke(fn); });
+ forEach(loadModules(modulesToLoad), function(fn) { if (fn) instanceInjector.invoke(fn); });
return instanceInjector;
@@ -17878,7 +17654,6 @@ 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;
@@ -17955,66 +17730,47 @@ function createInjector(modulesToLoad, strictDi) {
}
}
+ function invoke(fn, self, locals, serviceName) {
+ if (typeof locals === 'string') {
+ serviceName = locals;
+ locals = null;
+ }
- function injectionArgs(fn, locals, serviceName) {
var args = [],
- $inject = createInjector.$$annotate(fn, strictDi, serviceName);
+ $inject = createInjector.$$annotate(fn, strictDi, serviceName),
+ length, i,
+ key;
- for (var i = 0, length = $inject.length; i < length; i++) {
- var key = $inject[i];
+ for (i = 0, length = $inject.length; i < length; i++) {
+ key = $inject[i];
if (typeof key !== 'string') {
throw $injectorMinErr('itkn',
'Incorrect injection token! Expected service name as string, got {0}', key);
}
- args.push(locals && locals.hasOwnProperty(key) ? locals[key] :
- getService(key, serviceName));
- }
- return args;
- }
-
- function isClass(func) {
- // IE 9-11 do not support classes and IE9 leaks with the code below.
- if (msie <= 11) {
- return false;
- }
- // Workaround for MS Edge.
- // Check https://connect.microsoft.com/IE/Feedback/Details/2211653
- return typeof func === 'function'
- && /^(?:class\s|constructor\()/.test(Function.prototype.toString.call(func));
- }
-
- function invoke(fn, self, locals, serviceName) {
- if (typeof locals === 'string') {
- serviceName = locals;
- locals = null;
+ args.push(
+ locals && locals.hasOwnProperty(key)
+ ? locals[key]
+ : getService(key, serviceName)
+ );
}
-
- var args = injectionArgs(fn, locals, serviceName);
if (isArray(fn)) {
- fn = fn[fn.length - 1];
+ fn = fn[length];
}
- if (!isClass(fn)) {
- // http://jsperf.com/angularjs-invoke-apply-vs-switch
- // #5388
- return fn.apply(self, args);
- } else {
- args.unshift(null);
- return new (Function.prototype.bind.apply(fn, args))();
- }
+ // http://jsperf.com/angularjs-invoke-apply-vs-switch
+ // #5388
+ return fn.apply(self, args);
}
-
function instantiate(Type, locals, serviceName) {
// Check if Type is annotated and use just the given function at n-1 as parameter
// e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
- var ctor = (isArray(Type) ? Type[Type.length - 1] : Type);
- var args = injectionArgs(Type, locals, serviceName);
- // Empty object at position 0 is ignored for invocation with `new`, but required.
- args.unshift(null);
- return new (Function.prototype.bind.apply(ctor, args))();
- }
+ // Object creation: http://jsperf.com/create-constructor/2
+ var instance = Object.create((isArray(Type) ? Type[Type.length - 1] : Type).prototype || null);
+ var returnedValue = invoke(Type, instance, locals, serviceName);
+ return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance;
+ }
return {
invoke: invoke,
@@ -18071,7 +17827,7 @@ function $AnchorScrollProvider() {
* 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://www.w3.org/html/wg/drafts/html/master/browsers.html#the-indicated-part-of-the-document).
+ * [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
@@ -18354,8 +18110,27 @@ function prepareAnimateOptions(options) {
: {};
}
-var $$CoreAnimateJsProvider = function() {
- this.$get = noop;
+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
@@ -18383,75 +18158,65 @@ var $$CoreAnimateQueueProvider = function() {
addRemoveClassesPostDigest(element, options.addClass, options.removeClass);
}
- var runner = new $$AnimateRunner(); // jshint ignore:line
-
- // since there are no animations to run the runner needs to be
- // notified that the animation call is complete.
- runner.complete();
- return runner;
+ 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);
+ }
- function updateData(data, classes, value) {
- var changed = false;
- if (classes) {
- classes = isString(classes) ? classes.split(' ') :
- isArray(classes) ? classes : [];
- forEach(classes, function(className) {
+ if (add) {
+ forEach(add.split(' '), function(className) {
if (className) {
- changed = true;
- data[className] = value;
+ data[className] = true;
}
});
}
- return changed;
- }
-
- 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;
- }
-
- function addRemoveClassesPostDigest(element, add, remove) {
- var data = postDigestQueue.get(element) || {};
+ if (remove) {
+ forEach(remove.split(' '), function(className) {
+ if (className) {
+ data[className] = false;
+ }
+ });
+ }
- var classesAdded = updateData(data, add, true);
- var classesRemoved = updateData(data, remove, false);
+ if (postDigestElements.length > 1) return;
- if (classesAdded || classesRemoved) {
+ $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;
+ }
+ }
+ });
- postDigestQueue.put(element, data);
- postDigestElements.push(element);
+ forEach(element, function(elm) {
+ toAdd && jqLiteAddClass(elm, toAdd);
+ toRemove && jqLiteRemoveClass(elm, toRemove);
+ });
+ postDigestQueue.remove(element);
+ }
+ });
- if (postDigestElements.length === 1) {
- $rootScope.$$postDigest(handleCSSClassChanges);
- }
- }
+ postDigestElements.length = 0;
+ });
}
}];
};
@@ -18572,7 +18337,7 @@ var $AnimateProvider = ['$provide', function($provide) {
* 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 any animations. This is because the `ngAnimate` module isn't
+ * 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`,
@@ -18630,8 +18395,8 @@ var $AnimateProvider = ['$provide', function($provide) {
* // 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 `callback` that is set
- * // to listen for `enter` on the given `container` as well as its children
+ * // 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);
* ```
*
@@ -18853,30 +18618,17 @@ var $AnimateProvider = ['$provide', function($provide) {
* @kind function
*
* @description Performs an inline animation on the element which applies the provided to and from CSS styles to the element.
- * If any detected CSS transition, keyframe or JavaScript matches the provided className value, then the animation will take
- * on the provided styles. For example, if a transition animation is set for the given classNamem, then the provided `from` and
- * `to` styles will be applied alongside the given transition. If the CSS style provided in `from` does not have a corresponding
- * style in `to`, the style in `from` is applied immediately, and no animation is run.
- * If a JavaScript animation is detected then the provided styles will be given in as function parameters into the `animate`
- * method (or as part of the `options` parameter):
- *
- * ```js
- * ngModule.animation('.my-inline-animation', function() {
- * return {
- * animate : function(element, from, to, done, options) {
- * //animation
- * done();
- * }
- * }
- * });
- * ```
+ * If any detected CSS transition, keyframe or JavaScript matches the provided className value then the animation will take
+ * on the provided styles. For example, if a transition animation is set for the given className then the provided from and
+ * to styles will be applied alongside the given transition. If a JavaScript animation is detected then the provided styles
+ * will be given in as function paramters into the `animate` method (or as apart of the `options` parameter).
*
* @param {DOMElement} element the element which the CSS styles will be applied to
* @param {object} from the from (starting) CSS styles that will be applied to the element and across the animation.
* @param {object} to the to (destination) CSS styles that will be applied to the element and across the animation.
* @param {string=} className an optional CSS class that will be applied to the element for the duration of the animation. If
* this value is left as empty then a CSS class of `ng-inline-animate` will be applied to the element.
- * (Note that if no animation is detected then this value will not be applied to the element.)
+ * (Note that if no animation is detected then this value will not be appplied to the element.)
* @param {object=} options an optional collection of options/styles that will be applied to the element
*
* @return {Promise} the animation callback promise
@@ -18894,261 +18646,15 @@ var $AnimateProvider = ['$provide', function($provide) {
}];
}];
-var $$AnimateAsyncRunFactoryProvider = function() {
- this.$get = ['$$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;
- waitForTick(function() {
- passed = true;
- });
- return function(callback) {
- passed ? callback() : waitForTick(callback);
- };
- };
- }];
-};
-
-var $$AnimateRunnerFactoryProvider = function() {
- this.$get = ['$q', '$sniffer', '$$animateAsyncRun', '$document', '$timeout',
- function($q, $sniffer, $$animateAsyncRun, $document, $timeout) {
-
- var INITIAL_STATE = 0;
- var DONE_PENDING_STATE = 1;
- var DONE_COMPLETE_STATE = 2;
-
- AnimateRunner.chain = function(chain, callback) {
- var index = 0;
-
- next();
- function next() {
- if (index === chain.length) {
- callback(true);
- return;
- }
-
- chain[index](function(response) {
- if (response === false) {
- callback(false);
- return;
- }
- index++;
- next();
- });
- }
- };
-
- AnimateRunner.all = function(runners, callback) {
- var count = 0;
- var status = true;
- forEach(runners, function(runner) {
- runner.done(onProgress);
- });
-
- function onProgress(response) {
- status = status && response;
- if (++count === runners.length) {
- callback(status);
- }
- }
- };
-
- function AnimateRunner(host) {
- this.setHost(host);
-
- var rafTick = $$animateAsyncRun();
- var timeoutTick = function(fn) {
- $timeout(fn, 0, false);
- };
-
- this._doneCallbacks = [];
- this._tick = function(fn) {
- var doc = $document[0];
-
- // the document may not be ready or attached
- // to the module for some internal tests
- if (doc && doc.hidden) {
- timeoutTick(fn);
- } else {
- rafTick(fn);
- }
- };
- this._state = 0;
- }
-
- AnimateRunner.prototype = {
- setHost: function(host) {
- this.host = host || {};
- },
-
- done: function(fn) {
- if (this._state === DONE_COMPLETE_STATE) {
- fn();
- } else {
- this._doneCallbacks.push(fn);
- }
- },
-
- progress: noop,
-
- getPromise: function() {
- if (!this.promise) {
- var self = this;
- this.promise = $q(function(resolve, reject) {
- self.done(function(status) {
- status === false ? reject() : resolve();
- });
- });
- }
- return this.promise;
- },
-
- then: function(resolveHandler, rejectHandler) {
- return this.getPromise().then(resolveHandler, rejectHandler);
- },
-
- 'catch': function(handler) {
- return this.getPromise()['catch'](handler);
- },
-
- 'finally': function(handler) {
- return this.getPromise()['finally'](handler);
- },
-
- pause: function() {
- if (this.host.pause) {
- this.host.pause();
- }
- },
-
- resume: function() {
- if (this.host.resume) {
- this.host.resume();
- }
- },
-
- end: function() {
- if (this.host.end) {
- this.host.end();
- }
- this._resolve(true);
- },
-
- cancel: function() {
- if (this.host.cancel) {
- this.host.cancel();
- }
- this._resolve(false);
- },
-
- complete: function(response) {
- var self = this;
- if (self._state === INITIAL_STATE) {
- self._state = DONE_PENDING_STATE;
- self._tick(function() {
- self._resolve(response);
- });
- }
- },
-
- _resolve: function(response) {
- if (this._state !== DONE_COMPLETE_STATE) {
- forEach(this._doneCallbacks, function(fn) {
- fn(response);
- });
- this._doneCallbacks.length = 0;
- this._state = DONE_COMPLETE_STATE;
- }
- }
- };
-
- return AnimateRunner;
- }];
-};
-
-/**
- * @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', '$$AnimateRunner', function($$rAF, $q, $$AnimateRunner) {
-
- return function(element, initialOptions) {
- // all of the animation functions should create
- // a copy of the options data, however, if a
- // parent service has already created a copy then
- // we should stick to using that
- var options = initialOptions || {};
- if (!options.$$prepared) {
- options = copy(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;
- }
-
- /* jshint newcap: false */
- var closed, runner = new $$AnimateRunner();
- return {
- start: run,
- end: run
+function $$AsyncCallbackProvider() {
+ this.$get = ['$$rAF', '$timeout', function($$rAF, $timeout) {
+ return $$rAF.supported
+ ? function(fn) { return $$rAF(fn); }
+ : function(fn) {
+ return $timeout(fn, 0, false);
};
-
- function run() {
- $$rAF(function() {
- applyAnimationContents();
- if (!closed) {
- runner.complete();
- }
- closed = true;
- });
- return runner;
- }
-
- function applyAnimationContents() {
- 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 */
@@ -19175,6 +18681,7 @@ var $CoreAnimateCssProvider = function() {
*/
function Browser(window, document, $log, $sniffer) {
var self = this,
+ rawDocument = document[0],
location = window.location,
history = window.history,
setTimeout = window.setTimeout,
@@ -19237,14 +18744,7 @@ function Browser(window, document, $log, $sniffer) {
var cachedState, lastHistoryState,
lastBrowserUrl = location.href,
baseElement = document.find('base'),
- pendingLocation = null,
- getCurrentState = !$sniffer.history ? noop : function getCurrentState() {
- try {
- return history.state;
- } catch (e) {
- // MSIE can reportedly throw when there is no state (UNCONFIRMED).
- }
- };
+ reloadLocation = null;
cacheState();
lastHistoryState = cachedState;
@@ -19304,8 +18804,8 @@ function Browser(window, document, $log, $sniffer) {
// Do the assignment again so that those two variables are referentially identical.
lastHistoryState = cachedState;
} else {
- if (!sameBase || pendingLocation) {
- pendingLocation = url;
+ if (!sameBase || reloadLocation) {
+ reloadLocation = url;
}
if (replace) {
location.replace(url);
@@ -19314,18 +18814,14 @@ function Browser(window, document, $log, $sniffer) {
} else {
location.hash = getHash(url);
}
- if (location.href !== url) {
- pendingLocation = url;
- }
}
return self;
// getter
} else {
- // - 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).
+ // - reloadLocation is needed as browsers don't allow to read out
+ // the new location.href if a reload happened.
// - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
- return pendingLocation || location.href.replace(/%27/g,"'");
+ return reloadLocation || location.href.replace(/%27/g,"'");
}
};
@@ -19347,11 +18843,18 @@ function Browser(window, document, $log, $sniffer) {
urlChangeInit = false;
function cacheStateAndFireUrlChange() {
- pendingLocation = null;
cacheState();
fireUrlChange();
}
+ function getCurrentState() {
+ try {
+ return history.state;
+ } catch (e) {
+ // MSIE can reportedly throw when there is no state (UNCONFIRMED).
+ }
+ }
+
// This variable should be used *only* inside the cacheState function.
var lastCachedState = null;
function cacheState() {
@@ -19575,10 +19078,10 @@ function $BrowserProvider() {
$scope.keys = [];
$scope.cache = $cacheFactory('cacheId');
$scope.put = function(key, value) {
- if (angular.isUndefined($scope.cache.get(key))) {
+ if ($scope.cache.get(key) === undefined) {
$scope.keys.push(key);
}
- $scope.cache.put(key, angular.isUndefined(value) ? null : value);
+ $scope.cache.put(key, value === undefined ? null : value);
};
}]);
</file>
@@ -19601,9 +19104,9 @@ function $CacheFactoryProvider() {
var size = 0,
stats = extend({}, options, {id: cacheId}),
- data = createMap(),
+ data = {},
capacity = (options && options.capacity) || Number.MAX_VALUE,
- lruHash = createMap(),
+ lruHash = {},
freshEnd = null,
staleEnd = null;
@@ -19731,8 +19234,6 @@ function $CacheFactoryProvider() {
delete lruHash[key];
}
- if (!(key in data)) return;
-
delete data[key];
size--;
},
@@ -19747,9 +19248,9 @@ function $CacheFactoryProvider() {
* Clears the cache object of any entries.
*/
removeAll: function() {
- data = createMap();
+ data = {};
size = 0;
- lruHash = createMap();
+ lruHash = {};
freshEnd = staleEnd = null;
},
@@ -20038,7 +19539,7 @@ function $TemplateCacheProvider() {
* When this property is set to true, the HTML compiler will collect DOM nodes between
* nodes with the attributes `directive-name-start` and `directive-name-end`, and group them
* together as the directive elements. It is recommended that this feature be used on directives
- * which are not strictly behavioral (such as {@link ngClick}), and which
+ * which are not strictly behavioural (such as {@link ngClick}), and which
* do not manipulate or replace child nodes (such as {@link ngInclude}).
*
* #### `priority`
@@ -20056,129 +19557,59 @@ function $TemplateCacheProvider() {
* and other directives used in the directive's template will also be excluded from execution.
*
* #### `scope`
- * The scope property can be `true`, an object or a falsy value:
- *
- * * **falsy:** No scope will be created for the directive. The directive will use its parent's scope.
- *
- * * **`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.
+ * **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.
*
- * * **`{...}` (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.
+ * **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.
*
- * 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:
+ * 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:
*
* * `@` 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
- * attribute name is assumed to be the same as the local name. Given `<my-component
- * my-attr="hello {{name}}">` and the isolate scope definition `scope: { localName:'@myAttr' }`,
- * the directive's scope property `localName` will reflect the interpolated value of `hello
- * {{name}}`. As the `name` attribute changes so will the `localName` property on the directive's
- * scope. The `name` is read from the parent scope (not the directive's scope).
- *
- * * `=` or `=attr` - set up a bidirectional binding between a local scope property and an expression
- * passed via the attribute `attr`. The expression is evaluated in the context of the parent scope.
- * If no `attr` name is specified then the attribute name is assumed to be the same as the local
- * name. Given `<my-component my-attr="parentModel">` and the isolate scope definition `scope: {
- * localModel: '=myAttr' }`, the property `localModel` on the directive's scope will reflect the
- * value of `parentModel` on the parent scope. Changes to `parentModel` will be reflected in
- * `localModel` and vice versa. Optional attributes should be marked as such with a question mark:
- * `=?` or `=?attr`. If the binding expression is non-assignable, or if the attribute isn't
- * optional and doesn't exist, an exception ({@link error/$compile/nonassign `$compile:nonassign`})
- * will be thrown upon discovering changes to the local value, since it will be impossible to sync
- * them back to the parent scope. By default, the {@link ng.$rootScope.Scope#$watch `$watch`}
- * method is used for tracking changes, and the equality check is based on object identity.
- * However, if an object literal or an array literal is passed as the binding expression, the
- * equality check is done by value (using the {@link angular.equals} function). It's also possible
- * to watch the evaluated value shallowly with {@link ng.$rootScope.Scope#$watchCollection
- * `$watchCollection`}: use `=*` or `=*attr` (`=*?` or `=*?attr` if the attribute is optional).
- *
- * * `<` or `<attr` - set up a one-way (one-directional) binding between a local scope property and an
- * expression passed via the attribute `attr`. The expression is evaluated in the context of the
- * parent scope. If no `attr` name is specified then the attribute name is assumed to be the same as the
- * local name. You can also make the binding optional by adding `?`: `<?` or `<?attr`.
- *
- * For example, given `<my-component my-attr="parentModel">` and directive definition of
- * `scope: { localModel:'<myAttr' }`, then the isolated scope property `localModel` will reflect the
+ * always a string since DOM attributes are strings. If no `attr` name is specified then the
+ * attribute name is assumed to be the same as the local name.
+ * Given `<widget my-attr="hello {{name}}">` and widget definition
+ * of `scope: { localName:'@myAttr' }`, then widget scope property `localName` will reflect
+ * the interpolated value of `hello {{name}}`. As the `name` attribute changes so will the
+ * `localName` property on the widget scope. The `name` is read from the parent scope (not
+ * component scope).
+ *
+ * * `=` or `=attr` - set up bi-directional binding between a local scope property and the
+ * parent scope property of name defined via the value of the `attr` attribute. If no `attr`
+ * name is specified then the attribute name is assumed to be the same as the local name.
+ * Given `<widget my-attr="parentModel">` and widget definition of
+ * `scope: { localModel:'=myAttr' }`, then widget scope property `localModel` will reflect the
* value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected
- * in `localModel`, but changes in `localModel` will not reflect in `parentModel`. There are however
- * two caveats:
- * 1. one-way binding does not copy the value from the parent to the isolate scope, it simply
- * sets the same value. That means if your bound value is an object, changes to its properties
- * in the isolated scope will be reflected in the parent scope (because both reference the same object).
- * 2. one-way binding watches changes to the **identity** of the parent value. That means the
- * {@link ng.$rootScope.Scope#$watch `$watch`} on the parent value only fires if the reference
- * to the value has changed. In most cases, this should not be of concern, but can be important
- * to know if you one-way bind to an object, and then replace that object in the isolated scope.
- * If you now change a property of the object in your parent scope, the change will not be
- * propagated to the isolated scope, because the identity of the object on the parent scope
- * has not changed. Instead you must assign a new object.
- *
- * One-way binding is useful if you do not plan to propagate changes to your isolated scope bindings
- * back to the parent. However, it does not make this completely impossible.
- *
- * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope. If
- * no `attr` name is specified then the attribute name is assumed to be the same as the local name.
- * Given `<my-component my-attr="count = count + value">` and the isolate scope definition `scope: {
- * localFn:'&myAttr' }`, the isolate scope property `localFn` will point to a function wrapper for
- * the `count = count + value` expression. Often it's desirable to pass data from the isolated scope
- * via an expression to the parent scope. This can be done by passing a map of local variable names
- * and values into the expression wrapper fn. 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.
+ * in `localModel` and any changes in `localModel` will reflect in `parentModel`. If the parent
+ * scope property doesn't exist, it will throw a NON_ASSIGNABLE_MODEL_EXPRESSION exception. You
+ * can avoid this behavior using `=?` or `=?attr` in order to flag the property as optional. If
+ * you want to shallow watch for changes (i.e. $watchCollection instead of $watch) you can use
+ * `=*` or `=*attr` (`=*?` or `=*?attr` if the property is optional).
+ *
+ * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope.
+ * If no `attr` name is specified then the attribute name is assumed to be the same as the
+ * local name. Given `<widget my-attr="count = count + value">` and widget definition of
+ * `scope: { localFn:'&myAttr' }`, then isolate scope property `localFn` will point to
+ * a function wrapper for the `count = count + value` expression. Often it's desirable to
+ * pass data from the isolated scope via an expression to the parent scope, this can be
+ * done by passing a map of local variable names and values into the expression wrapper fn.
+ * For example, if the expression is `increment(amount)` then we can specify the amount value
+ * by calling the `localFn` as `localFn({amount: 22})`.
*
*
* #### `bindToController`
- * This property is used to bind scope properties directly to the controller. It can be either
- * `true` or an object hash with the same format as the `scope` property. Additionally, a controller
- * alias must be set, either by using `controllerAs: 'myAlias'` or by specifying the alias in the controller
- * definition: `controller: 'myCtrl as myAlias'`.
- *
- * When an isolate scope is used for a directive (see above), `bindToController: true` will
- * allow a component to have its properties bound to the controller, rather than to scope.
- *
- * After the controller is instantiated, the initial values of the isolate scope bindings will be bound to the controller
- * properties. You can access these bindings once they have been initialized by providing a controller method called
- * `$onInit`, which is called after all the controllers on an element have been constructed and had their bindings
- * initialized.
- *
- * <div class="alert alert-warning">
- * **Deprecation warning:** although bindings for non-ES6 class controllers are currently
- * bound to `this` before the controller constructor is called, this use is now deprecated. Please place initialization
- * code that relies upon bindings inside a `$onInit` method on the controller, instead.
- * </div>
- *
- * It is also possible to set `bindToController` to an object hash with the same format as the `scope` property.
- * This will set up the scope bindings to the controller directly. Note that `scope` can still be used
- * to define which kind of scope is created. By default, no scope is created. Use `scope: {}` to create an isolate
- * scope (useful for component directives).
- *
- * If both `bindToController` and `scope` are defined and have object hashes, `bindToController` overrides `scope`.
- *
+ * When an isolate scope is used for a component (see above), and `controllerAs` is used, `bindToController: true` will
+ * allow a component to have its properties bound to the controller, rather than to scope. When the controller
+ * is instantiated, the initial values of the isolate scope bindings are already available.
*
* #### `controller`
* Controller constructor function. The controller is instantiated before the
- * pre-linking phase and can be accessed by other directives (see
+ * pre-linking phase and it is shared with 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:
*
@@ -20186,10 +19617,10 @@ function $TemplateCacheProvider() {
* * `$element` - Current element
* * `$attrs` - Current attributes object for the element
* * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope:
- * `function([scope], cloneLinkingFn, futureParentElement, slotName)`:
- * * `scope`: (optional) override the scope.
- * * `cloneLinkingFn`: (optional) argument to create clones of the original transcluded content.
- * * `futureParentElement` (optional):
+ * `function([scope], cloneLinkingFn, futureParentElement)`.
+ * * `scope`: optional argument to override the scope.
+ * * `cloneLinkingFn`: optional argument to create clones of the original transcluded content.
+ * * `futureParentElement`:
* * defines the parent to which the `cloneLinkingFn` will add the cloned elements.
* * default: `$element.parent()` resp. `$element` for `transclude:'element'` resp. `transclude:true`.
* * only needed for transcludes that are allowed to contain non html elements (e.g. SVG elements)
@@ -20197,48 +19628,14 @@ function $TemplateCacheProvider() {
* as those elements need to created and cloned in a special way when they are defined outside their
* usual containers (e.g. like `<svg>`).
* * See also the `directive.templateNamespace` property.
- * * `slotName`: (optional) the name of the slot to transclude. If falsy (e.g. `null`, `undefined` or `''`)
- * then the default translusion is provided.
- * The `$transclude` function also has a method on it, `$transclude.isSlotFilled(slotName)`, which returns
- * `true` if the specified slot contains content (i.e. one or more DOM nodes).
- *
- * The controller can provide the following methods that act as life-cycle hooks:
- * * `$onInit()` - Called on each controller after all the controllers on an element have been constructed and
- * had their bindings initialized (and before the pre &amp; post linking functions for the directives on
- * this element). This is a good place to put initialization code for your controller.
- * * `$onChanges(changesObj)` - Called whenever one-way (`<`) or interpolation (`@`) bindings are updated. The
- * `changesObj` is a hash whose keys are the names of the bound properties that have changed, and the values are an
- * object of the form `{ currentValue: ..., previousValue: ... }`. Use this hook to trigger updates within a component
- * such as cloning the bound value to prevent accidental mutation of the outer value.
- * * `$onDestroy()` - Called on a controller when its containing scope is destroyed. Use this hook for releasing
- * external resources, watches and event handlers. Note that components have their `$onDestroy()` hooks called in
- * the same order as the `$scope.$broadcast` events are triggered, which is top down. This means that parent
- * components will have their `$onDestroy()` hook called before child components.
- * * `$postLink()` - Called after this controller's element and its children have been linked. Similar to the post-link
- * function this hook can be used to set up DOM event handlers and do direct DOM manipulation.
- * Note that child elements that contain `templateUrl` directives will not have been compiled and linked since
- * they are waiting for their template to load asynchronously and their own compilation and linking has been
- * suspended until that occurs.
*
*
* #### `require`
* Require another directive and inject its controller as the fourth argument to the linking function. The
- * `require` property can be a string, an array or an object:
- * * a **string** containing the name of the directive to pass to the linking function
- * * an **array** containing the names of directives to pass to the linking function. The argument passed to the
- * linking function will be an array of controllers in the same order as the names in the `require` property
- * * an **object** whose property values are the names of the directives to pass to the linking function. The argument
- * passed to the linking function will also be an object with matching keys, whose values will hold the corresponding
- * controllers.
- *
- * If the `require` property is an object and `bindToController` is truthy, then the required controllers are
- * bound to the controller using the keys of the `require` property. This binding occurs after all the controllers
- * have been constructed but before `$onInit` is called.
- * See the {@link $compileProvider#component} helper for an example of how this can be used.
- *
- * If no such required directive(s) can be found, or if the directive does not have a controller, then an error is
- * raised (unless no link function is specified and the required controllers are not being bound to the directive
- * controller, in which case error checking is skipped). The name can be prefixed with:
+ * `require` takes a string name (or array of strings) of the directive(s) to pass in. If an array is used, the
+ * injected argument will be an array in corresponding order. If no such directive can be
+ * found, or if the directive does not have a controller, then an error is raised (unless no link function
+ * is specified, in which case error checking is skipped). The name can be prefixed with:
*
* * (no prefix) - Locate the required controller on the current element. Throw an error if not found.
* * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.
@@ -20252,10 +19649,9 @@ 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. 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.
+ * 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.
*
*
* #### `restrict`
@@ -20331,6 +19727,14 @@ function $TemplateCacheProvider() {
* The contents are compiled and provided to the directive as a **transclusion function**. See the
* {@link $compile#transclusion Transclusion} section below.
*
+ * There are two kinds of transclusion depending upon whether you want to transclude just the contents of the
+ * directive's element or the entire element:
+ *
+ * * `true` - transclude the content (i.e. the child nodes) of the directive's element.
+ * * `'element'` - transclude the whole of the directive's element including any directives on this
+ * element that defined at a lower priority than this directive. When used, the `template`
+ * property is ignored.
+ *
*
* #### `compile`
*
@@ -20358,7 +19762,7 @@ function $TemplateCacheProvider() {
* <div class="alert alert-warning">
* **Note:** The compile function cannot handle directives that recursively use themselves in their
- * own templates or compile functions. Compiling these directives results in an infinite loop and
+ * own templates or compile functions. Compiling these directives results in an infinite loop and a
* stack overflow errors.
*
* This can be avoided by manually using $compile in the postLink function to imperatively compile
@@ -20414,7 +19818,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
- * any other controller.
+ * like any other controller.
*
* * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope.
* This is the same as the `$transclude`
@@ -20440,7 +19844,7 @@ function $TemplateCacheProvider() {
*
* ### Transclusion
*
- * Transclusion is the process of extracting a collection of DOM elements from one part of the DOM and
+ * Transclusion is the process of extracting a collection of DOM element 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.
*
@@ -20460,34 +19864,6 @@ function $TemplateCacheProvider() {
* Testing Transclusion Directives}.
* </div>
*
- * There are three kinds of transclusion depending upon whether you want to transclude just the contents of the
- * directive's element, the entire element or multiple parts of the element contents:
- *
- * * `true` - transclude the content (i.e. the child nodes) of the directive's element.
- * * `'element'` - transclude the whole of the directive's element including any directives on this
- * element that defined at a lower priority than this directive. When used, the `template`
- * property is ignored.
- * * **`{...}` (an object hash):** - map elements of the content onto transclusion "slots" in the template.
- *
- * **Mult-slot transclusion** is declared by providing an object for the `transclude` property.
- *
- * This object is a map where the keys are the name of the slot to fill and the value is an element selector
- * used to match the HTML to the slot. The element selector should be in normalized form (e.g. `myElement`)
- * and will match the standard element variants (e.g. `my-element`, `my:element`, `data-my-element`, etc).
- *
- * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}
- *
- * If the element selector is prefixed with a `?` then that slot is optional.
- *
- * For example, the transclude object `{ slotA: '?myCustomElement' }` maps `<my-custom-element>` elements to
- * the `slotA` slot, which can be accessed via the `$transclude` function or via the {@link ngTransclude} directive.
- *
- * Slots that are not marked as optional (`?`) will trigger a compile time error if there are no matching elements
- * in the transclude content. If you wish to know if an optional slot was filled with content, then you can call
- * `$transclude.isSlotFilled(slotName)` on the transclude function passed to the directive's link function and
- * injectable into the directive's controller.
- *
- *
* #### Transclusion Functions
*
* When a directive requests transclusion, the compiler extracts its contents and provides a **transclusion
@@ -20508,7 +19884,7 @@ function $TemplateCacheProvider() {
* content and the `scope` is the newly created transclusion scope, to which the clone is bound.
*
* <div class="alert alert-info">
- * **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a transclude function
+ * **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a translude function
* since you then get a fresh clone of the original DOM and also have access to the new transclusion scope.
* </div>
*
@@ -20540,7 +19916,7 @@ function $TemplateCacheProvider() {
* </div>
*
* The built-in DOM manipulation directives, such as {@link ngIf}, {@link ngSwitch} and {@link ngRepeat}
- * automatically destroy their transcluded clones as necessary so you do not need to worry about this if
+ * automatically destroy their transluded clones as necessary so you do not need to worry about this if
* you are simply using {@link ngTransclude} to inject the transclusion into your directive.
*
*
@@ -20565,19 +19941,19 @@ function $TemplateCacheProvider() {
*
* The `$parent` scope hierarchy will look like this:
*
- ```
- - $rootScope
- - isolate
- - transclusion
- ```
+ * ```
+ * - $rootScope
+ * - isolate
+ * - transclusion
+ * ```
*
* but the scopes will inherit prototypically from different scopes to their `$parent`.
*
- ```
- - $rootScope
- - transclusion
- - isolate
- ```
+ * ```
+ * - $rootScope
+ * - transclusion
+ * - isolate
+ * ```
*
*
* ### Attributes
@@ -20585,9 +19961,10 @@ function $TemplateCacheProvider() {
* The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the
* `link()` or `compile()` functions. It has a variety of uses.
*
- * * *Accessing normalized attribute names:* Directives like 'ngBind' can be expressed in many ways:
- * 'ng:bind', `data-ng-bind`, or 'x-ng-bind'. The attributes object allows for normalized access
- * to the attributes.
+ * accessing *Normalized attribute names:*
+ * Directives like 'ngBind' can be expressed in many ways: 'ng:bind', `data-ng-bind`, or 'x-ng-bind'.
+ * the attributes object allows for normalized access to
+ * the attributes.
*
* * *Directive inter-communication:* All directives share the same instance of the attributes
* object which allows the directives to use the attributes object as inter directive
@@ -20708,15 +20085,8 @@ function $TemplateCacheProvider() {
* directives; if given, it will be passed through to the link functions of
* directives found in `element` during compilation.
* * `transcludeControllers` - an object hash with keys that map controller names
- * to a hash with the key `instance`, which maps to the controller instance;
- * if given, it will make the controllers available to directives on the compileNode:
- * ```
- * {
- * parent: {
- * instance: parentControllerInstance
- * }
- * }
- * ```
+ * to controller instances; if given, it will make the controllers
+ * available to directives.
* * `futureParentElement` - defines the parent to which the `cloneAttachFn` will add
* the cloned elements; only needed for transcludes that are allowed to contain non html
* elements (e.g. SVG elements). See also the directive.controller property.
@@ -20775,18 +20145,13 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
// The assumption is that future DOM event attribute names will begin with
// 'on' and be composed of only English letters.
var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/;
- var bindingCache = createMap();
function parseIsolateBindings(scope, directiveName, isController) {
- var LOCAL_REGEXP = /^\s*([@&<]|=(\*?))(\??)\s*(\w*)\s*$/;
+ var LOCAL_REGEXP = /^\s*([@&]|=(\*?))(\??)\s*(\w*)\s*$/;
var bindings = {};
forEach(scope, function(definition, scopeName) {
- if (definition in bindingCache) {
- bindings[scopeName] = bindingCache[definition];
- return;
- }
var match = definition.match(LOCAL_REGEXP);
if (!match) {
@@ -20804,9 +20169,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
optional: match[3] === '?',
attrName: match[4] || scopeName
};
- if (match[4]) {
- bindingCache[definition] = bindings[scopeName];
- }
});
return bindings;
@@ -20852,11 +20214,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
function assertValidDirectiveName(name) {
var letter = name.charAt(0);
if (!letter || letter !== lowercase(letter)) {
- throw $compileMinErr('baddir', "Directive/Component name '{0}' is invalid. The first character must be a lowercase letter", name);
+ throw $compileMinErr('baddir', "Directive name '{0}' is invalid. The first character must be a lowercase letter", name);
}
if (name !== name.trim()) {
throw $compileMinErr('baddir',
- "Directive/Component name '{0}' is invalid. The name should not contain leading or trailing whitespaces",
+ "Directive name '{0}' is invalid. The name should not contain leading or trailing whitespaces",
name);
}
}
@@ -20872,11 +20234,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
* @param {string|Object} name Name of the directive in camel-case (i.e. <code>ngBind</code> which
* will match as <code>ng-bind</code>), or an object map of directives where the keys are the
* names and the values are the factories.
- * @param {Function|Array} directiveFactory An injectable directive factory function. See the
- * {@link guide/directive directive guide} and the {@link $compile compile API} for more info.
+ * @param {Function|Array} directiveFactory An injectable directive factory function. See
+ * {@link guide/directive} for more info.
* @returns {ng.$compileProvider} Self for chaining.
*/
- this.directive = function registerDirective(name, directiveFactory) {
+ this.directive = function registerDirective(name, directiveFactory) {
assertNotHasOwnProperty(name, 'directive');
if (isString(name)) {
assertValidDirectiveName(name);
@@ -20899,6 +20261,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
directive.name = directive.name || name;
directive.require = directive.require || (directive.controller && directive.name);
directive.restrict = directive.restrict || 'EA';
+ var bindings = directive.$$bindings =
+ parseDirectiveBindings(directive, directive.name);
+ if (isObject(bindings.isolateScope)) {
+ directive.$$isolateBindings = bindings.isolateScope;
+ }
directive.$$moduleName = directiveFactory.$$moduleName;
directives.push(directive);
} catch (e) {
@@ -20915,131 +20282,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
return this;
};
- /**
- * @ngdoc method
- * @name $compileProvider#component
- * @module ng
- * @param {string} name Name of the component in camelCase (i.e. `myComp` which will match `<my-comp>`)
- * @param {Object} options Component definition object (a simplified
- * {@link ng.$compile#directive-definition-object directive definition object}),
- * with the following properties (all optional):
- *
- * - `controller` – `{(string|function()=}` – controller constructor function that should be
- * associated with newly created scope or the name of a {@link ng.$compile#-controller-
- * registered controller} if passed as a string. An empty `noop` function by default.
- * - `controllerAs` – `{string=}` – identifier name for to reference the controller in the component's scope.
- * If present, the controller will be published to scope under the `controllerAs` name.
- * If not present, this will default to be `$ctrl`.
- * - `template` – `{string=|function()=}` – html template as a string or a function that
- * returns an html template as a string which should be used as the contents of this component.
- * Empty string by default.
- *
- * If `template` is a function, then it is {@link auto.$injector#invoke injected} with
- * the following locals:
- *
- * - `$element` - Current element
- * - `$attrs` - Current attributes object for the element
- *
- * - `templateUrl` – `{string=|function()=}` – path or function that returns a path to an html
- * template that should be used as the contents of this component.
- *
- * If `templateUrl` is a function, then it is {@link auto.$injector#invoke injected} with
- * the following locals:
- *
- * - `$element` - Current element
- * - `$attrs` - Current attributes object for the element
- *
- * - `bindings` – `{object=}` – defines bindings between DOM attributes and component properties.
- * Component properties are always bound to the component controller and not to the scope.
- * See {@link ng.$compile#-bindtocontroller- `bindToController`}.
- * - `transclude` – `{boolean=}` – whether {@link $compile#transclusion content transclusion} is enabled.
- * Disabled by default.
- * - `$...` – additional properties to attach to the directive factory function and the controller
- * constructor function. (This is used by the component router to annotate)
- *
- * @returns {ng.$compileProvider} the compile provider itself, for chaining of function calls.
- * @description
- * Register a **component definition** with the compiler. This is a shorthand for registering a special
- * type of directive, which represents a self-contained UI component in your application. Such components
- * are always isolated (i.e. `scope: {}`) and are always restricted to elements (i.e. `restrict: 'E'`).
- *
- * Component definitions are very simple and do not require as much configuration as defining general
- * directives. Component definitions usually consist only of a template and a controller backing it.
- *
- * In order to make the definition easier, components enforce best practices like use of `controllerAs`,
- * `bindToController`. They always have **isolate scope** and are restricted to elements.
- *
- * Here are a few examples of how you would usually define components:
- *
- * ```js
- * var myMod = angular.module(...);
- * myMod.component('myComp', {
- * template: '<div>My name is {{$ctrl.name}}</div>',
- * controller: function() {
- * this.name = 'shahar';
- * }
- * });
- *
- * myMod.component('myComp', {
- * template: '<div>My name is {{$ctrl.name}}</div>',
- * bindings: {name: '@'}
- * });
- *
- * myMod.component('myComp', {
- * templateUrl: 'views/my-comp.html',
- * controller: 'MyCtrl',
- * controllerAs: 'ctrl',
- * bindings: {name: '@'}
- * });
- *
- * ```
- * For more examples, and an in-depth guide, see the {@link guide/component component guide}.
- *
- * <br />
- * See also {@link ng.$compileProvider#directive $compileProvider.directive()}.
- */
- this.component = function registerComponent(name, options) {
- var controller = options.controller || noop;
-
- function factory($injector) {
- function makeInjectable(fn) {
- if (isFunction(fn) || isArray(fn)) {
- return function(tElement, tAttrs) {
- return $injector.invoke(fn, this, {$element: tElement, $attrs: tAttrs});
- };
- } else {
- return fn;
- }
- }
-
- var template = (!options.template && !options.templateUrl ? '' : options.template);
- return {
- controller: controller,
- controllerAs: identifierForController(options.controller) || options.controllerAs || '$ctrl',
- template: makeInjectable(template),
- templateUrl: makeInjectable(options.templateUrl),
- transclude: options.transclude,
- scope: {},
- bindToController: options.bindings || {},
- restrict: 'E',
- require: options.require
- };
- }
-
- // Copy any annotation properties (starting with $) over to the factory function
- // These could be used by libraries such as the new component router
- forEach(options, function(val, key) {
- if (key.charAt(0) === '$') {
- factory[key] = val;
- controller[key] = val;
- }
- });
-
- factory.$inject = ['$injector'];
-
- return this.directive(name, factory);
- };
-
/**
* @ngdoc method
@@ -21131,75 +20373,13 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
return debugInfoEnabled;
};
-
- var TTL = 10;
- /**
- * @ngdoc method
- * @name $compileProvider#onChangesTtl
- * @description
- *
- * Sets the number of times `$onChanges` hooks can trigger new changes before giving up and
- * assuming that the model is unstable.
- *
- * The current default is 10 iterations.
- *
- * In complex applications it's possible that dependencies between `$onChanges` hooks and bindings will result
- * in several iterations of calls to these hooks. However if an application needs more than the default 10
- * iterations to stabilize then you should investigate what is causing the model to continuously change during
- * the `$onChanges` hook execution.
- *
- * Increasing the TTL could have performance implications, so you should not change it without proper justification.
- *
- * @param {number} limit The number of `$onChanges` hook iterations.
- * @returns {number|object} the current limit (or `this` if called as a setter for chaining)
- */
- this.onChangesTtl = function(value) {
- if (arguments.length) {
- TTL = value;
- return this;
- }
- return TTL;
- };
-
this.$get = [
'$injector', '$interpolate', '$exceptionHandler', '$templateRequest', '$parse',
- '$controller', '$rootScope', '$sce', '$animate', '$$sanitizeUri',
+ '$controller', '$rootScope', '$document', '$sce', '$animate', '$$sanitizeUri',
function($injector, $interpolate, $exceptionHandler, $templateRequest, $parse,
- $controller, $rootScope, $sce, $animate, $$sanitizeUri) {
-
- var SIMPLE_ATTR_NAME = /^\w/;
- var specialAttrHolder = document.createElement('div');
-
-
-
- var onChangesTtl = TTL;
- // The onChanges hooks should all be run together in a single digest
- // When changes occur, the call to trigger their hooks will be added to this queue
- var onChangesQueue;
-
- // This function is called in a $$postDigest to trigger all the onChanges hooks in a single digest
- function flushOnChangesQueue() {
- try {
- if (!(--onChangesTtl)) {
- // We have hit the TTL limit so reset everything
- onChangesQueue = undefined;
- throw $compileMinErr('infchng', '{0} $onChanges() iterations reached. Aborting!\n', TTL);
- }
- // We must run this hook in an apply since the $$postDigest runs outside apply
- $rootScope.$apply(function() {
- for (var i = 0, ii = onChangesQueue.length; i < ii; ++i) {
- onChangesQueue[i]();
- }
- // Reset the queue to trigger a new schedule next time there is a change
- onChangesQueue = undefined;
- });
- } finally {
- onChangesTtl++;
- }
- }
+ $controller, $rootScope, $document, $sce, $animate, $$sanitizeUri) {
-
- function Attributes(element, attributesToCopy) {
+ var Attributes = function(element, attributesToCopy) {
if (attributesToCopy) {
var keys = Object.keys(attributesToCopy);
var i, l, key;
@@ -21213,7 +20393,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
}
this.$$element = element;
- }
+ };
Attributes.prototype = {
/**
@@ -21308,7 +20488,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
var node = this.$$element[0],
booleanKey = getBooleanAttrName(node, key),
- aliasedKey = getAliasedAttrName(key),
+ aliasedKey = getAliasedAttrName(node, key),
observer = key,
nodeName;
@@ -21334,7 +20514,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
nodeName = nodeName_(this.$$element);
- if ((nodeName === 'a' && (key === 'href' || key === 'xlinkHref')) ||
+ if ((nodeName === 'a' && key === 'href') ||
(nodeName === 'img' && key === 'src')) {
// sanitize a[href] and img[src] values
this[key] = value = $$sanitizeUri(value, key === 'src');
@@ -21375,14 +20555,10 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
}
if (writeAttr !== false) {
- if (value === null || isUndefined(value)) {
+ if (value === null || value === undefined) {
this.$$element.removeAttr(attrName);
} else {
- if (SIMPLE_ATTR_NAME.test(attrName)) {
- this.$$element.attr(attrName, value);
- } else {
- setSpecialAttr(this.$$element[0], attrName, value);
- }
+ this.$$element.attr(attrName, value);
}
}
@@ -21413,8 +20589,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
* @param {string} key Normalized key. (ie ngAttribute) .
* @param {function(interpolatedValue)} fn Function that will be called whenever
the interpolated value of the attribute changes.
- * See the {@link guide/interpolation#how-text-and-attribute-bindings-work Interpolation
- * guide} for more info.
+ * See the {@link guide/directive#text-and-attribute-bindings Directives} guide for more info.
* @returns {function()} Returns a deregistration function for this observer.
*/
$observe: function(key, fn) {
@@ -21424,7 +20599,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
listeners.push(fn);
$rootScope.$evalAsync(function() {
- if (!listeners.$$inter && attrs.hasOwnProperty(key) && !isUndefined(attrs[key])) {
+ if (!listeners.$$inter && attrs.hasOwnProperty(key)) {
// no one registered attribute interpolation function, so lets call it manually
fn(attrs[key]);
}
@@ -21436,18 +20611,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
}
};
- function setSpecialAttr(element, attrName, value) {
- // Attributes names that do not start with letters (such as `(click)`) cannot be set using `setAttribute`
- // so we have to jump through some hoops to get such an attribute
- // https://github.com/angular/angular.js/pull/13318
- specialAttrHolder.innerHTML = "<span " + attrName + ">";
- var attributes = specialAttrHolder.firstChild.attributes;
- var attribute = attributes[0];
- // We have to remove the attribute from its container element before we can add it to the destination element
- attributes.removeNamedItem(attribute.name);
- attribute.value = value;
- element.attributes.setNamedItem(attribute);
- }
function safeAddClass($element, className) {
try {
@@ -21461,13 +20624,12 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
var startSymbol = $interpolate.startSymbol(),
endSymbol = $interpolate.endSymbol(),
- denormalizeTemplate = (startSymbol == '{{' && endSymbol == '}}')
+ denormalizeTemplate = (startSymbol == '{{' || endSymbol == '}}')
? identity
: function denormalizeTemplate(template) {
return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol);
},
NG_ATTR_BINDING = /^ngAttr[A-Z]/;
- var MULTI_ELEMENT_DIR_RE = /^(.+)Start$/;
compile.$$addBindingInfo = debugInfoEnabled ? function $$addBindingInfo($element, binding) {
var bindings = $element.data('$binding') || [];
@@ -21494,14 +20656,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
safeAddClass($element, isolated ? 'ng-isolate-scope' : 'ng-scope');
} : noop;
- compile.$$createComment = function(directiveName, comment) {
- var content = '';
- if (debugInfoEnabled) {
- content = ' ' + (directiveName || '') + ': ' + (comment || '') + ' ';
- }
- return document.createComment(content);
- };
-
return compile;
//================================
@@ -21513,19 +20667,13 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
// modify it.
$compileNodes = jqLite($compileNodes);
}
-
- var NOT_EMPTY = /\S+/;
-
// We can not compile top level text elements since text nodes can be merged and we will
// not be able to attach scope data to them, so we will wrap them in <span>
- for (var i = 0, len = $compileNodes.length; i < len; i++) {
- var domNode = $compileNodes[i];
-
- if (domNode.nodeType === NODE_TYPE_TEXT && domNode.nodeValue.match(NOT_EMPTY) /* non-empty */) {
- jqLiteWrapNode(domNode, $compileNodes[i] = document.createElement('span'));
+ forEach($compileNodes, function(node, index) {
+ if (node.nodeType == NODE_TYPE_TEXT && node.nodeValue.match(/\S+/) /* non-empty */ ) {
+ $compileNodes[index] = jqLite(node).wrap('<span></span>').parent()[0];
}
- }
-
+ });
var compositeLinkFn =
compileNodes($compileNodes, transcludeFn, $compileNodes,
maxPriority, ignoreDirective, previousCompileContext);
@@ -21534,14 +20682,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
return function publicLinkFn(scope, cloneConnectFn, options) {
assertArg(scope, 'scope');
- if (previousCompileContext && previousCompileContext.needsNewScope) {
- // A parent directive did a replace and a directive on this element asked
- // for transclusion, which caused us to lose a layer of element on which
- // we could hold the new transclusion scope, so we will create it manually
- // here.
- scope = scope.$parent.$new();
- }
-
options = options || {};
var parentBoundTranscludeFn = options.parentBoundTranscludeFn,
transcludeControllers = options.transcludeControllers,
@@ -21596,7 +20736,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
if (!node) {
return 'html';
} else {
- return nodeName_(node) !== 'foreignobject' && toString.call(node).match(/SVG/) ? 'svg' : 'html';
+ return nodeName_(node) !== 'foreignobject' && node.toString().match(/SVG/) ? 'svg' : 'html';
}
}
@@ -21687,6 +20827,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
if (nodeLinkFn.scope) {
childScope = scope.$new();
compile.$$addScopeInfo(jqLite(node), childScope);
+ var destroyBindings = nodeLinkFn.$$destroyBindings;
+ if (destroyBindings) {
+ nodeLinkFn.$$destroyBindings = null;
+ childScope.$on('$destroyed', destroyBindings);
+ }
} else {
childScope = scope;
}
@@ -21705,7 +20850,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
childBoundTranscludeFn = null;
}
- nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn);
+ nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn,
+ nodeLinkFn);
} else if (childLinkFn) {
childLinkFn(scope, node.childNodes, undefined, parentBoundTranscludeFn);
@@ -21715,7 +20861,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
}
function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn) {
- function boundTranscludeFn(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
+
+ var boundTranscludeFn = function(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
if (!transcludedScope) {
transcludedScope = scope.$new(false, containingScope);
@@ -21727,18 +20874,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
transcludeControllers: controllers,
futureParentElement: futureParentElement
});
- }
-
- // We need to attach the transclusion slots onto the `boundTranscludeFn`
- // so that they are available inside the `controllersBoundTransclude` function
- var boundSlots = boundTranscludeFn.$$slots = createMap();
- for (var slotName in transcludeFn.$$slots) {
- if (transcludeFn.$$slots[slotName]) {
- boundSlots[slotName] = createBoundTranscludeFn(scope, transcludeFn.$$slots[slotName], previousBoundTranscludeFn);
- } else {
- boundSlots[slotName] = null;
- }
- }
+ };
return boundTranscludeFn;
}
@@ -21784,11 +20920,13 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
});
}
- var multiElementMatch = ngAttrName.match(MULTI_ELEMENT_DIR_RE);
- if (multiElementMatch && directiveIsMultiElement(multiElementMatch[1])) {
- attrStartName = name;
- attrEndName = name.substr(0, name.length - 5) + 'end';
- name = name.substr(0, name.length - 6);
+ var directiveNName = ngAttrName.replace(/(Start|End)$/, '');
+ if (directiveIsMultiElement(directiveNName)) {
+ if (ngAttrName === directiveNName + 'Start') {
+ attrStartName = name;
+ attrEndName = name.substr(0, name.length - 5) + 'end';
+ name = name.substr(0, name.length - 6);
+ }
}
nName = directiveNormalize(name.toLowerCase());
@@ -21892,42 +21030,13 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
* @returns {Function}
*/
function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) {
- return function groupedElementsLink(scope, element, attrs, controllers, transcludeFn) {
+ return function(scope, element, attrs, controllers, transcludeFn) {
element = groupScan(element[0], attrStart, attrEnd);
return linkFn(scope, element, attrs, controllers, transcludeFn);
};
}
/**
- * A function generator that is used to support both eager and lazy compilation
- * linking function.
- * @param eager
- * @param $compileNodes
- * @param transcludeFn
- * @param maxPriority
- * @param ignoreDirective
- * @param previousCompileContext
- * @returns {Function}
- */
- function compilationGenerator(eager, $compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext) {
- var compiled;
-
- if (eager) {
- return compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext);
- }
- return function lazyCompilation() {
- if (!compiled) {
- compiled = compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext);
-
- // Null out all of these references in order to make them eligible for garbage collection
- // since this is a potentially long lived closure
- $compileNodes = transcludeFn = previousCompileContext = null;
- }
- return compiled.apply(this, arguments);
- };
- }
-
- /**
* Once the directives have been collected, their compile functions are executed. This method
* is responsible for inlining directive templates as well as terminating the application
* of the directives if the terminal directive has been reached.
@@ -21971,8 +21080,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
replaceDirective = originalReplaceDirective,
childTranscludeFn = transcludeFn,
linkFn,
- didScanForMultipleTransclusion = false,
- mightHaveMultipleTransclusionError = false,
directiveValue;
// executes all directives on the current element
@@ -22015,27 +21122,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
directiveName = directive.name;
- // If we encounter a condition that can result in transclusion on the directive,
- // then scan ahead in the remaining directives for others that may cause a multiple
- // transclusion error to be thrown during the compilation process. If a matching directive
- // is found, then we know that when we encounter a transcluded directive, we need to eagerly
- // compile the `transclude` function rather than doing it lazily in order to throw
- // exceptions at the correct time
- if (!didScanForMultipleTransclusion && ((directive.replace && (directive.templateUrl || directive.template))
- || (directive.transclude && !directive.$$tlb))) {
- var candidateDirective;
-
- for (var scanningIndex = i + 1; candidateDirective = directives[scanningIndex++];) {
- if ((candidateDirective.transclude && !candidateDirective.$$tlb)
- || (candidateDirective.replace && (candidateDirective.templateUrl || candidateDirective.template))) {
- mightHaveMultipleTransclusionError = true;
- break;
- }
- }
-
- didScanForMultipleTransclusion = true;
- }
-
if (!directive.templateUrl && directive.controller) {
directiveValue = directive.controller;
controllerDirectives = controllerDirectives || createMap();
@@ -22060,22 +21146,12 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
terminalPriority = directive.priority;
$template = $compileNode;
$compileNode = templateAttrs.$$element =
- jqLite(compile.$$createComment(directiveName, templateAttrs[directiveName]));
+ jqLite(document.createComment(' ' + directiveName + ': ' +
+ templateAttrs[directiveName] + ' '));
compileNode = $compileNode[0];
replaceWith(jqCollection, sliceArgs($template), compileNode);
- // Support: Chrome < 50
- // https://github.com/angular/angular.js/issues/14041
-
- // In the versions of V8 prior to Chrome 50, the document fragment that is created
- // in the `replaceWith` function is improperly garbage collected despite still
- // being referenced by the `parentNode` property of all of the child nodes. By adding
- // a reference to the fragment via a different property, we can avoid that incorrect
- // behavior.
- // TODO: remove this line after Chrome 50 has been released
- $template[0].$$parentNode = $template[0].parentNode;
-
- childTranscludeFn = compilationGenerator(mightHaveMultipleTransclusionError, $template, transcludeFn, terminalPriority,
+ childTranscludeFn = compile($template, transcludeFn, terminalPriority,
replaceDirective && replaceDirective.name, {
// Don't pass in:
// - controllerDirectives - otherwise we'll create duplicates controllers
@@ -22087,69 +21163,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
nonTlbTranscludeDirective: nonTlbTranscludeDirective
});
} else {
-
- var slots = createMap();
-
$template = jqLite(jqLiteClone(compileNode)).contents();
-
- if (isObject(directiveValue)) {
-
- // We have transclusion slots,
- // collect them up, compile them and store their transclusion functions
- $template = [];
-
- var slotMap = createMap();
- var filledSlots = createMap();
-
- // Parse the element selectors
- forEach(directiveValue, function(elementSelector, slotName) {
- // If an element selector starts with a ? then it is optional
- var optional = (elementSelector.charAt(0) === '?');
- elementSelector = optional ? elementSelector.substring(1) : elementSelector;
-
- slotMap[elementSelector] = slotName;
-
- // We explicitly assign `null` since this implies that a slot was defined but not filled.
- // Later when calling boundTransclusion functions with a slot name we only error if the
- // slot is `undefined`
- slots[slotName] = null;
-
- // filledSlots contains `true` for all slots that are either optional or have been
- // filled. This is used to check that we have not missed any required slots
- filledSlots[slotName] = optional;
- });
-
- // Add the matching elements into their slot
- forEach($compileNode.contents(), function(node) {
- var slotName = slotMap[directiveNormalize(nodeName_(node))];
- if (slotName) {
- filledSlots[slotName] = true;
- slots[slotName] = slots[slotName] || [];
- slots[slotName].push(node);
- } else {
- $template.push(node);
- }
- });
-
- // Check for required slots that were not filled
- forEach(filledSlots, function(filled, slotName) {
- if (!filled) {
- throw $compileMinErr('reqslot', 'Required transclusion slot `{0}` was not filled.', slotName);
- }
- });
-
- for (var slotName in slots) {
- if (slots[slotName]) {
- // Only define a transclusion function if the slot was filled
- slots[slotName] = compilationGenerator(mightHaveMultipleTransclusionError, slots[slotName], transcludeFn);
- }
- }
- }
-
$compileNode.empty(); // clear contents
- childTranscludeFn = compilationGenerator(mightHaveMultipleTransclusionError, $template, transcludeFn, undefined,
- undefined, { needsNewScope: directive.$$isolateScope || directive.$$newScope});
- childTranscludeFn.$$slots = slots;
+ childTranscludeFn = compile($template, transcludeFn);
}
}
@@ -22191,11 +21207,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs);
var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1));
- if (newIsolateScopeDirective || newScopeDirective) {
- // The original directive caused the current element to be replaced but this element
- // also needs to have a new scope, so we need to tell the template directives
- // that they would need to get their scope from further up, if they require transclusion
- markDirectiveScope(templateDirectives, newIsolateScopeDirective, newScopeDirective);
+ if (newIsolateScopeDirective) {
+ markDirectivesAsIsolate(templateDirectives);
}
directives = directives.concat(templateDirectives).concat(unprocessedDirectives);
mergeTemplateAttributes(templateAttrs, newTemplateAttrs);
@@ -22277,9 +21290,81 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
}
}
- function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
- var i, ii, linkFn, isolateScope, controllerScope, elementControllers, transcludeFn, $element,
- attrs, removeScopeBindingWatches, removeControllerBindingWatches;
+
+ function getControllers(directiveName, require, $element, elementControllers) {
+ var value;
+
+ if (isString(require)) {
+ var match = require.match(REQUIRE_PREFIX_REGEXP);
+ var name = require.substring(match[0].length);
+ var inheritType = match[1] || match[3];
+ var optional = match[2] === '?';
+
+ //If only parents then start at the parent element
+ if (inheritType === '^^') {
+ $element = $element.parent();
+ //Otherwise attempt getting the controller from elementControllers in case
+ //the element is transcluded (and has no data) and to avoid .data if possible
+ } else {
+ value = elementControllers && elementControllers[name];
+ value = value && value.instance;
+ }
+
+ if (!value) {
+ var dataName = '$' + name + 'Controller';
+ value = inheritType ? $element.inheritedData(dataName) : $element.data(dataName);
+ }
+
+ if (!value && !optional) {
+ throw $compileMinErr('ctreq',
+ "Controller '{0}', required by directive '{1}', can't be found!",
+ name, directiveName);
+ }
+ } else if (isArray(require)) {
+ value = [];
+ for (var i = 0, ii = require.length; i < ii; i++) {
+ value[i] = getControllers(directiveName, require[i], $element, elementControllers);
+ }
+ }
+
+ return value || null;
+ }
+
+ function setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope) {
+ var elementControllers = createMap();
+ for (var controllerKey in controllerDirectives) {
+ var directive = controllerDirectives[controllerKey];
+ var locals = {
+ $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
+ $element: $element,
+ $attrs: attrs,
+ $transclude: transcludeFn
+ };
+
+ var controller = directive.controller;
+ if (controller == '@') {
+ controller = attrs[directive.name];
+ }
+
+ var controllerInstance = $controller(controller, locals, true, directive.controllerAs);
+
+ // For directives with element transclusion the element is a comment,
+ // but jQuery .data doesn't support attaching data to comment nodes as it's hard to
+ // clean up (http://bugs.jquery.com/ticket/8335).
+ // Instead, we save the controllers for the element in a local hash and attach to .data
+ // later, once we have the actual element.
+ elementControllers[directive.name] = controllerInstance;
+ if (!hasElementTranscludeDirective) {
+ $element.data('$' + directive.name + 'Controller', controllerInstance.instance);
+ }
+ }
+ return elementControllers;
+ }
+
+ function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn,
+ thisLinkFn) {
+ var i, ii, linkFn, controller, isolateScope, elementControllers, transcludeFn, $element,
+ attrs;
if (compileNode === linkNode) {
attrs = templateAttrs;
@@ -22289,11 +21374,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
attrs = new Attributes($element, templateAttrs);
}
- controllerScope = scope;
if (newIsolateScopeDirective) {
isolateScope = scope.$new(true);
- } else if (newScopeDirective) {
- controllerScope = scope.$parent;
}
if (boundTranscludeFn) {
@@ -22301,14 +21383,10 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
// is later passed as `parentBoundTranscludeFn` to `publicLinkFn`
transcludeFn = controllersBoundTransclude;
transcludeFn.$$boundTransclude = boundTranscludeFn;
- // expose the slots on the `$transclude` function
- transcludeFn.isSlotFilled = function(slotName) {
- return !!boundTranscludeFn.$$slots[slotName];
- };
}
if (controllerDirectives) {
- elementControllers = setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope, newIsolateScopeDirective);
+ elementControllers = setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope);
}
if (newIsolateScopeDirective) {
@@ -22318,58 +21396,45 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
compile.$$addScopeClass($element, true);
isolateScope.$$isolateBindings =
newIsolateScopeDirective.$$isolateBindings;
- removeScopeBindingWatches = initializeDirectiveBindings(scope, attrs, isolateScope,
- isolateScope.$$isolateBindings,
- newIsolateScopeDirective);
- if (removeScopeBindingWatches) {
- isolateScope.$on('$destroy', removeScopeBindingWatches);
- }
- }
-
- // Initialize bindToController bindings
- for (var name in elementControllers) {
- var controllerDirective = controllerDirectives[name];
- var controller = elementControllers[name];
- var bindings = controllerDirective.$$bindings.bindToController;
-
- if (controller.identifier && bindings) {
- removeControllerBindingWatches =
- initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
- }
-
- var controllerResult = controller();
- if (controllerResult !== controller.instance) {
- // If the controller constructor has a return value, overwrite the instance
- // from setupControllers
- controller.instance = controllerResult;
- $element.data('$' + controllerDirective.name + 'Controller', controllerResult);
- removeControllerBindingWatches && removeControllerBindingWatches();
- removeControllerBindingWatches =
- initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
+ initializeDirectiveBindings(scope, attrs, isolateScope,
+ isolateScope.$$isolateBindings,
+ newIsolateScopeDirective, isolateScope);
+ }
+ if (elementControllers) {
+ // Initialize bindToController bindings for new/isolate scopes
+ var scopeDirective = newIsolateScopeDirective || newScopeDirective;
+ var bindings;
+ var controllerForBindings;
+ if (scopeDirective && elementControllers[scopeDirective.name]) {
+ bindings = scopeDirective.$$bindings.bindToController;
+ controller = elementControllers[scopeDirective.name];
+
+ if (controller && controller.identifier && bindings) {
+ controllerForBindings = controller;
+ thisLinkFn.$$destroyBindings =
+ initializeDirectiveBindings(scope, attrs, controller.instance,
+ bindings, scopeDirective);
+ }
+ }
+ for (i in elementControllers) {
+ controller = elementControllers[i];
+ var controllerResult = controller();
+
+ if (controllerResult !== controller.instance) {
+ // If the controller constructor has a return value, overwrite the instance
+ // from setupControllers and update the element data
+ controller.instance = controllerResult;
+ $element.data('$' + i + 'Controller', controllerResult);
+ if (controller === controllerForBindings) {
+ // Remove and re-install bindToController bindings
+ thisLinkFn.$$destroyBindings();
+ thisLinkFn.$$destroyBindings =
+ initializeDirectiveBindings(scope, attrs, controllerResult, bindings, scopeDirective);
+ }
+ }
}
}
- // Bind the required controllers to the controller, if `require` is an object and `bindToController` is truthy
- forEach(controllerDirectives, function(controllerDirective, name) {
- var require = controllerDirective.require;
- if (controllerDirective.bindToController && !isArray(require) && isObject(require)) {
- extend(elementControllers[name].instance, getControllers(name, require, $element, elementControllers));
- }
- });
-
- // Handle the init and destroy lifecycle hooks on all controllers that have them
- forEach(elementControllers, function(controller) {
- var controllerInstance = controller.instance;
- if (isFunction(controllerInstance.$onInit)) {
- controllerInstance.$onInit();
- }
- if (isFunction(controllerInstance.$onDestroy)) {
- controllerScope.$on('$destroy', function callOnDestroyHook() {
- controllerInstance.$onDestroy();
- });
- }
- });
-
// PRELINKING
for (i = 0, ii = preLinkFns.length; i < ii; i++) {
linkFn = preLinkFns[i];
@@ -22403,21 +21468,13 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
);
}
- // Trigger $postLink lifecycle hooks
- forEach(elementControllers, function(controller) {
- var controllerInstance = controller.instance;
- if (isFunction(controllerInstance.$postLink)) {
- controllerInstance.$postLink();
- }
- });
-
// This is the function that is injected as `$transclude`.
// Note: all arguments are optional!
- function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement, slotName) {
+ function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement) {
var transcludeControllers;
+
// No scope passed in:
if (!isScope(scope)) {
- slotName = futureParentElement;
futureParentElement = cloneAttachFn;
cloneAttachFn = scope;
scope = undefined;
@@ -22429,108 +21486,15 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
if (!futureParentElement) {
futureParentElement = hasElementTranscludeDirective ? $element.parent() : $element;
}
- if (slotName) {
- // slotTranscludeFn can be one of three things:
- // * a transclude function - a filled slot
- // * `null` - an optional slot that was not filled
- // * `undefined` - a slot that was not declared (i.e. invalid)
- var slotTranscludeFn = boundTranscludeFn.$$slots[slotName];
- if (slotTranscludeFn) {
- return slotTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
- } else if (isUndefined(slotTranscludeFn)) {
- throw $compileMinErr('noslot',
- 'No parent directive that requires a transclusion with slot name "{0}". ' +
- 'Element: {1}',
- slotName, startingTag($element));
- }
- } else {
- return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
- }
- }
- }
- }
-
- function getControllers(directiveName, require, $element, elementControllers) {
- var value;
-
- if (isString(require)) {
- var match = require.match(REQUIRE_PREFIX_REGEXP);
- var name = require.substring(match[0].length);
- var inheritType = match[1] || match[3];
- var optional = match[2] === '?';
-
- //If only parents then start at the parent element
- if (inheritType === '^^') {
- $element = $element.parent();
- //Otherwise attempt getting the controller from elementControllers in case
- //the element is transcluded (and has no data) and to avoid .data if possible
- } else {
- value = elementControllers && elementControllers[name];
- value = value && value.instance;
- }
-
- if (!value) {
- var dataName = '$' + name + 'Controller';
- value = inheritType ? $element.inheritedData(dataName) : $element.data(dataName);
- }
-
- if (!value && !optional) {
- throw $compileMinErr('ctreq',
- "Controller '{0}', required by directive '{1}', can't be found!",
- name, directiveName);
+ return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
}
- } else if (isArray(require)) {
- value = [];
- for (var i = 0, ii = require.length; i < ii; i++) {
- value[i] = getControllers(directiveName, require[i], $element, elementControllers);
- }
- } else if (isObject(require)) {
- value = {};
- forEach(require, function(controller, property) {
- value[property] = getControllers(directiveName, controller, $element, elementControllers);
- });
}
-
- return value || null;
}
- function setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope, newIsolateScopeDirective) {
- var elementControllers = createMap();
- for (var controllerKey in controllerDirectives) {
- var directive = controllerDirectives[controllerKey];
- var locals = {
- $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
- $element: $element,
- $attrs: attrs,
- $transclude: transcludeFn
- };
-
- var controller = directive.controller;
- if (controller == '@') {
- controller = attrs[directive.name];
- }
-
- var controllerInstance = $controller(controller, locals, true, directive.controllerAs);
-
- // For directives with element transclusion the element is a comment.
- // In this case .data will not attach any data.
- // Instead, we save the controllers for the element in a local hash and attach to .data
- // later, once we have the actual element.
- elementControllers[directive.name] = controllerInstance;
- $element.data('$' + directive.name + 'Controller', controllerInstance.instance);
- }
- return elementControllers;
- }
-
- // Depending upon the context in which a directive finds itself it might need to have a new isolated
- // or child scope created. For instance:
- // * if the directive has been pulled into a template because another directive with a higher priority
- // asked for element transclusion
- // * if the directive itself asks for transclusion but it is at the root of a template and the original
- // element was replaced. See https://github.com/angular/angular.js/issues/12936
- function markDirectiveScope(directives, isolateScope, newScope) {
+ function markDirectivesAsIsolate(directives) {
+ // mark all directives as needing isolate scope.
for (var j = 0, jj = directives.length; j < jj; j++) {
- directives[j] = inherit(directives[j], {$$isolateScope: isolateScope, $$newScope: newScope});
+ directives[j] = inherit(directives[j], {$$isolateScope: true});
}
}
@@ -22557,18 +21521,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
i = 0, ii = directives.length; i < ii; i++) {
try {
directive = directives[i];
- if ((isUndefined(maxPriority) || maxPriority > directive.priority) &&
+ if ((maxPriority === undefined || maxPriority > directive.priority) &&
directive.restrict.indexOf(location) != -1) {
if (startAttrName) {
directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName});
}
- if (!directive.$$bindings) {
- var bindings = directive.$$bindings =
- parseDirectiveBindings(directive, directive.name);
- if (isObject(bindings.isolateScope)) {
- directive.$$isolateBindings = bindings.isolateScope;
- }
- }
tDirectives.push(directive);
match = directive;
}
@@ -22684,9 +21641,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs);
if (isObject(origAsyncDirective.scope)) {
- // the original directive that caused the template to be loaded async required
- // an isolate scope
- markDirectiveScope(templateDirectives, true);
+ markDirectivesAsIsolate(templateDirectives);
}
directives = templateDirectives.concat(directives);
mergeTemplateAttributes(tAttrs, tempTemplateAttrs);
@@ -22735,7 +21690,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
childBoundTranscludeFn = boundTranscludeFn;
}
afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement,
- childBoundTranscludeFn);
+ childBoundTranscludeFn, afterTemplateNodeLinkFn);
}
linkQueue = null;
});
@@ -22752,7 +21707,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
}
- afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn);
+ afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn,
+ afterTemplateNodeLinkFn);
}
};
}
@@ -22861,7 +21817,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
compile: function() {
return {
pre: function attrInterpolatePreLinkFn(scope, element, attr) {
- var $$observers = (attr.$$observers || (attr.$$observers = createMap()));
+ var $$observers = (attr.$$observers || (attr.$$observers = {}));
if (EVENT_HANDLER_ATTR_REGEXP.test(name)) {
throw $compileMinErr('nodomevents',
@@ -22956,33 +21912,41 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
parent.replaceChild(newNode, firstElementToRemove);
}
- // Append all the `elementsToRemove` to a fragment. This will...
- // - remove them from the DOM
- // - allow them to still be traversed with .nextSibling
- // - allow a single fragment.qSA to fetch all elements being removed
+ // TODO(perf): what's this document fragment for? is it needed? can we at least reuse it?
var fragment = document.createDocumentFragment();
- for (i = 0; i < removeCount; i++) {
- fragment.appendChild(elementsToRemove[i]);
- }
+ fragment.appendChild(firstElementToRemove);
if (jqLite.hasData(firstElementToRemove)) {
// Copy over user data (that includes Angular's $scope etc.). Don't copy private
// data here because there's no public interface in jQuery to do that and copying over
// event listeners (which is the main use of private data) wouldn't work anyway.
- jqLite.data(newNode, jqLite.data(firstElementToRemove));
+ jqLite(newNode).data(jqLite(firstElementToRemove).data());
- // Remove $destroy event listeners from `firstElementToRemove`
- jqLite(firstElementToRemove).off('$destroy');
+ // Remove data of the replaced element. We cannot just call .remove()
+ // on the element it since that would deallocate scope that is needed
+ // for the new node. Instead, remove the data "manually".
+ if (!jQuery) {
+ delete jqLite.cache[firstElementToRemove[jqLite.expando]];
+ } else {
+ // jQuery 2.x doesn't expose the data storage. Use jQuery.cleanData to clean up after
+ // the replaced element. The cleanData version monkey-patched by Angular would cause
+ // the scope to be trashed and we do need the very same scope to work with the new
+ // element. However, we cannot just cache the non-patched version and use it here as
+ // that would break if another library patches the method after Angular does (one
+ // example is jQuery UI). Instead, set a flag indicating scope destroying should be
+ // skipped this one time.
+ skipDestroyOnNextJQueryCleanData = true;
+ jQuery.cleanData([firstElementToRemove]);
+ }
}
- // Cleanup any data/listeners on the elements and children.
- // This includes invoking the $destroy event on any elements with listeners.
- jqLite.cleanData(fragment.querySelectorAll('*'));
-
- // Update the jqLite collection to only contain the `newNode`
- for (i = 1; i < removeCount; i++) {
- delete elementsToRemove[i];
+ for (var k = 1, kk = elementsToRemove.length; k < kk; k++) {
+ var element = elementsToRemove[k];
+ jqLite(element).remove(); // must do this way to clean up expando
+ fragment.appendChild(element);
+ delete elementsToRemove[k];
}
+
elementsToRemove[0] = newNode;
elementsToRemove.length = 1;
}
@@ -23004,61 +21968,57 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
// Set up $watches for isolate scope and controller bindings. This process
// only occurs for isolate scopes and new scopes with controllerAs.
- function initializeDirectiveBindings(scope, attrs, destination, bindings, directive) {
- var removeWatchCollection = [];
- var changes;
- forEach(bindings, function initializeBinding(definition, scopeName) {
+ function initializeDirectiveBindings(scope, attrs, destination, bindings,
+ directive, newScope) {
+ var onNewScopeDestroyed;
+ forEach(bindings, function(definition, scopeName) {
var attrName = definition.attrName,
optional = definition.optional,
mode = definition.mode, // @, =, or &
lastValue,
- parentGet, parentSet, compare, removeWatch;
+ 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 (!optional && !hasOwnProperty.call(attrs, attrName)) {
- destination[scopeName] = attrs[attrName] = void 0;
+ if (!attrs[attrName] && !optional) {
+ destination[scopeName] = undefined;
}
+
attrs.$observe(attrName, function(value) {
- if (isString(value)) {
- var oldValue = destination[scopeName];
- recordChanges(scopeName, value, oldValue);
- destination[scopeName] = value;
- }
+ destination[scopeName] = value;
});
attrs.$$observers[attrName].$$scope = scope;
- lastValue = attrs[attrName];
- if (isString(lastValue)) {
+ if (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(lastValue)(scope);
- } else if (isBoolean(lastValue)) {
- // If the attributes is one of the BOOLEAN_ATTR then Angular will have converted
- // the value to boolean rather than a string, so we special case this situation
- destination[scopeName] = lastValue;
+ destination[scopeName] = $interpolate(attrs[attrName])(scope);
}
break;
case '=':
- if (!hasOwnProperty.call(attrs, attrName)) {
- if (optional) break;
- attrs[attrName] = void 0;
+ if (optional && !attrs[attrName]) {
+ return;
}
- if (optional && !attrs[attrName]) break;
-
parentGet = $parse(attrs[attrName]);
+
if (parentGet.literal) {
compare = equals;
} else {
- compare = function simpleCompare(a, b) { return a === b || (a !== a && b !== b); };
+ compare = function(a, b) { return a === b || (a !== a && b !== b); };
}
parentSet = parentGet.assign || function() {
// reset the change, or we will throw this exception on every $digest
lastValue = destination[scopeName] = parentGet(scope);
throw $compileMinErr('nonassign',
- "Expression '{0}' in attribute '{1}' used with directive '{2}' is non-assignable!",
- attrs[attrName], attrName, directive.name);
+ "Expression '{0}' used with directive '{1}' is non-assignable!",
+ attrs[attrName], directive.name);
};
lastValue = destination[scopeName] = parentGet(scope);
var parentValueWatch = function parentValueWatch(parentValue) {
@@ -23075,37 +22035,18 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
return lastValue = parentValue;
};
parentValueWatch.$stateful = true;
+ var unwatch;
if (definition.collection) {
- removeWatch = scope.$watchCollection(attrs[attrName], parentValueWatch);
+ unwatch = scope.$watchCollection(attrs[attrName], parentValueWatch);
} else {
- removeWatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal);
+ unwatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal);
}
- removeWatchCollection.push(removeWatch);
- break;
-
- case '<':
- if (!hasOwnProperty.call(attrs, attrName)) {
- if (optional) break;
- attrs[attrName] = void 0;
- }
- if (optional && !attrs[attrName]) break;
-
- parentGet = $parse(attrs[attrName]);
-
- destination[scopeName] = parentGet(scope);
-
- removeWatch = scope.$watch(parentGet, function parentValueWatchAction(newParentValue) {
- var oldValue = destination[scopeName];
- recordChanges(scopeName, newParentValue, oldValue);
- destination[scopeName] = newParentValue;
- }, parentGet.literal);
-
- removeWatchCollection.push(removeWatch);
+ onNewScopeDestroyed = (onNewScopeDestroyed || []);
+ onNewScopeDestroyed.push(unwatch);
break;
case '&':
- // Don't assign Object.prototype method to scope
- parentGet = attrs.hasOwnProperty(attrName) ? $parse(attrs[attrName]) : noop;
+ parentGet = $parse(attrs[attrName]);
// Don't assign noop to destination if expression is not valid
if (parentGet === noop && optional) break;
@@ -23116,39 +22057,16 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
break;
}
});
-
- function recordChanges(key, currentValue, previousValue) {
- if (isFunction(destination.$onChanges) && currentValue !== previousValue) {
- // If we have not already scheduled the top level onChangesQueue handler then do so now
- if (!onChangesQueue) {
- scope.$$postDigest(flushOnChangesQueue);
- onChangesQueue = [];
- }
- // If we have not already queued a trigger of onChanges for this controller then do so now
- if (!changes) {
- changes = {};
- onChangesQueue.push(triggerOnChangesHook);
- }
- // If the has been a change on this property already then we need to reuse the previous value
- if (changes[key]) {
- previousValue = changes[key].previousValue;
- }
- // Store this change
- changes[key] = {previousValue: previousValue, currentValue: currentValue};
+ var destroyBindings = onNewScopeDestroyed ? function destroyBindings() {
+ for (var i = 0, ii = onNewScopeDestroyed.length; i < ii; ++i) {
+ onNewScopeDestroyed[i]();
}
+ } : noop;
+ if (newScope && destroyBindings !== noop) {
+ newScope.$on('$destroy', destroyBindings);
+ return noop;
}
-
- function triggerOnChangesHook() {
- destination.$onChanges(changes);
- // Now clear the changes so that we schedule onChanges when more changes arrive
- changes = undefined;
- }
-
- return removeWatchCollection.length && function removeWatches() {
- for (var i = 0, ii = removeWatchCollection.length; i < ii; ++i) {
- removeWatchCollection[i]();
- }
- };
+ return destroyBindings;
}
}];
}
@@ -23258,7 +22176,7 @@ function removeComments(jqNodes) {
var $controllerMinErr = minErr('$controller');
-var CNTRL_REG = /^(\S+)(\s+as\s+([\w$]+))?$/;
+var CNTRL_REG = /^(\S+)(\s+as\s+(\w+))?$/;
function identifierForController(controller, ident) {
if (ident && isString(ident)) return ident;
if (isString(controller)) {
@@ -23284,15 +22202,6 @@ function $ControllerProvider() {
/**
* @ngdoc method
- * @name $controllerProvider#has
- * @param {string} name Controller name to check.
- */
- this.has = function(name) {
- return controllers.hasOwnProperty(name);
- };
-
- /**
- * @ngdoc method
* @name $controllerProvider#register
* @param {string|Object} name Controller name, or an object map of controllers where the keys are
* the names and the values are the constructors.
@@ -23347,7 +22256,7 @@ function $ControllerProvider() {
* It's just a simple call to {@link auto.$injector $injector}, but extracted into
* a service, so that one can override this service with [BC version](https://gist.github.com/1649788).
*/
- return function $controller(expression, locals, later, ident) {
+ return function(expression, locals, later, ident) {
// PRIVATE API:
// param `later` --- indicates that the controller's constructor is invoked at a later time.
// If true, $controller will allocate the object with the correct
@@ -23398,7 +22307,7 @@ function $ControllerProvider() {
}
var instantiate;
- return instantiate = extend(function $controllerInit() {
+ return instantiate = extend(function() {
var result = $injector.invoke(expression, instance, locals, constructor);
if (result !== instance && (isObject(result) || isFunction(result))) {
instance = result;
@@ -23514,29 +22423,6 @@ 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 = /^\[|^\{(?!\{)/;
@@ -23545,12 +22431,6 @@ 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)) {
@@ -23584,7 +22464,7 @@ function $HttpParamSerializerProvider() {
forEachSorted(params, function(value, key) {
if (value === null || isUndefined(value)) return;
if (isArray(value)) {
- forEach(value, function(v) {
+ forEach(value, function(v, k) {
parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(v)));
});
} else {
@@ -23651,8 +22531,8 @@ function $HttpParamSerializerJQLikeProvider() {
function serialize(toSerialize, prefix, topLevel) {
if (toSerialize === null || isUndefined(toSerialize)) return;
if (isArray(toSerialize)) {
- forEach(toSerialize, function(value, index) {
- serialize(value, prefix + '[' + (isObject(value) ? index : '') + ']');
+ forEach(toSerialize, function(value) {
+ serialize(value, prefix + '[]');
});
} else if (isObject(toSerialize) && !isDate(toSerialize)) {
forEachSorted(toSerialize, function(value, key) {
@@ -23794,9 +22674,10 @@ function $HttpProvider() {
*
* Object containing default values for all {@link ng.$http $http} requests.
*
- * - **`defaults.cache`** - {boolean|Object} - A boolean value or object created with
- * {@link ng.$cacheFactory `$cacheFactory`} to enable or disable caching of HTTP responses
- * by default. See {@link $http#caching $http Caching} for more information.
+ * - **`defaults.cache`** - {Object} - an object built with {@link ng.$cacheFactory `$cacheFactory`}
+ * that will provide the cache for all requests who set their `cache` property to `true`.
+ * If you set the `defaults.cache = false` then only requests that specify their own custom
+ * cache object will be cached. See {@link $http#caching $http Caching} for more information.
*
* - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token.
* Defaults value is `'XSRF-TOKEN'`.
@@ -23872,30 +22753,6 @@ 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 true. If no value is specified, returns the current configured value.
- *
- * @param {boolean=} value If true, `$http` will return a promise with the deprecated legacy `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
@@ -23961,47 +22818,66 @@ function $HttpProvider() {
*
*
* ## General usage
- * 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}.
+ * 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`.
*
* ```js
- * // Simple GET request example:
- * $http({
- * method: 'GET',
- * url: '/someUrl'
- * }).then(function successCallback(response) {
+ * // Simple GET request example :
+ * $http.get('/someUrl').
+ * success(function(data, status, headers, config) {
* // this callback will be called asynchronously
* // when the response is available
- * }, function errorCallback(response) {
+ * }).
+ * 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:
+ * ```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.
+ * });
+ * ```
*
- * - **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.
+ *
+ * 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.
*
* 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. An optional config can be passed as the
- * last argument.
+ * request data must be passed in for POST/PUT requests.
*
* ```js
- * $http.get('/someUrl', config).then(successCallback, errorCallback);
- * $http.post('/someUrl', data, config).then(successCallback, errorCallback);
+ * $http.get('/someUrl').success(successCallback);
+ * $http.post('/someUrl', data).success(successCallback);
* ```
*
* Complete list of shortcut methods:
@@ -24015,25 +22891,6 @@ 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
- * <div class="alert alert-danger">
- * 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.
- * </div>
- *
* ## Setting HTTP Headers
*
* The $http service will automatically add certain HTTP headers to all requests. These defaults
@@ -24057,7 +22914,7 @@ function $HttpProvider() {
*
* ```
* module.run(function($http) {
- * $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w';
+ * $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w'
* });
* ```
*
@@ -24077,7 +22934,7 @@ function $HttpProvider() {
* data: { test: 'test' }
* }
*
- * $http(req).then(function(){...}, function(){...});
+ * $http(req).success(function(){...}).error(function(){...});
* ```
*
* ## Transforming Requests and Responses
@@ -24087,15 +22944,6 @@ function $HttpProvider() {
* the transformed value (`function(data, headersGetter, status)`) or an array of such transformation functions,
* which allows you to `push` or `unshift` a new transformation function into the transformation chain.
*
- * <div class="alert alert-warning">
- * **Note:** Angular does not make a copy of the `data` parameter before it is passed into the `transformRequest` pipeline.
- * That means changes to the properties of `data` are not local to the transform function (since Javascript passes objects by reference).
- * For example, when calling `$http.get(url, $scope.myObject)`, modifications to the object's properties in a transformRequest
- * function will be reflected on the scope and in any templates where the object is data-bound.
- * To prevent his, transform functions should have no side-effects.
- * If you need to modify properties, it is recommended to make a copy of the data, or create new object to return.
- * </div>
- *
* ### Default Transformations
*
* The `$httpProvider` provider and `$http` service expose `defaults.transformRequest` and
@@ -24153,35 +23001,26 @@ function $HttpProvider() {
*
* ## Caching
*
- * {@link ng.$http `$http`} responses are not cached by default. To enable caching, you must
- * set the config.cache value or the default cache value to TRUE or to a cache object (created
- * with {@link ng.$cacheFactory `$cacheFactory`}). If defined, the value of config.cache takes
- * precedence over the default cache value.
- *
- * In order to:
- * * cache all responses - set the default cache value to TRUE or to a cache object
- * * cache a specific response - set config.cache value to TRUE or to a cache object
+ * To enable caching, set the request configuration `cache` property to `true` (to use default
+ * cache) or to a custom cache object (built with {@link ng.$cacheFactory `$cacheFactory`}).
+ * When the cache is enabled, `$http` stores the response from the server in the specified
+ * cache. The next time the same request is made, the response is served from the cache without
+ * sending a request to the server.
*
- * If caching is enabled, but neither the default cache nor config.cache are set to a cache object,
- * then the default `$cacheFactory($http)` object is used.
+ * Note that even if the response is served from cache, delivery of the data is asynchronous in
+ * the same way that real requests are.
*
- * The default cache value can be set by updating the
- * {@link ng.$http#defaults `$http.defaults.cache`} property or the
- * {@link $httpProvider#defaults `$httpProvider.defaults.cache`} property.
+ * If there are multiple GET requests for the same URL that should be cached using the same
+ * cache, but the cache is not populated yet, only one request to the server will be made and
+ * the remaining requests will be fulfilled using the response from the first request.
*
- * When caching is enabled, {@link ng.$http `$http`} stores the response from the server using
- * the relevant cache object. The next time the same request is made, the response is returned
- * from the cache without sending a request to the server.
- *
- * Take note that:
- *
- * * Only GET and JSONP requests are cached.
- * * The cache key is the request URL including search parameters; headers are not considered.
- * * Cached responses are returned asynchronously, in the same way as responses from the server.
- * * If multiple identical requests are made using the same cache, which is not yet populated,
- * one request will be made to the server and remaining requests will return the same response.
- * * A cache-control header on the response does not affect if or how responses are cached.
+ * You can change the default cache to a new object (built with
+ * {@link ng.$cacheFactory `$cacheFactory`}) by updating the
+ * {@link ng.$http#defaults `$http.defaults.cache`} property. All requests who set
+ * their `cache` property to `true` will now use this cache object.
*
+ * If you set the default cache to `false` then only requests that specify their own custom
+ * cache object will be cached.
*
* ## Interceptors
*
@@ -24201,7 +23040,7 @@ function $HttpProvider() {
*
* There are two kinds of interceptors (and two kinds of rejection interceptors):
*
- * * `request`: interceptors get called with a http {@link $http#usage config} object. The function is free to
+ * * `request`: interceptors get called with a http `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
@@ -24303,13 +23142,13 @@ function $HttpProvider() {
*
* ### Cross Site Request Forgery (XSRF) Protection
*
- * [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is an attack technique by
- * which the attacker can trick an authenticated user into unknowingly executing actions on your
- * website. Angular provides a mechanism to counter XSRF. When performing XHR requests, the
- * $http service reads a token from a cookie (by default, `XSRF-TOKEN`) and sets it as an HTTP
- * header (`X-XSRF-TOKEN`). Since only JavaScript that runs on your domain could read the
- * cookie, your server can be assured that the XHR came from JavaScript running on your domain.
- * The header will not be set for cross-domain requests.
+ * [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is a technique by which
+ * an unauthorized site can gain your user's private data. Angular provides a mechanism
+ * to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie
+ * (by default, `XSRF-TOKEN`) and sets it as an HTTP header (`X-XSRF-TOKEN`). Since only
+ * JavaScript that runs on your domain could read the cookie, your server can be assured that
+ * the XHR came from JavaScript running on your domain. The header will not be set for
+ * cross-domain requests.
*
* To take advantage of this, your server needs to set a token in a JavaScript readable session
* cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the
@@ -24327,6 +23166,7 @@ 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:
*
@@ -24351,7 +23191,7 @@ function $HttpProvider() {
* transform function or an array of such functions. The transform function takes the http
* response body, headers and status and returns its transformed (typically deserialized) version.
* See {@link ng.$http#overriding-the-default-transformations-per-request
- * Overriding the Default Transformations}
+ * Overriding the Default TransformationjqLiks}
* - **paramSerializer** - `{string|function(Object<string,string>):string}` - A function used to
* prepare the string representation of request parameters (specified as an object).
* If specified as string, it is interpreted as function registered with the
@@ -24359,9 +23199,10 @@ function $HttpProvider() {
* by registering it as a {@link auto.$provide#service service}.
* The default serializer is the {@link $httpParamSerializer $httpParamSerializer};
* alternatively, you can use the {@link $httpParamSerializerJQLike $httpParamSerializerJQLike}
- * - **cache** – `{boolean|Object}` – A boolean value or object created with
- * {@link ng.$cacheFactory `$cacheFactory`} to enable or disable caching of the HTTP response.
- * See {@link $http#caching $http Caching} for more information.
+ * - **cache** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
+ * GET request, otherwise if a cache instance built with
+ * {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
+ * caching.
* - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise}
* that should abort the request when resolved.
* - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the
@@ -24370,9 +23211,20 @@ 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}` that will be resolved to a response object
- * when the request succeeds or fails.
+ * @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:
*
+ * - **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.<Object>} pendingRequests Array of config objects for currently pending
* requests. This is primarily meant to be used for debugging purposes.
@@ -24414,12 +23266,13 @@ function $HttpProvider() {
$scope.response = null;
$http({method: $scope.method, url: $scope.url, cache: $templateCache}).
- then(function(response) {
- $scope.status = response.status;
- $scope.data = response.data;
- }, function(response) {
- $scope.data = response.data || "Request failed";
- $scope.status = response.status;
+ success(function(data, status) {
+ $scope.status = status;
+ $scope.data = data;
+ }).
+ error(function(data, status) {
+ $scope.data = data || "Request failed";
+ $scope.status = status;
});
};
@@ -24467,14 +23320,10 @@ function $HttpProvider() {
*/
function $http(requestConfig) {
- if (!isObject(requestConfig)) {
+ if (!angular.isObject(requestConfig)) {
throw minErr('$http')('badreq', 'Http request configuration must be an object. Received: {0}', requestConfig);
}
- if (!isString(requestConfig.url)) {
- throw minErr('$http')('badreq', 'Http request configuration url must be a string. Received: {0}', requestConfig.url);
- }
-
var config = extend({
method: 'get',
transformRequest: defaults.transformRequest,
@@ -24528,36 +23377,34 @@ function $HttpProvider() {
promise = promise.then(thenFn, rejectFn);
}
- if (useLegacyPromise) {
- promise.success = function(fn) {
- assertArgFn(fn, 'fn');
+ promise.success = function(fn) {
+ assertArgFn(fn, 'fn');
- promise.then(function(response) {
- fn(response.data, response.status, response.headers, config);
- });
- return promise;
- };
+ promise.then(function(response) {
+ fn(response.data, response.status, response.headers, config);
+ });
+ return promise;
+ };
- promise.error = function(fn) {
- assertArgFn(fn, 'fn');
+ promise.error = function(fn) {
+ assertArgFn(fn, 'fn');
- promise.then(null, function(response) {
- fn(response.data, response.status, response.headers, config);
- });
- return promise;
- };
- } else {
- promise.success = $httpMinErrLegacyFn('success');
- promise.error = $httpMinErrLegacyFn('error');
- }
+ promise.then(null, function(response) {
+ fn(response.data, response.status, response.headers, config);
+ });
+ return promise;
+ };
return promise;
function transformResponse(response) {
// make a copy since the response must be cacheable
var resp = extend({}, response);
- resp.data = transformData(response.data, response.headers, response.status,
- config.transformResponse);
+ if (!response.data) {
+ resp.data = response.data;
+ } else {
+ resp.data = transformData(response.data, response.headers, response.status, config.transformResponse);
+ }
return (isSuccess(response.status))
? resp
: $q.reject(resp);
@@ -24587,7 +23434,7 @@ function $HttpProvider() {
defHeaders = extend({}, defHeaders.common, defHeaders[lowercase(config.method)]);
- // using for-in instead of forEach to avoid unnecessary iteration after header has been found
+ // using for-in instead of forEach to avoid unecessary iteration after header has been found
defaultHeadersIteration:
for (defHeaderName in defHeaders) {
lowercaseDefHeaderName = lowercase(defHeaderName);
@@ -24835,8 +23682,8 @@ function $HttpProvider() {
* Resolves the raw $http promise.
*/
function resolvePromise(response, status, headers, statusText) {
- //status: HTTP response status code, 0, -1 (aborted by timeout / promise)
- status = status >= -1 ? status : 0;
+ // normalize internal statuses to 0
+ status = Math.max(status, 0);
(isSuccess(status) ? deferred.resolve : deferred.reject)({
data: response,
@@ -24867,33 +23714,8 @@ function $HttpProvider() {
}];
}
-/**
- * @ngdoc service
- * @name $xhrFactory
- *
- * @description
- * Factory function used to create XMLHttpRequest objects.
- *
- * Replace or decorate this service to create your own custom XMLHttpRequest objects.
- *
- * ```
- * angular.module('myApp', [])
- * .factory('$xhrFactory', function() {
- * return function createXhr(method, url) {
- * return new window.XMLHttpRequest({mozSystem: true});
- * };
- * });
- * ```
- *
- * @param {string} method HTTP method of the request (GET, POST, PUT, ..)
- * @param {string} url URL of the request.
- */
-function $xhrFactoryProvider() {
- this.$get = function() {
- return function createXhr() {
- return new window.XMLHttpRequest();
- };
- };
+function createXhr() {
+ return new window.XMLHttpRequest();
}
/**
@@ -24901,7 +23723,6 @@ function $xhrFactoryProvider() {
* @name $httpBackend
* @requires $window
* @requires $document
- * @requires $xhrFactory
*
* @description
* HTTP backend used by the {@link ng.$http service} that delegates to
@@ -24914,8 +23735,8 @@ function $xhrFactoryProvider() {
* $httpBackend} which can be trained with responses.
*/
function $HttpBackendProvider() {
- this.$get = ['$browser', '$window', '$document', '$xhrFactory', function($browser, $window, $document, $xhrFactory) {
- return createHttpBackend($browser, $xhrFactory, $browser.defer, $window.angular.callbacks, $document[0]);
+ this.$get = ['$browser', '$window', '$document', function($browser, $window, $document) {
+ return createHttpBackend($browser, createXhr, $browser.defer, $window.angular.callbacks, $document[0]);
}];
}
@@ -24939,7 +23760,7 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
});
} else {
- var xhr = createXhr(method, url);
+ var xhr = createXhr();
xhr.open(method, url, true);
forEach(headers, function(value, key) {
@@ -24951,7 +23772,7 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
xhr.onload = function requestLoaded() {
var statusText = xhr.statusText || '';
- // responseText is the old-school way of retrieving response (supported by IE9)
+ // responseText is the old-school way of retrieving response (supported by IE8 & 9)
// response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
var response = ('response' in xhr) ? xhr.response : xhr.responseText;
@@ -25002,7 +23823,7 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
}
}
- xhr.send(isUndefined(post) ? null : post);
+ xhr.send(post);
}
if (timeout > 0) {
@@ -25019,7 +23840,7 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
function completeRequest(callback, status, response, headersString, statusText) {
// cancel timeout and subsequent timeout promise resolution
- if (isDefined(timeoutId)) {
+ if (timeoutId !== undefined) {
$browserDefer.cancel(timeoutId);
}
jsonpDone = xhr = null;
@@ -25086,16 +23907,8 @@ $interpolateMinErr.interr = function(text, err) {
*
* Used for configuring the interpolation markup. Defaults to `{{` and `}}`.
*
- * <div class="alert alert-danger">
- * This feature is sometimes used to mix different markup languages, e.g. to wrap an Angular
- * template within a Python Jinja template (or any other template language). Mixing templating
- * languages is **very dangerous**. The embedding template language will not safely escape Angular
- * expressions, so any user-controlled values in the template will cause Cross Site Scripting (XSS)
- * security bugs!
- * </div>
- *
* @example
-<example name="custom-interpolation-markup" module="customInterpolationApp">
+<example module="customInterpolationApp">
<file name="index.html">
<script>
var customInterpolationApp = angular.module('customInterpolationApp', []);
@@ -25110,7 +23923,7 @@ $interpolateMinErr.interr = function(text, err) {
this.label = "This binding is brought you by // interpolation symbols.";
});
</script>
-<div ng-controller="DemoController as demo">
+<div ng-app="App" ng-controller="DemoController as demo">
//demo.label//
</div>
</file>
@@ -25194,15 +24007,6 @@ function $InterpolateProvider() {
return value;
}
- //TODO: this is the same as the constantWatchDelegate in parse.js
- function constantWatchDelegate(scope, listener, objectEquality, constantInterp) {
- var unwatch;
- return unwatch = scope.$watch(function constantInterpolateWatch(scope) {
- unwatch();
- return constantInterp(scope);
- }, listener, objectEquality);
- }
-
/**
* @ngdoc service
* @name $interpolate
@@ -25222,7 +24026,7 @@ function $InterpolateProvider() {
* ```js
* var $interpolate = ...; // injected
* var exp = $interpolate('Hello {{name | uppercase}}!');
- * expect(exp({name:'Angular'})).toEqual('Hello ANGULAR!');
+ * expect(exp({name:'Angular'}).toEqual('Hello ANGULAR!');
* ```
*
* `$interpolate` takes an optional fourth argument, `allOrNothing`. If `allOrNothing` is
@@ -25298,19 +24102,6 @@ function $InterpolateProvider() {
* - `context`: evaluation context for all expressions embedded in the interpolated text
*/
function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) {
- // Provide a quick exit and simplified result function for text with no interpolation
- if (!text.length || text.indexOf(startSymbol) === -1) {
- var constantInterp;
- if (!mustHaveExpression) {
- var unescapedText = unescapeText(text);
- constantInterp = valueFn(unescapedText);
- constantInterp.exp = text;
- constantInterp.expressions = [];
- constantInterp.$$watchDelegate = constantWatchDelegate;
- }
- return constantInterp;
- }
-
allOrNothing = !!allOrNothing;
var startIndex,
endIndex,
@@ -25447,8 +24238,8 @@ function $InterpolateProvider() {
}
function $IntervalProvider() {
- this.$get = ['$rootScope', '$window', '$q', '$$q', '$browser',
- function($rootScope, $window, $q, $$q, $browser) {
+ this.$get = ['$rootScope', '$window', '$q', '$$q',
+ function($rootScope, $window, $q, $$q) {
var intervals = {};
@@ -25589,12 +24380,11 @@ function $IntervalProvider() {
count = isDefined(count) ? count : 0;
+ promise.then(null, null, (!hasParams) ? fn : function() {
+ fn.apply(null, args);
+ });
+
promise.$$intervalId = setInterval(function tick() {
- if (skipApply) {
- $browser.defer(callback);
- } else {
- $rootScope.$evalAsync(callback);
- }
deferred.notify(iteration++);
if (count > 0 && iteration >= count) {
@@ -25610,14 +24400,6 @@ function $IntervalProvider() {
intervals[promise.$$intervalId] = deferred;
return promise;
-
- function callback() {
- if (!hasParams) {
- fn(iteration);
- } else {
- fn.apply(null, args);
- }
- }
}
@@ -25628,7 +24410,7 @@ function $IntervalProvider() {
* @description
* Cancels a task associated with the `promise`.
*
- * @param {Promise=} promise returned by the `$interval` function.
+ * @param {promise} promise returned by the `$interval` function.
* @returns {boolean} Returns `true` if the task was successfully canceled.
*/
interval.cancel = function(promise) {
@@ -25655,6 +24437,75 @@ function $IntervalProvider() {
*
* * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`)
*/
+function $LocaleProvider() {
+ this.$get = function() {
+ return {
+ id: 'en-us',
+
+ NUMBER_FORMATS: {
+ DECIMAL_SEP: '.',
+ GROUP_SEP: ',',
+ PATTERNS: [
+ { // Decimal Pattern
+ minInt: 1,
+ minFrac: 0,
+ maxFrac: 3,
+ posPre: '',
+ posSuf: '',
+ negPre: '-',
+ negSuf: '',
+ gSize: 3,
+ lgSize: 3
+ },{ //Currency Pattern
+ minInt: 1,
+ minFrac: 2,
+ maxFrac: 2,
+ posPre: '\u00A4',
+ posSuf: '',
+ negPre: '(\u00A4',
+ negSuf: ')',
+ gSize: 3,
+ lgSize: 3
+ }
+ ],
+ CURRENCY_SYM: '$'
+ },
+
+ DATETIME_FORMATS: {
+ MONTH:
+ 'January,February,March,April,May,June,July,August,September,October,November,December'
+ .split(','),
+ SHORTMONTH: 'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec'.split(','),
+ DAY: 'Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday'.split(','),
+ SHORTDAY: 'Sun,Mon,Tue,Wed,Thu,Fri,Sat'.split(','),
+ AMPMS: ['AM','PM'],
+ medium: 'MMM d, y h:mm:ss a',
+ 'short': 'M/d/yy h:mm a',
+ fullDate: 'EEEE, MMMM d, y',
+ longDate: 'MMMM d, y',
+ mediumDate: 'MMM d, y',
+ shortDate: 'M/d/yy',
+ mediumTime: 'h:mm:ss a',
+ shortTime: 'h:mm a',
+ ERANAMES: [
+ "Before Christ",
+ "Anno Domini"
+ ],
+ ERAS: [
+ "BC",
+ "AD"
+ ]
+ },
+
+ pluralCat: function(num) {
+ if (num === 1) {
+ return 'one';
+ }
+ return 'other';
+ }
+ };
+ };
+}
var PATH_MATCH = /^([^\?#]*)(\?([^#]*))?(#(.*))?$/,
DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21};
@@ -25745,12 +24596,12 @@ function serverBase(url) {
*
* @constructor
* @param {string} appBase application base URL
- * @param {string} appBaseNoFile application base URL stripped of any filename
* @param {string} basePrefix url path prefix
*/
-function LocationHtml5Url(appBase, appBaseNoFile, basePrefix) {
+function LocationHtml5Url(appBase, basePrefix) {
this.$$html5 = true;
basePrefix = basePrefix || '';
+ var appBaseNoFile = stripFile(appBase);
parseAbsoluteUrl(appBase, this);
@@ -25797,14 +24648,14 @@ function LocationHtml5Url(appBase, appBaseNoFile, basePrefix) {
var appUrl, prevAppUrl;
var rewrittenUrl;
- if (isDefined(appUrl = beginsWith(appBase, url))) {
+ if ((appUrl = beginsWith(appBase, url)) !== undefined) {
prevAppUrl = appUrl;
- if (isDefined(appUrl = beginsWith(basePrefix, appUrl))) {
+ if ((appUrl = beginsWith(basePrefix, appUrl)) !== undefined) {
rewrittenUrl = appBaseNoFile + (beginsWith('/', appUrl) || appUrl);
} else {
rewrittenUrl = appBase + prevAppUrl;
}
- } else if (isDefined(appUrl = beginsWith(appBaseNoFile, url))) {
+ } else if ((appUrl = beginsWith(appBaseNoFile, url)) !== undefined) {
rewrittenUrl = appBaseNoFile + appUrl;
} else if (appBaseNoFile == url + '/') {
rewrittenUrl = appBaseNoFile;
@@ -25824,10 +24675,10 @@ function LocationHtml5Url(appBase, appBaseNoFile, basePrefix) {
*
* @constructor
* @param {string} appBase application base URL
- * @param {string} appBaseNoFile application base URL stripped of any filename
* @param {string} hashPrefix hashbang prefix
*/
-function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) {
+function LocationHashbangUrl(appBase, hashPrefix) {
+ var appBaseNoFile = stripFile(appBase);
parseAbsoluteUrl(appBase, this);
@@ -25936,13 +24787,14 @@ function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) {
*
* @constructor
* @param {string} appBase application base URL
- * @param {string} appBaseNoFile application base URL stripped of any filename
* @param {string} hashPrefix hashbang prefix
*/
-function LocationHashbangInHtml5Url(appBase, appBaseNoFile, hashPrefix) {
+function LocationHashbangInHtml5Url(appBase, hashPrefix) {
this.$$html5 = true;
LocationHashbangUrl.apply(this, arguments);
+ var appBaseNoFile = stripFile(appBase);
+
this.$$parseLinkUrl = function(url, relHref) {
if (relHref && relHref[0] === '#') {
// special case for links to hash fragments:
@@ -25972,7 +24824,7 @@ function LocationHashbangInHtml5Url(appBase, appBaseNoFile, hashPrefix) {
hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
- // include hashPrefix in $$absUrl when $$url is empty so IE9 does not reload page because of removal of '#'
+ // include hashPrefix in $$absUrl when $$url is empty so IE8 & 9 do not reload page because of removal of '#'
this.$$absUrl = appBase + hashPrefix + this.$$url;
};
@@ -26230,9 +25082,9 @@ var locationPrototype = {
* @description
* This method is getter / setter.
*
- * Returns the hash fragment when called without any parameters.
+ * Return hash fragment when called without any parameter.
*
- * Changes the hash fragment when called with a parameter and returns `$location`.
+ * Change hash fragment when called with parameter and return `$location`.
*
*
* ```js
@@ -26253,8 +25105,8 @@ var locationPrototype = {
* @name $location#replace
*
* @description
- * If called, all changes to $location during the current `$digest` will replace the current history
- * record, instead of adding a new one.
+ * If called, all changes to $location during current `$digest` will be replacing current history
+ * record, instead of adding new one.
*/
replace: function() {
this.$$replace = true;
@@ -26481,9 +25333,7 @@ function $LocationProvider() {
appBase = stripHash(initialUrl);
LocationMode = LocationHashbangUrl;
}
- var appBaseNoFile = stripFile(appBase);
-
- $location = new LocationMode(appBase, appBaseNoFile, '#' + hashPrefix);
+ $location = new LocationMode(appBase, '#' + hashPrefix);
$location.$$parseLinkUrl(initialUrl, initialUrl);
$location.$$state = $browser.state();
@@ -26563,18 +25413,11 @@ function $LocationProvider() {
// update $location when $browser url changes
$browser.onUrlChange(function(newUrl, newState) {
-
- if (isUndefined(beginsWith(appBaseNoFile, newUrl))) {
- // If we are navigating outside of the app then force a reload
- $window.location.href = newUrl;
- return;
- }
-
$rootScope.$evalAsync(function() {
var oldUrl = $location.absUrl();
var oldState = $location.$$state;
var defaultPrevented;
- newUrl = trimEmptyHash(newUrl);
+
$location.$$parse(newUrl);
$location.$$state = newState;
@@ -26857,24 +25700,6 @@ function ensureSafeMemberName(name, fullExpression) {
return name;
}
-function getStringValue(name) {
- // Property names must be strings. This means that non-string objects cannot be used
- // as keys in an object. Any non-string object, including a number, is typecasted
- // into a string via the toString method.
- // -- MDN, https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Property_accessors#Property_names
- //
- // So, to ensure that we are checking the same `name` that JavaScript would use, we cast it
- // to a string. It's not always possible. If `name` is an object and its `toString` method is
- // 'broken' (doesn't return a string, isn't a function, etc.), an error will be thrown:
- //
- // TypeError: Cannot convert object to primitive value
- //
- // For performance reasons, we don't catch this error here and allow it to propagate up the call
- // stack. Note that you'll get the same error in JavaScript if you try to access a property using
- // such a 'broken' object as a key.
- return name + '';
-}
-
function ensureSafeObject(obj, fullExpression) {
// nifty check if obj is Function that is fast and works across iframes and other contexts
if (obj) {
@@ -26920,16 +25745,6 @@ function ensureSafeFunction(obj, fullExpression) {
}
}
-function ensureSafeAssignContext(obj, fullExpression) {
- if (obj) {
- if (obj === (0).constructor || obj === (false).constructor || obj === ''.constructor ||
- obj === {}.constructor || obj === [].constructor || obj === Function.constructor) {
- throw $parseMinErr('isecaf',
- 'Assigning to a constructor is disallowed! Expression: {0}', fullExpression);
- }
- }
-}
-
var OPERATORS = createMap();
forEach('+ - * / % === !== == != < > <= >= && || ! = |'.split(' '), function(operator) { OPERATORS[operator] = true; });
var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'};
@@ -27133,7 +25948,6 @@ AST.ArrayExpression = 'ArrayExpression';
AST.Property = 'Property';
AST.ObjectExpression = 'ObjectExpression';
AST.ThisExpression = 'ThisExpression';
-AST.LocalsExpression = 'LocalsExpression';
// Internal use only
AST.NGValueParameter = 'NGValueParameter';
@@ -27272,10 +26086,8 @@ AST.prototype = {
primary = this.arrayDeclaration();
} else if (this.expect('{')) {
primary = this.object();
- } else if (this.selfReferential.hasOwnProperty(this.peek().text)) {
- primary = copy(this.selfReferential[this.consume().text]);
- } else if (this.options.literals.hasOwnProperty(this.peek().text)) {
- primary = { type: AST.Literal, value: this.options.literals[this.consume().text]};
+ } else if (this.constants.hasOwnProperty(this.peek().text)) {
+ primary = copy(this.constants[this.consume().text]);
} else if (this.peek().identifier) {
primary = this.identifier();
} else if (this.peek().constant) {
@@ -27427,9 +26239,16 @@ AST.prototype = {
return false;
},
- selfReferential: {
- 'this': {type: AST.ThisExpression },
- '$locals': {type: AST.LocalsExpression }
+
+ /* `undefined` is not a constant, it is an identifier,
+ * but using it as an identifier is not supported
+ */
+ constants: {
+ 'true': { type: AST.Literal, value: true },
+ 'false': { type: AST.Literal, value: false },
+ 'null': { type: AST.Literal, value: null },
+ 'undefined': {type: AST.Literal, value: undefined },
+ 'this': {type: AST.ThisExpression }
}
};
@@ -27549,10 +26368,6 @@ function findConstantAndWatchExpressions(ast, $filter) {
ast.constant = false;
ast.toWatch = [];
break;
- case AST.LocalsExpression:
- ast.constant = false;
- ast.toWatch = [];
- break;
}
}
@@ -27611,7 +26426,6 @@ ASTCompiler.prototype = {
this.state.computing = 'assign';
var result = this.nextId();
this.recurse(assignable, result);
- this.return_(result);
extra = 'fn.assign=' + this.generateFunction('assign', 's,v,l');
}
var toWatch = getInputs(ast.body);
@@ -27644,8 +26458,6 @@ ASTCompiler.prototype = {
'ensureSafeMemberName',
'ensureSafeObject',
'ensureSafeFunction',
- 'getStringValue',
- 'ensureSafeAssignContext',
'ifDefined',
'plus',
'text',
@@ -27654,8 +26466,6 @@ ASTCompiler.prototype = {
ensureSafeMemberName,
ensureSafeObject,
ensureSafeFunction,
- getStringValue,
- ensureSafeAssignContext,
ifDefined,
plusFn,
expression);
@@ -27796,13 +26606,9 @@ ASTCompiler.prototype = {
intoId = intoId || this.nextId();
self.recurse(ast.object, left, undefined, function() {
self.if_(self.notNull(left), function() {
- if (create && create !== 1) {
- self.addEnsureSafeAssignContext(left);
- }
if (ast.computed) {
right = self.nextId();
self.recurse(ast.property, right);
- self.getStringValue(right);
self.addEnsureSafeMemberName(right);
if (create && create !== 1) {
self.if_(self.not(self.computedMember(left, right)), self.lazyAssign(self.computedMember(left, right), '{}'));
@@ -27880,13 +26686,12 @@ ASTCompiler.prototype = {
right = this.nextId();
left = {};
if (!isAssignable(ast.left)) {
- throw $parseMinErr('lval', 'Trying to assign a value to a non l-value');
+ throw $parseMinErr('lval', 'Trying to assing a value to a non l-value');
}
this.recurse(ast.left, undefined, left, function() {
self.if_(self.notNull(left.context), function() {
self.recurse(ast.right, right);
self.addEnsureSafeObject(self.member(left.context, left.name, left.computed));
- self.addEnsureSafeAssignContext(left.context);
expression = self.member(left.context, left.name, left.computed) + ast.operator + right;
self.assign(intoId, expression);
recursionFn(intoId || expression);
@@ -27922,10 +26727,6 @@ ASTCompiler.prototype = {
this.assign(intoId, 's');
recursionFn('s');
break;
- case AST.LocalsExpression:
- this.assign(intoId, 'l');
- recursionFn('l');
- break;
case AST.NGValueParameter:
this.assign(intoId, 'v');
recursionFn('v');
@@ -28016,10 +26817,6 @@ ASTCompiler.prototype = {
this.current().body.push(this.ensureSafeFunction(item), ';');
},
- addEnsureSafeAssignContext: function(item) {
- this.current().body.push(this.ensureSafeAssignContext(item), ';');
- },
-
ensureSafeObject: function(item) {
return 'ensureSafeObject(' + item + ',text)';
},
@@ -28032,14 +26829,6 @@ ASTCompiler.prototype = {
return 'ensureSafeFunction(' + item + ',text)';
},
- getStringValue: function(item) {
- this.assign(item, 'getStringValue(' + item + ')');
- },
-
- ensureSafeAssignContext: function(item) {
- return 'ensureSafeAssignContext(' + item + ',text)';
- },
-
lazyRecurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
var self = this;
return function() {
@@ -28117,7 +26906,7 @@ ASTInterpreter.prototype = {
forEach(ast.body, function(expression) {
expressions.push(self.recurse(expression.expression));
});
- var fn = ast.body.length === 0 ? noop :
+ var fn = ast.body.length === 0 ? function() {} :
ast.body.length === 1 ? expressions[0] :
function(scope, locals) {
var lastValue;
@@ -28217,7 +27006,6 @@ ASTInterpreter.prototype = {
var lhs = left(scope, locals, assign, inputs);
var rhs = right(scope, locals, assign, inputs);
ensureSafeObject(lhs.value, self.expression);
- ensureSafeAssignContext(lhs.context);
lhs.context[lhs.name] = rhs;
return context ? {value: rhs} : rhs;
};
@@ -28253,12 +27041,8 @@ ASTInterpreter.prototype = {
return function(scope) {
return context ? {value: scope} : scope;
};
- case AST.LocalsExpression:
- return function(scope, locals) {
- return context ? {value: locals} : locals;
- };
case AST.NGValueParameter:
- return function(scope, locals, assign) {
+ return function(scope, locals, assign, inputs) {
return context ? {value: assign} : assign;
};
}
@@ -28419,13 +27203,9 @@ ASTInterpreter.prototype = {
var value;
if (lhs != null) {
rhs = right(scope, locals, assign, inputs);
- rhs = getStringValue(rhs);
ensureSafeMemberName(rhs, expression);
- if (create && create !== 1) {
- ensureSafeAssignContext(lhs);
- if (lhs && !(lhs[rhs])) {
- lhs[rhs] = {};
- }
+ if (create && create !== 1 && lhs && !(lhs[rhs])) {
+ lhs[rhs] = {};
}
value = lhs[rhs];
ensureSafeObject(value, expression);
@@ -28440,11 +27220,8 @@ ASTInterpreter.prototype = {
nonComputedMember: function(left, right, expensiveChecks, context, create, expression) {
return function(scope, locals, assign, inputs) {
var lhs = left(scope, locals, assign, inputs);
- if (create && create !== 1) {
- ensureSafeAssignContext(lhs);
- if (lhs && !(lhs[right])) {
- lhs[right] = {};
- }
+ if (create && create !== 1 && lhs && !(lhs[right])) {
+ lhs[right] = {};
}
var value = lhs != null ? lhs[right] : undefined;
if (expensiveChecks || isPossiblyDangerousMemberName(right)) {
@@ -28472,7 +27249,7 @@ var Parser = function(lexer, $filter, options) {
this.lexer = lexer;
this.$filter = $filter;
this.options = options;
- this.ast = new AST(lexer, options);
+ this.ast = new AST(this.lexer);
this.astCompiler = options.csp ? new ASTInterpreter(this.ast, $filter) :
new ASTCompiler(this.ast, $filter);
};
@@ -28485,6 +27262,32 @@ Parser.prototype = {
}
};
+//////////////////////////////////////////////////
+// Parser helper functions
+//////////////////////////////////////////////////
+
+function setter(obj, path, setValue, fullExp) {
+ ensureSafeObject(obj, fullExp);
+
+ var element = path.split('.'), key;
+ for (var i = 0; element.length > 1; i++) {
+ key = ensureSafeMemberName(element.shift(), fullExp);
+ var propertyObj = ensureSafeObject(obj[key], fullExp);
+ if (!propertyObj) {
+ propertyObj = {};
+ obj[key] = propertyObj;
+ }
+ obj = propertyObj;
+ }
+ key = ensureSafeMemberName(element.shift(), fullExp);
+ ensureSafeObject(obj[key], fullExp);
+ obj[key] = setValue;
+ return setValue;
+}
+
+var getterFnCacheDefault = createMap();
+var getterFnCacheExpensive = createMap();
+
function isPossiblyDangerousMemberName(name) {
return name == 'constructor';
}
@@ -28549,53 +27352,20 @@ function getValueOf(value) {
function $ParseProvider() {
var cacheDefault = createMap();
var cacheExpensive = createMap();
- var literals = {
- 'true': true,
- 'false': false,
- 'null': null,
- 'undefined': undefined
- };
-
- /**
- * @ngdoc method
- * @name $parseProvider#addLiteral
- * @description
- *
- * Configure $parse service to add literal values that will be present as literal at expressions.
- *
- * @param {string} literalName Token for the literal value. The literal name value must be a valid literal name.
- * @param {*} literalValue Value for this literal. All literal values must be primitives or `undefined`.
- *
- **/
- this.addLiteral = function(literalName, literalValue) {
- literals[literalName] = literalValue;
- };
- this.$get = ['$filter', function($filter) {
- var noUnsafeEval = csp().noUnsafeEval;
+ this.$get = ['$filter', '$sniffer', function($filter, $sniffer) {
var $parseOptions = {
- csp: noUnsafeEval,
- expensiveChecks: false,
- literals: copy(literals)
+ csp: $sniffer.csp,
+ expensiveChecks: false
},
$parseOptionsExpensive = {
- csp: noUnsafeEval,
- expensiveChecks: true,
- literals: copy(literals)
+ csp: $sniffer.csp,
+ expensiveChecks: true
};
- var runningChecksEnabled = false;
-
- $parse.$$runningExpensiveChecks = function() {
- return runningChecksEnabled;
- };
-
- return $parse;
- function $parse(exp, interceptorFn, expensiveChecks) {
+ return function $parse(exp, interceptorFn, expensiveChecks) {
var parsedExpression, oneTime, cacheKey;
- expensiveChecks = expensiveChecks || runningChecksEnabled;
-
switch (typeof exp) {
case 'string':
exp = exp.trim();
@@ -28621,9 +27391,6 @@ function $ParseProvider() {
} else if (parsedExpression.inputs) {
parsedExpression.$$watchDelegate = inputsWatchDelegate;
}
- if (expensiveChecks) {
- parsedExpression = expensiveChecksInterceptor(parsedExpression);
- }
cache[cacheKey] = parsedExpression;
}
return addInterceptor(parsedExpression, interceptorFn);
@@ -28632,33 +27399,9 @@ function $ParseProvider() {
return addInterceptor(exp, interceptorFn);
default:
- return addInterceptor(noop, interceptorFn);
- }
- }
-
- function expensiveChecksInterceptor(fn) {
- if (!fn) return fn;
- expensiveCheckFn.$$watchDelegate = fn.$$watchDelegate;
- expensiveCheckFn.assign = expensiveChecksInterceptor(fn.assign);
- expensiveCheckFn.constant = fn.constant;
- expensiveCheckFn.literal = fn.literal;
- for (var i = 0; fn.inputs && i < fn.inputs.length; ++i) {
- fn.inputs[i] = expensiveChecksInterceptor(fn.inputs[i]);
+ return noop;
}
- expensiveCheckFn.inputs = fn.inputs;
-
- return expensiveCheckFn;
-
- function expensiveCheckFn(scope, locals, assign, inputs) {
- var expensiveCheckOldValue = runningChecksEnabled;
- runningChecksEnabled = true;
- try {
- return fn(scope, locals, assign, inputs);
- } finally {
- runningChecksEnabled = expensiveCheckOldValue;
- }
- }
- }
+ };
function expressionInputDirtyCheck(newValue, oldValueOfValue) {
@@ -28775,22 +27518,25 @@ function $ParseProvider() {
function constantWatchDelegate(scope, listener, objectEquality, parsedExpression) {
var unwatch;
return unwatch = scope.$watch(function constantWatch(scope) {
- unwatch();
return parsedExpression(scope);
- }, listener, objectEquality);
+ }, function constantListener(value, old, scope) {
+ if (isFunction(listener)) {
+ listener.apply(this, arguments);
+ }
+ unwatch();
+ }, objectEquality);
}
function addInterceptor(parsedExpression, interceptorFn) {
if (!interceptorFn) return parsedExpression;
var watchDelegate = parsedExpression.$$watchDelegate;
- var useInputs = false;
var regularWatch =
watchDelegate !== oneTimeLiteralWatchDelegate &&
watchDelegate !== oneTimeWatchDelegate;
var fn = regularWatch ? function regularInterceptedExpression(scope, locals, assign, inputs) {
- var value = useInputs && inputs ? inputs[0] : parsedExpression(scope, locals, assign, inputs);
+ var value = parsedExpression(scope, locals, assign, inputs);
return interceptorFn(value, scope, locals);
} : function oneTimeInterceptedExpression(scope, locals, assign, inputs) {
var value = parsedExpression(scope, locals, assign, inputs);
@@ -28808,7 +27554,6 @@ function $ParseProvider() {
// If there is an interceptor, but no watchDelegate then treat the interceptor like
// we treat filters - it is assumed to be a pure function unless flagged with $stateful
fn.$$watchDelegate = inputsWatchDelegate;
- useInputs = !parsedExpression.inputs;
fn.inputs = parsedExpression.inputs ? parsedExpression.inputs : [parsedExpression];
}
@@ -28830,15 +27575,15 @@ function $ParseProvider() {
* [Kris Kowal's Q](https://github.com/kriskowal/q).
*
* $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred
- * implementations, and the other which resembles ES6 (ES2015) promises to some degree.
+ * implementations, and the other which resembles ES6 promises to some degree.
*
* # $q constructor
*
* The streamlined ES6 style promise is essentially just using $q as a constructor which takes a `resolver`
- * function as the first argument. This is similar to the native Promise implementation from ES6,
+ * function as the first argument. This is similar to the native Promise implementation from ES6 Harmony,
* see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
*
- * While the constructor-style use is supported, not all of the supporting methods from ES6 promises are
+ * While the constructor-style use is supported, not all of the supporting methods from ES6 Harmony promises are
* available yet.
*
* It can be used like so:
@@ -28870,8 +27615,6 @@ function $ParseProvider() {
*
* Note: progress/notify callbacks are not currently supported via the ES6-style interface.
*
- * Note: unlike ES6 behavior, an exception thrown in the constructor function will NOT implicitly reject the promise.
- *
* However, the more traditional CommonJS-style usage is still available, and documented below.
*
* [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an
@@ -29003,7 +27746,7 @@ function $ParseProvider() {
* - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
* all the important functionality needed for common async tasks.
*
- * # Testing
+ * # Testing
*
* ```js
* it('should simulate promise', inject(function($q, $rootScope) {
@@ -29060,6 +27803,18 @@ function $$QProvider() {
*/
function qFactory(nextTick, exceptionHandler) {
var $qMinErr = minErr('$q', TypeError);
+ function callOnce(self, resolveFn, rejectFn) {
+ var called = false;
+ function wrap(fn) {
+ return function(value) {
+ if (called) return;
+ called = true;
+ fn.call(self, value);
+ };
+ }
+
+ return [wrap(resolveFn), wrap(rejectFn)];
+ }
/**
* @ngdoc method
@@ -29072,23 +27827,15 @@ function qFactory(nextTick, exceptionHandler) {
* @returns {Deferred} Returns a new instance of deferred.
*/
var defer = function() {
- var d = new Deferred();
- //Necessary to support unbound execution :/
- d.resolve = simpleBind(d, d.resolve);
- d.reject = simpleBind(d, d.reject);
- d.notify = simpleBind(d, d.notify);
- return d;
+ return new Deferred();
};
function Promise() {
this.$$state = { status: 0 };
}
- extend(Promise.prototype, {
+ Promise.prototype = {
then: function(onFulfilled, onRejected, progressBack) {
- if (isUndefined(onFulfilled) && isUndefined(onRejected) && isUndefined(progressBack)) {
- return this;
- }
var result = new Deferred();
this.$$state.pending = this.$$state.pending || [];
@@ -29109,7 +27856,7 @@ function qFactory(nextTick, exceptionHandler) {
return handleCallback(error, false, callback);
}, progressBack);
}
- });
+ };
//Faster, more basic than angular.bind http://jsperf.com/angular-bind-vs-custom-vs-native
function simpleBind(context, fn) {
@@ -29150,9 +27897,13 @@ function qFactory(nextTick, exceptionHandler) {
function Deferred() {
this.promise = new Promise();
+ //Necessary to support unbound execution :/
+ this.resolve = simpleBind(this, this.resolve);
+ this.reject = simpleBind(this, this.reject);
+ this.notify = simpleBind(this, this.notify);
}
- extend(Deferred.prototype, {
+ Deferred.prototype = {
resolve: function(val) {
if (this.promise.$$state.status) return;
if (val === this.promise) {
@@ -29167,34 +27918,23 @@ function qFactory(nextTick, exceptionHandler) {
},
$$resolve: function(val) {
- var then;
- var that = this;
- var done = false;
+ var then, fns;
+
+ fns = callOnce(this, this.$$resolve, this.$$reject);
try {
if ((isObject(val) || isFunction(val))) then = val && val.then;
if (isFunction(then)) {
this.promise.$$state.status = -1;
- then.call(val, resolvePromise, rejectPromise, simpleBind(this, this.notify));
+ then.call(val, fns[0], fns[1], this.notify);
} else {
this.promise.$$state.value = val;
this.promise.$$state.status = 1;
scheduleProcessQueue(this.promise.$$state);
}
} catch (e) {
- rejectPromise(e);
+ fns[1](e);
exceptionHandler(e);
}
-
- function resolvePromise(val) {
- if (done) return;
- done = true;
- that.$$resolve(val);
- }
- function rejectPromise(val) {
- if (done) return;
- done = true;
- that.$$reject(val);
- }
},
reject: function(reason) {
@@ -29226,7 +27966,7 @@ function qFactory(nextTick, exceptionHandler) {
});
}
}
- });
+ };
/**
* @ngdoc method
@@ -29309,9 +28049,6 @@ function qFactory(nextTick, exceptionHandler) {
* the promise comes from a source that can't be trusted.
*
* @param {*} value Value or a promise
- * @param {Function=} successCallback
- * @param {Function=} errorCallback
- * @param {Function=} progressCallback
* @returns {Promise} Returns a promise of the passed value or promise
*/
@@ -29331,9 +28068,6 @@ function qFactory(nextTick, exceptionHandler) {
* Alias of {@link ng.$q#when when} to maintain naming consistency with ES6.
*
* @param {*} value Value or a promise
- * @param {Function=} successCallback
- * @param {Function=} errorCallback
- * @param {Function=} progressCallback
* @returns {Promise} Returns a promise of the passed value or promise
*/
var resolve = when;
@@ -29383,6 +28117,11 @@ function qFactory(nextTick, exceptionHandler) {
throw $qMinErr('norslvr', "Expected resolverFn, got '{0}'", resolver);
}
+ if (!(this instanceof Q)) {
+ // More useful when $Q is the Promise itself.
+ return new Q(resolver);
+ }
+
var deferred = new Deferred();
function resolveFn(value) {
@@ -29398,10 +28137,6 @@ function qFactory(nextTick, exceptionHandler) {
return deferred.promise;
};
- // Let's make the instanceof operator work for promises, so that
- // `new $q(fn) instanceof $q` would evaluate to true.
- $Q.prototype = Promise.prototype;
-
$Q.defer = defer;
$Q.reject = reject;
$Q.when = when;
@@ -29421,7 +28156,7 @@ function $$RAFProvider() { //rAF
$window.webkitCancelRequestAnimationFrame;
var rafSupported = !!requestAnimationFrame;
- var raf = rafSupported
+ var rafFn = rafSupported
? function(fn) {
var id = requestAnimationFrame(fn);
return function() {
@@ -29435,9 +28170,47 @@ function $$RAFProvider() { //rAF
};
};
- raf.supported = rafSupported;
+ queueFn.supported = rafSupported;
+
+ var cancelLastRAF;
+ var taskCount = 0;
+ var taskQueue = [];
+ return queueFn;
+
+ function flush() {
+ for (var i = 0; i < taskQueue.length; i++) {
+ var task = taskQueue[i];
+ if (task) {
+ taskQueue[i] = null;
+ task();
+ }
+ }
+ taskCount = taskQueue.length = 0;
+ }
+
+ function queueFn(asyncFn) {
+ var index = taskQueue.length;
+
+ taskCount++;
+ taskQueue.push(asyncFn);
- return raf;
+ if (index === 0) {
+ cancelLastRAF = rafFn(flush);
+ }
+
+ return function cancelQueueFn() {
+ if (index >= 0) {
+ taskQueue[index] = null;
+ index = null;
+
+ if (--taskCount === 0 && cancelLastRAF) {
+ cancelLastRAF();
+ cancelLastRAF = null;
+ taskQueue.length = 0;
+ }
+ }
+ };
+ }
}];
}
@@ -29455,15 +28228,15 @@ function $$RAFProvider() { //rAF
* exposed as $$____ properties
*
* Loop operations are optimized by using while(count--) { ... }
- * - This means that in order to keep the same order of execution as addition we have to add
+ * - this means that in order to keep the same order of execution as addition we have to add
* items to the array at the beginning (unshift) instead of at the end (push)
*
* Child scopes are created and removed often
- * - Using an array would be slow since inserts in the middle are expensive; so we use linked lists
+ * - Using an array would be slow since inserts in middle are expensive so we use linked list
*
- * There are fewer watches than observers. This is why you don't want the observer to be implemented
- * in the same way as watch. Watch requires return of the initialization function which is expensive
- * to construct.
+ * There are few watches then a lot of observers. This is why you don't want the observer to be
+ * implemented in the same way as watch. Watch requires return of initialization function which
+ * are expensive to construct.
*/
@@ -29505,7 +28278,7 @@ function $$RAFProvider() { //rAF
* Every application has a single root {@link ng.$rootScope.Scope scope}.
* All other scopes are descendant scopes of the root scope. Scopes provide separation
* between the model and the view, via a mechanism for watching the model for changes.
- * They also provide event emission/broadcast and subscription facility. See the
+ * They also provide an event emission/broadcast and subscription facility. See the
* {@link guide/scope developer guide on scopes}.
*/
function $RootScopeProvider() {
@@ -29535,36 +28308,13 @@ function $RootScopeProvider() {
return ChildScope;
}
- this.$get = ['$exceptionHandler', '$parse', '$browser',
- function($exceptionHandler, $parse, $browser) {
+ this.$get = ['$injector', '$exceptionHandler', '$parse', '$browser',
+ function($injector, $exceptionHandler, $parse, $browser) {
function destroyChildScope($event) {
$event.currentScope.$$destroyed = true;
}
- function cleanUpScope($scope) {
-
- if (msie === 9) {
- // There is a memory leak in IE9 if all child scopes are not disconnected
- // completely when a scope is destroyed. So this code will recurse up through
- // all this scopes children
- //
- // See issue https://github.com/angular/angular.js/issues/10706
- $scope.$$childHead && cleanUpScope($scope.$$childHead);
- $scope.$$nextSibling && cleanUpScope($scope.$$nextSibling);
- }
-
- // The code below works around IE9 and V8's memory leaks
- //
- // See:
- // - https://code.google.com/p/v8/issues/detail?id=2073#c26
- // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909
- // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
-
- $scope.$parent = $scope.$$nextSibling = $scope.$$prevSibling = $scope.$$childHead =
- $scope.$$childTail = $scope.$root = $scope.$$watchers = null;
- }
-
/**
* @ngdoc type
* @name $rootScope.Scope
@@ -29573,9 +28323,12 @@ function $RootScopeProvider() {
* A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the
* {@link auto.$injector $injector}. Child scopes are created using the
* {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when
- * compiled HTML template is executed.) See also the {@link guide/scope Scopes guide} for
- * an in-depth introduction and usage examples.
+ * compiled HTML template is executed.)
*
+ * Here is a simple scope snippet to show how you can interact with the scope.
+ * ```html
+ * <file src="./test/ng/rootScopeSpec.js" tag="docs1" />
+ * ```
*
* # Inheritance
* A scope can inherit from a parent scope, as in this example:
@@ -29717,10 +28470,10 @@ function $RootScopeProvider() {
* Registers a `listener` callback to be executed whenever the `watchExpression` changes.
*
* - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest
- * $digest()} and should return the value that will be watched. (`watchExpression` should not change
- * its value when executed multiple times with the same input because it may be executed multiple
- * times by {@link ng.$rootScope.Scope#$digest $digest()}. That is, `watchExpression` should be
- * [idempotent](http://en.wikipedia.org/wiki/Idempotence).
+ * $digest()} and should return the value that will be watched. (Since
+ * {@link ng.$rootScope.Scope#$digest $digest()} reruns when it detects changes the
+ * `watchExpression` can execute multiple times per
+ * {@link ng.$rootScope.Scope#$digest $digest()} and should be idempotent.)
* - The `listener` is called only when the value from the current `watchExpression` and the
* previous call to `watchExpression` are not equal (with the exception of the initial run,
* see below). Inequality is determined according to reference inequality,
@@ -29737,9 +28490,9 @@ function $RootScopeProvider() {
*
*
* If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called,
- * you can register a `watchExpression` function with no `listener`. (Be prepared for
- * multiple calls to your `watchExpression` because it will execute multiple times in a
- * single {@link ng.$rootScope.Scope#$digest $digest} cycle if a change is detected.)
+ * you can register a `watchExpression` function with no `listener`. (Since `watchExpression`
+ * can execute multiple times per {@link ng.$rootScope.Scope#$digest $digest} cycle when a
+ * change is detected, be prepared for multiple calls to your listener.)
*
* After a watcher is registered with the scope, the `listener` fn is called asynchronously
* (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the
@@ -29820,7 +28573,7 @@ function $RootScopeProvider() {
* - `newVal` contains the current value of the `watchExpression`
* - `oldVal` contains the previous value of the `watchExpression`
* - `scope` refers to the current scope
- * @param {boolean=} [objectEquality=false] Compare for object equality using {@link angular.equals} instead of
+ * @param {boolean=} objectEquality Compare for object equality using {@link angular.equals} instead of
* comparing for reference equality.
* @returns {function()} Returns a deregistration function for this listener.
*/
@@ -30069,7 +28822,7 @@ function $RootScopeProvider() {
// copy the items to oldValue and look for changes.
newLength = 0;
for (key in newValue) {
- if (hasOwnProperty.call(newValue, key)) {
+ if (newValue.hasOwnProperty(key)) {
newLength++;
newItem = newValue[key];
oldItem = oldValue[key];
@@ -30091,7 +28844,7 @@ function $RootScopeProvider() {
// we used to have more keys, need to find them and destroy them.
changeDetected++;
for (key in oldValue) {
- if (!hasOwnProperty.call(newValue, key)) {
+ if (!newValue.hasOwnProperty(key)) {
oldLength--;
delete oldValue[key];
}
@@ -30185,13 +28938,13 @@ function $RootScopeProvider() {
*
*/
$digest: function() {
- var watch, value, last, fn, get,
+ var watch, value, last,
watchers,
length,
dirty, ttl = TTL,
next, current, target = this,
watchLog = [],
- logIdx, asyncTask;
+ logIdx, logMsg, asyncTask;
beginPhase('$digest');
// Check for changes to browser url that happened in sync before the call to $digest
@@ -30231,8 +28984,7 @@ function $RootScopeProvider() {
// Most common watches are on primitives, in which case we can short
// circuit it with === operator, only when === fails do we use .equals
if (watch) {
- get = watch.get;
- if ((value = get(current)) !== (last = watch.last) &&
+ if ((value = watch.get(current)) !== (last = watch.last) &&
!(watch.eq
? equals(value, last)
: (typeof value === 'number' && typeof last === 'number'
@@ -30240,8 +28992,7 @@ function $RootScopeProvider() {
dirty = true;
lastDirtyWatch = watch;
watch.last = watch.eq ? copy(value, null) : value;
- fn = watch.fn;
- fn(value, ((last === initWatchVal) ? value : last), current);
+ watch.fn(value, ((last === initWatchVal) ? value : last), current);
if (ttl < 5) {
logIdx = 4 - ttl;
if (!watchLog[logIdx]) watchLog[logIdx] = [];
@@ -30363,9 +29114,16 @@ function $RootScopeProvider() {
this.$on = this.$watch = this.$watchGroup = function() { return noop; };
this.$$listeners = {};
- // Disconnect the next sibling to prevent `cleanUpScope` destroying those too
- this.$$nextSibling = null;
- cleanUpScope(this);
+ // All of the code below is bogus code that works around V8's memory leak via optimized code
+ // and inline caches.
+ //
+ // see:
+ // - https://code.google.com/p/v8/issues/detail?id=2073#c26
+ // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909
+ // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
+
+ this.$parent = this.$$nextSibling = this.$$prevSibling = this.$$childHead =
+ this.$$childTail = this.$root = this.$$watchers = null;
},
/**
@@ -30441,7 +29199,7 @@ function $RootScopeProvider() {
});
}
- asyncQueue.push({scope: this, expression: $parse(expr), locals: locals});
+ asyncQueue.push({scope: this, expression: expr, locals: locals});
},
$$postDigest: function(fn) {
@@ -30496,14 +29254,11 @@ function $RootScopeProvider() {
$apply: function(expr) {
try {
beginPhase('$apply');
- try {
- return this.$eval(expr);
- } finally {
- clearPhase();
- }
+ return this.$eval(expr);
} catch (e) {
$exceptionHandler(e);
} finally {
+ clearPhase();
try {
$rootScope.$digest();
} catch (e) {
@@ -30533,7 +29288,6 @@ function $RootScopeProvider() {
$applyAsync: function(expr) {
var scope = this;
expr && applyAsyncQueue.push($applyAsyncExpression);
- expr = $parse(expr);
scheduleApplyAsync();
function $applyAsyncExpression() {
@@ -30809,21 +29563,6 @@ function $RootScopeProvider() {
}
/**
- * @ngdoc service
- * @name $rootElement
- *
- * @description
- * The root element of Angular application. This is either the element where {@link
- * ng.directive:ngApp ngApp} was declared or the element passed into
- * {@link angular.bootstrap}. The element represents the root element of application. It is also the
- * location where the application's {@link auto.$injector $injector} service gets
- * published, and can be retrieved using `$rootElement.injector()`.
- */
-
-
-// the implementation is in angular.bootstrap
-
-/**
* @description
* Private service to sanitize uris for links and images. Used by $compile and $sanitize.
*/
@@ -31037,15 +29776,13 @@ function $SceDelegateProvider() {
* @kind function
*
* @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value
- * provided. This must be an array or null. A snapshot of this array is used so further
- * changes to the array are ignored.
+ * provided. This must be an array or null. A snapshot of this array is used so further
+ * changes to the array are ignored.
*
- * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
- * allowed in this array.
+ * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
+ * allowed in this array.
*
- * <div class="alert alert-warning">
- * **Note:** an empty whitelist array will block all URLs!
- * </div>
+ * Note: **an empty whitelist array will block all URLs**!
*
* @return {Array} the currently set whitelist array.
*
@@ -31068,17 +29805,17 @@ function $SceDelegateProvider() {
* @kind function
*
* @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value
- * provided. This must be an array or null. A snapshot of this array is used so further
- * changes to the array are ignored.
+ * provided. This must be an array or null. A snapshot of this array is used so further
+ * changes to the array are ignored.
*
- * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
- * allowed in this array.
+ * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
+ * allowed in this array.
*
- * The typical usage for the blacklist is to **block
- * [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as
- * these would otherwise be trusted but actually return content from the redirected domain.
+ * The typical usage for the blacklist is to **block
+ * [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as
+ * these would otherwise be trusted but actually return content from the redirected domain.
*
- * Finally, **the blacklist overrides the whitelist** and has the final say.
+ * Finally, **the blacklist overrides the whitelist** and has the final say.
*
* @return {Array} the currently set blacklist array.
*
@@ -31189,7 +29926,7 @@ function $SceDelegateProvider() {
'Attempted to trust a value in invalid context. Context: {0}; Value: {1}',
type, trustedValue);
}
- if (trustedValue === null || isUndefined(trustedValue) || trustedValue === '') {
+ if (trustedValue === null || trustedValue === undefined || trustedValue === '') {
return trustedValue;
}
// All the current contexts in SCE_CONTEXTS happen to be strings. In order to avoid trusting
@@ -31237,11 +29974,6 @@ function $SceDelegateProvider() {
* returns the originally supplied value if the queried context type is a supertype of the
* created type. If this condition isn't satisfied, throws an exception.
*
- * <div class="alert alert-danger">
- * Disabling auto-escaping is extremely dangerous, it usually creates a Cross Site Scripting
- * (XSS) vulnerability in your application.
- * </div>
- *
* @param {string} type The kind of context in which this value is to be used.
* @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs
* `$sceDelegate.trustAs`} call.
@@ -31249,7 +29981,7 @@ function $SceDelegateProvider() {
* `$sceDelegate.trustAs`} if valid in this context. Otherwise, throws an exception.
*/
function getTrusted(type, maybeTrusted) {
- if (maybeTrusted === null || isUndefined(maybeTrusted) || maybeTrusted === '') {
+ if (maybeTrusted === null || maybeTrusted === undefined || maybeTrusted === '') {
return maybeTrusted;
}
var constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
@@ -31384,7 +30116,7 @@ function $SceDelegateProvider() {
* By default, Angular only loads templates from the same domain and protocol as the application
* document. This is done by calling {@link ng.$sce#getTrustedResourceUrl
* $sce.getTrustedResourceUrl} on the template URL. To load templates from other domains and/or
- * protocols, you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist
+ * protocols, you may either either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist
* them} or {@link ng.$sce#trustAsResourceUrl wrap it} into a trusted value.
*
* *Please note*:
@@ -31442,10 +30174,10 @@ function $SceDelegateProvider() {
* - There are exactly **two wildcard sequences** - `*` and `**`. All other characters
* match themselves.
* - `*`: matches zero or more occurrences of any character other than one of the following 6
- * characters: '`:`', '`/`', '`.`', '`?`', '`&`' and '`;`'. It's a useful wildcard for use
+ * characters: '`:`', '`/`', '`.`', '`?`', '`&`' and ';'. It's a useful wildcard for use
* in a whitelist.
* - `**`: matches zero or more occurrences of *any* character. As such, it's not
- * appropriate for use in a scheme, domain, etc. as it would match too much. (e.g.
+ * not appropriate to use in for a scheme, domain, etc. as it would match too much. (e.g.
* http://**.example.com/ would match http://evil.com/?ignore=.example.com/ and that might
* not have been the intention.) Its usage at the very end of the path is ok. (e.g.
* http://foo.example.com/templates/**).
@@ -31453,11 +30185,11 @@ function $SceDelegateProvider() {
* - *Caveat*: While regular expressions are powerful and offer great flexibility, their syntax
* (and all the inevitable escaping) makes them *harder to maintain*. It's easy to
* accidentally introduce a bug when one updates a complex expression (imho, all regexes should
- * have good test coverage). For instance, the use of `.` in the regex is correct only in a
+ * have good test coverage.). For instance, the use of `.` in the regex is correct only in a
* small number of cases. A `.` character in the regex used when matching the scheme or a
* subdomain could be matched against a `:` or literal `.` that was likely not intended. It
* is highly recommended to use the string patterns and only fall back to regular expressions
- * as a last resort.
+ * if they as a last resort.
* - The regular expression must be an instance of RegExp (i.e. not a string.) It is
* matched against the **entire** *normalized / absolute URL* of the resource being tested
* (even when the RegExp did not have the `^` and `$` codes.) In addition, any flags
@@ -31467,7 +30199,7 @@ function $SceDelegateProvider() {
* remember to escape your regular expression (and be aware that you might need more than
* one level of escaping depending on your templating engine and the way you interpolated
* the value.) Do make use of your platform's escaping mechanism as it might be good
- * enough before coding your own. E.g. Ruby has
+ * enough before coding your own. e.g. Ruby has
* [Regexp.escape(str)](http://www.ruby-doc.org/core-2.0.0/Regexp.html#method-c-escape)
* and Python has [re.escape](http://docs.python.org/library/re.html#re.escape).
* Javascript lacks a similar built in function for escaping. Take a look at Google
@@ -31976,10 +30708,6 @@ function $SceProvider() {
function $SnifferProvider() {
this.$get = ['$window', '$document', function($window, $document) {
var eventSupport = {},
- // Chrome Packaged Apps are not allowed to access `history.pushState`. They can be detected by
- // the presence of `chrome.app.runtime` (see https://developer.chrome.com/apps/api_index)
- isChromePackagedApp = $window.chrome && $window.chrome.app && $window.chrome.app.runtime,
- hasHistoryPushState = !isChromePackagedApp && $window.history && $window.history.pushState,
android =
toInt((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]),
boxee = /Boxee/i.test(($window.navigator || {}).userAgent),
@@ -32024,7 +30752,7 @@ function $SnifferProvider() {
// so let's not use the history API also
// We are purposefully using `!(android < 4)` to cover the case when `android` is undefined
// jshint -W018
- history: !!(hasHistoryPushState && !(android < 4) && !boxee),
+ history: !!($window.history && $window.history.pushState && !(android < 4) && !boxee),
// jshint +W018
hasEvent: function(event) {
// IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
@@ -32050,66 +30778,29 @@ function $SnifferProvider() {
}];
}
-var $templateRequestMinErr = minErr('$compile');
+var $compileMinErr = minErr('$compile');
/**
- * @ngdoc provider
- * @name $templateRequestProvider
+ * @ngdoc service
+ * @name $templateRequest
+ *
* @description
- * Used to configure the options passed to the {@link $http} service when making a template request.
+ * The `$templateRequest` service runs security checks then downloads the provided template using
+ * `$http` and, upon success, stores the contents inside of `$templateCache`. If the HTTP request
+ * fails or the response data of the HTTP request is empty, a `$compile` error will be thrown (the
+ * exception can be thwarted by setting the 2nd parameter of the function to true). Note that the
+ * contents of `$templateCache` are trusted, so the call to `$sce.getTrustedUrl(tpl)` is omitted
+ * when `tpl` is of type string and `$templateCache` has the matching entry.
+ *
+ * @param {string|TrustedResourceUrl} tpl The HTTP request template URL
+ * @param {boolean=} ignoreRequestError Whether or not to ignore the exception when the request fails or the template is empty
*
- * For example, it can be used for specifying the "Accept" header that is sent to the server, when
- * requesting a template.
+ * @return {Promise} a promise for the HTTP response data of the given URL.
+ *
+ * @property {number} totalPendingRequests total amount of pending template requests being downloaded.
*/
function $TemplateRequestProvider() {
-
- var httpOptions;
-
- /**
- * @ngdoc method
- * @name $templateRequestProvider#httpOptions
- * @description
- * The options to be passed to the {@link $http} service when making the request.
- * You can use this to override options such as the "Accept" header for template requests.
- *
- * The {@link $templateRequest} will set the `cache` and the `transformResponse` properties of the
- * options if not overridden here.
- *
- * @param {string=} value new value for the {@link $http} options.
- * @returns {string|self} Returns the {@link $http} options when used as getter and self if used as setter.
- */
- this.httpOptions = function(val) {
- if (val) {
- httpOptions = val;
- return this;
- }
- return httpOptions;
- };
-
- /**
- * @ngdoc service
- * @name $templateRequest
- *
- * @description
- * The `$templateRequest` service runs security checks then downloads the provided template using
- * `$http` and, upon success, stores the contents inside of `$templateCache`. If the HTTP request
- * fails or the response data of the HTTP request is empty, a `$compile` error will be thrown (the
- * exception can be thwarted by setting the 2nd parameter of the function to true). Note that the
- * contents of `$templateCache` are trusted, so the call to `$sce.getTrustedUrl(tpl)` is omitted
- * when `tpl` is of type string and `$templateCache` has the matching entry.
- *
- * If you want to pass custom options to the `$http` service, such as setting the Accept header you
- * can configure this via {@link $templateRequestProvider#httpOptions}.
- *
- * @param {string|TrustedResourceUrl} tpl The HTTP request template URL
- * @param {boolean=} ignoreRequestError Whether or not to ignore the exception when the request fails or the template is empty
- *
- * @return {Promise} a promise for the HTTP response data of the given URL.
- *
- * @property {number} totalPendingRequests total amount of pending template requests being downloaded.
- */
this.$get = ['$templateCache', '$http', '$q', '$sce', function($templateCache, $http, $q, $sce) {
-
function handleRequestFn(tpl, ignoreRequestError) {
handleRequestFn.totalPendingRequests++;
@@ -32132,10 +30823,12 @@ function $TemplateRequestProvider() {
transformResponse = null;
}
- return $http.get(tpl, extend({
- cache: $templateCache,
- transformResponse: transformResponse
- }, httpOptions))
+ var httpOptions = {
+ cache: $templateCache,
+ transformResponse: transformResponse
+ };
+
+ return $http.get(tpl, httpOptions)
['finally'](function() {
handleRequestFn.totalPendingRequests--;
})
@@ -32146,7 +30839,7 @@ function $TemplateRequestProvider() {
function handleError(resp) {
if (!ignoreRequestError) {
- throw $templateRequestMinErr('tpload', 'Failed to load template: {0} (HTTP status: {1} {2})',
+ throw $compileMinErr('tpload', 'Failed to load template: {0} (HTTP status: {1} {2})',
tpl, resp.status, resp.statusText);
}
return $q.reject(resp);
@@ -32306,8 +30999,8 @@ function $TimeoutProvider() {
* @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
* will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
* @param {...*=} Pass additional parameters to the executed function.
- * @returns {Promise} Promise that will be resolved when the timeout is reached. The promise
- * will be resolved with the return value of the `fn` function.
+ * @returns {Promise} Promise that will be resolved when the timeout is reached. The value this
+ * promise will be resolved with is the return value of the `fn` function.
*
*/
function timeout(fn, delay, invokeApply) {
@@ -32394,13 +31087,20 @@ var originUrl = urlResolve(window.location.href);
*
* Implementation Notes for IE
* ---------------------------
- * IE <= 10 normalizes the URL when assigned to the anchor node similar to the other
+ * IE >= 8 and <= 10 normalizes the URL when assigned to the anchor node similar to the other
* browsers. However, the parsed components will not be set if the URL assigned did not specify
* them. (e.g. if you assign a.href = "foo", then a.protocol, a.host, etc. will be empty.) We
* work around that by performing the parsing in a 2nd step by taking a previously normalized
* URL (e.g. by assigning to a.href) and assigning it a.href again. This correctly populates the
* properties such as protocol, hostname, port, etc.
*
+ * IE7 does not normalize the URL when assigned to an anchor node. (Apparently, it does, if one
+ * uses the inner HTML approach to assign the URL as part of an HTML snippet -
+ * http://stackoverflow.com/a/472729) However, setting img[src] does normalize the URL.
+ * Unfortunately, setting img[src] to something like "javascript:foo" on IE throws an exception.
+ * Since the primary usage for normalizing URLs is to sanitize such URLs, we can't use that
+ * method and IE < 8 is unsupported.
+ *
* References:
* http://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement
* http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
@@ -32549,7 +31249,7 @@ function $$CookieReader($document) {
// the first value that is seen for a cookie is the most
// specific one. values for the same cookie name that
// follow are for less specific paths.
- if (isUndefined(lastCookies[name])) {
+ if (lastCookies[name] === undefined) {
lastCookies[name] = safeDecodeURIComponent(cookie.substring(index + 1));
}
}
@@ -32680,7 +31380,6 @@ function $FilterProvider($provide) {
* your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
* (`myapp_subsection_filterx`).
* </div>
- * @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.
*/
@@ -32983,10 +31682,6 @@ function getTypeForFilter(val) {
return (val === null) ? 'null' : typeof val;
}
-var MAX_DIGITS = 22;
-var DECIMAL_SEP = '.';
-var ZERO_CHAR = '0';
-
/**
* @ngdoc filter
* @name currency
@@ -33032,9 +31727,9 @@ var ZERO_CHAR = '0';
}
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)');
});
</file>
</example>
@@ -33068,7 +31763,7 @@ function currencyFilter($locale) {
* Formats a number as text.
*
* If the input is null or undefined, it will just be returned.
- * If the input is infinite (Infinity or -Infinity), the Infinity symbol '∞' or '-∞' is returned, respectively.
+ * If the input is infinite (Infinity/-Infinity) the Infinity symbol '∞' is returned.
* If the input is not a number an empty string is returned.
*
*
@@ -33076,7 +31771,7 @@ function currencyFilter($locale) {
* @param {(number|string)=} fractionSize Number of decimal places to round the number to.
* If this is not provided then the fraction size is computed from the current locale's number
* formatting pattern. In the case of the default locale, it will be 3.
- * @returns {string} Number rounded to fractionSize and places a “,” after each third digit.
+ * @returns {string} Number rounded to decimalPlaces and places a “,” after each third digit.
*
* @example
<example module="numberFilterExample">
@@ -33111,6 +31806,8 @@ function currencyFilter($locale) {
</file>
</example>
*/
+
+
numberFilter.$inject = ['$locale'];
function numberFilter($locale) {
var formats = $locale.NUMBER_FORMATS;
@@ -33124,227 +31821,102 @@ function numberFilter($locale) {
};
}
-/**
- * Parse a number (as a string) into three components that can be used
- * for formatting the number.
- *
- * (Significant bits of this parse algorithm came from https://github.com/MikeMcl/big.js/)
- *
- * @param {string} numStr The number to parse
- * @return {object} An object describing this number, containing the following keys:
- * - d : an array of digits containing leading zeros as necessary
- * - i : the number of the digits in `d` that are to the left of the decimal point
- * - e : the exponent for numbers that would need more than `MAX_DIGITS` digits in `d`
- *
- */
-function parse(numStr) {
- var exponent = 0, digits, numberOfIntegerDigits;
- var i, j, zeros;
-
- // Decimal point?
- if ((numberOfIntegerDigits = numStr.indexOf(DECIMAL_SEP)) > -1) {
- numStr = numStr.replace(DECIMAL_SEP, '');
- }
-
- // Exponential form?
- if ((i = numStr.search(/e/i)) > 0) {
- // Work out the exponent.
- if (numberOfIntegerDigits < 0) numberOfIntegerDigits = i;
- numberOfIntegerDigits += +numStr.slice(i + 1);
- numStr = numStr.substring(0, i);
- } else if (numberOfIntegerDigits < 0) {
- // There was no decimal point or exponent so it is an integer.
- numberOfIntegerDigits = numStr.length;
- }
-
- // Count the number of leading zeros.
- for (i = 0; numStr.charAt(i) == ZERO_CHAR; i++) {/* jshint noempty: false */}
-
- if (i == (zeros = numStr.length)) {
- // The digits are all zero.
- digits = [0];
- numberOfIntegerDigits = 1;
- } else {
- // Count the number of trailing zeros
- zeros--;
- while (numStr.charAt(zeros) == ZERO_CHAR) zeros--;
-
- // Trailing zeros are insignificant so ignore them
- numberOfIntegerDigits -= i;
- digits = [];
- // Convert string to array of digits without leading/trailing zeros.
- for (j = 0; i <= zeros; i++, j++) {
- digits[j] = +numStr.charAt(i);
- }
- }
-
- // If the number overflows the maximum allowed digits then use an exponent.
- if (numberOfIntegerDigits > MAX_DIGITS) {
- digits = digits.splice(0, MAX_DIGITS - 1);
- exponent = numberOfIntegerDigits - 1;
- numberOfIntegerDigits = 1;
- }
-
- return { d: digits, e: exponent, i: numberOfIntegerDigits };
-}
+var DECIMAL_SEP = '.';
+function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
+ if (isObject(number)) return '';
-/**
- * Round the parsed number to the specified number of decimal places
- * This function changed the parsedNumber in-place
- */
-function roundNumber(parsedNumber, fractionSize, minFrac, maxFrac) {
- var digits = parsedNumber.d;
- var fractionLen = digits.length - parsedNumber.i;
+ var isNegative = number < 0;
+ number = Math.abs(number);
- // determine fractionSize if it is not specified; `+fractionSize` converts it to a number
- fractionSize = (isUndefined(fractionSize)) ? Math.min(Math.max(minFrac, fractionLen), maxFrac) : +fractionSize;
+ var isInfinity = number === Infinity;
+ if (!isInfinity && !isFinite(number)) return '';
- // The index of the digit to where rounding is to occur
- var roundAt = fractionSize + parsedNumber.i;
- var digit = digits[roundAt];
+ var numStr = number + '',
+ formatedText = '',
+ hasExponent = false,
+ parts = [];
- if (roundAt > 0) {
- // Drop fractional digits beyond `roundAt`
- digits.splice(Math.max(parsedNumber.i, roundAt));
+ if (isInfinity) formatedText = '\u221e';
- // Set non-fractional digits beyond `roundAt` to 0
- for (var j = roundAt; j < digits.length; j++) {
- digits[j] = 0;
- }
+ if (!isInfinity && numStr.indexOf('e') !== -1) {
+ var match = numStr.match(/([\d\.]+)e(-?)(\d+)/);
+ if (match && match[2] == '-' && match[3] > fractionSize + 1) {
+ number = 0;
} else {
- // We rounded to zero so reset the parsedNumber
- fractionLen = Math.max(0, fractionLen);
- parsedNumber.i = 1;
- digits.length = Math.max(1, roundAt = fractionSize + 1);
- digits[0] = 0;
- for (var i = 1; i < roundAt; i++) digits[i] = 0;
- }
-
- if (digit >= 5) {
- if (roundAt - 1 < 0) {
- for (var k = 0; k > roundAt; k--) {
- digits.unshift(0);
- parsedNumber.i++;
- }
- digits.unshift(1);
- parsedNumber.i++;
- } else {
- digits[roundAt - 1]++;
- }
+ formatedText = numStr;
+ hasExponent = true;
}
+ }
- // Pad out with zeros to get the required fraction length
- for (; fractionLen < Math.max(0, fractionSize); fractionLen++) digits.push(0);
-
+ if (!isInfinity && !hasExponent) {
+ var fractionLen = (numStr.split(DECIMAL_SEP)[1] || '').length;
- // Do any carrying, e.g. a digit was rounded up to 10
- var carry = digits.reduceRight(function(carry, d, i, digits) {
- d = d + carry;
- digits[i] = d % 10;
- return Math.floor(d / 10);
- }, 0);
- if (carry) {
- digits.unshift(carry);
- parsedNumber.i++;
+ // determine fractionSize if it is not specified
+ if (isUndefined(fractionSize)) {
+ fractionSize = Math.min(Math.max(pattern.minFrac, fractionLen), pattern.maxFrac);
}
-}
-
-/**
- * Format a number into a string
- * @param {number} number The number to format
- * @param {{
- * minFrac, // the minimum number of digits required in the fraction part of the number
- * maxFrac, // the maximum number of digits required in the fraction part of the number
- * gSize, // number of digits in each group of separated digits
- * lgSize, // number of digits in the last group of digits before the decimal separator
- * negPre, // the string to go in front of a negative number (e.g. `-` or `(`))
- * posPre, // the string to go in front of a positive number
- * negSuf, // the string to go after a negative number (e.g. `)`)
- * posSuf // the string to go after a positive number
- * }} pattern
- * @param {string} groupSep The string to separate groups of number (e.g. `,`)
- * @param {string} decimalSep The string to act as the decimal separator (e.g. `.`)
- * @param {[type]} fractionSize The size of the fractional part of the number
- * @return {string} The number formatted as a string
- */
-function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
- if (!(isString(number) || isNumber(number)) || isNaN(number)) return '';
+ // safely round numbers in JS without hitting imprecisions of floating-point arithmetics
+ // inspired by:
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
+ number = +(Math.round(+(number.toString() + 'e' + fractionSize)).toString() + 'e' + -fractionSize);
- var isInfinity = !isFinite(number);
- var isZero = false;
- var numStr = Math.abs(number) + '',
- formattedText = '',
- parsedNumber;
+ var fraction = ('' + number).split(DECIMAL_SEP);
+ var whole = fraction[0];
+ fraction = fraction[1] || '';
- if (isInfinity) {
- formattedText = '\u221e';
- } else {
- parsedNumber = parse(numStr);
-
- roundNumber(parsedNumber, fractionSize, pattern.minFrac, pattern.maxFrac);
-
- var digits = parsedNumber.d;
- var integerLen = parsedNumber.i;
- var exponent = parsedNumber.e;
- var decimals = [];
- isZero = digits.reduce(function(isZero, d) { return isZero && !d; }, true);
-
- // pad zeros for small numbers
- while (integerLen < 0) {
- digits.unshift(0);
- integerLen++;
- }
+ var i, pos = 0,
+ lgroup = pattern.lgSize,
+ group = pattern.gSize;
- // extract decimals digits
- if (integerLen > 0) {
- decimals = digits.splice(integerLen);
- } else {
- decimals = digits;
- digits = [0];
+ if (whole.length >= (lgroup + group)) {
+ pos = whole.length - lgroup;
+ for (i = 0; i < pos; i++) {
+ if ((pos - i) % group === 0 && i !== 0) {
+ formatedText += groupSep;
+ }
+ formatedText += whole.charAt(i);
+ }
}
- // format the integer digits with grouping separators
- var groups = [];
- if (digits.length >= pattern.lgSize) {
- groups.unshift(digits.splice(-pattern.lgSize).join(''));
- }
- while (digits.length > pattern.gSize) {
- groups.unshift(digits.splice(-pattern.gSize).join(''));
- }
- if (digits.length) {
- groups.unshift(digits.join(''));
+ for (i = pos; i < whole.length; i++) {
+ if ((whole.length - i) % lgroup === 0 && i !== 0) {
+ formatedText += groupSep;
+ }
+ formatedText += whole.charAt(i);
}
- formattedText = groups.join(groupSep);
- // append the decimal digits
- if (decimals.length) {
- formattedText += decimalSep + decimals.join('');
+ // format fraction part.
+ while (fraction.length < fractionSize) {
+ fraction += '0';
}
- if (exponent) {
- formattedText += 'e+' + exponent;
+ if (fractionSize && fractionSize !== "0") formatedText += decimalSep + fraction.substr(0, fractionSize);
+ } else {
+ if (fractionSize > 0 && number < 1) {
+ formatedText = number.toFixed(fractionSize);
+ number = parseFloat(formatedText);
}
}
- if (number < 0 && !isZero) {
- return pattern.negPre + formattedText + pattern.negSuf;
- } else {
- return pattern.posPre + formattedText + pattern.posSuf;
+
+ if (number === 0) {
+ isNegative = false;
}
+
+ parts.push(isNegative ? pattern.negPre : pattern.posPre,
+ formatedText,
+ isNegative ? pattern.negSuf : pattern.posSuf);
+ return parts.join('');
}
-function padNumber(num, digits, trim, negWrap) {
+function padNumber(num, digits, trim) {
var neg = '';
- if (num < 0 || (negWrap && num <= 0)) {
- if (negWrap) {
- num = -num + 1;
- } else {
- num = -num;
- neg = '-';
- }
+ if (num < 0) {
+ neg = '-';
+ num = -num;
}
num = '' + num;
- while (num.length < digits) num = ZERO_CHAR + num;
+ while (num.length < digits) num = '0' + num;
if (trim) {
num = num.substr(num.length - digits);
}
@@ -33352,7 +31924,7 @@ function padNumber(num, digits, trim, negWrap) {
}
-function dateGetter(name, size, offset, trim, negWrap) {
+function dateGetter(name, size, offset, trim) {
offset = offset || 0;
return function(date) {
var value = date['get' + name]();
@@ -33360,15 +31932,14 @@ function dateGetter(name, size, offset, trim, negWrap) {
value += offset;
}
if (value === 0 && offset == -12) value = 12;
- return padNumber(value, size, trim, negWrap);
+ return padNumber(value, size, trim);
};
}
-function dateStrGetter(name, shortForm, standAlone) {
+function dateStrGetter(name, shortForm) {
return function(date, formats) {
var value = date['get' + name]();
- var propPrefix = (standAlone ? 'STANDALONE' : '') + (shortForm ? 'SHORT' : '');
- var get = uppercase(propPrefix + name);
+ var get = uppercase(shortForm ? ('SHORT' + name) : name);
return formats[get][value];
};
@@ -33423,14 +31994,13 @@ function longEraGetter(date, formats) {
}
var DATE_FORMATS = {
- yyyy: dateGetter('FullYear', 4, 0, false, true),
- yy: dateGetter('FullYear', 2, 0, true, true),
- y: dateGetter('FullYear', 1, 0, false, true),
+ yyyy: dateGetter('FullYear', 4),
+ yy: dateGetter('FullYear', 2, 0, true),
+ y: dateGetter('FullYear', 1),
MMMM: dateStrGetter('Month'),
MMM: dateStrGetter('Month', true),
MM: dateGetter('Month', 2, 1),
M: dateGetter('Month', 1, 1),
- LLLL: dateStrGetter('Month', false, true),
dd: dateGetter('Date', 2),
d: dateGetter('Date', 1),
HH: dateGetter('Hours', 2),
@@ -33456,7 +32026,7 @@ var DATE_FORMATS = {
GGGG: longEraGetter
};
-var DATE_FORMATS_SPLIT = /((?:[^yMLdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|L+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,
+var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,
NUMBER_STRING = /^\-?\d+$/;
/**
@@ -33476,7 +32046,6 @@ var DATE_FORMATS_SPLIT = /((?:[^yMLdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+
* * `'MMM'`: Month in year (Jan-Dec)
* * `'MM'`: Month in year, padded (01-12)
* * `'M'`: Month in year (1-12)
- * * `'LLLL'`: Stand-alone month in year (January-December)
* * `'dd'`: Day in month, padded (01-31)
* * `'d'`: Day in month (1-31)
* * `'EEEE'`: Day in Week,(Sunday-Saturday)
@@ -33616,13 +32185,13 @@ function dateFilter($locale) {
var dateTimezoneOffset = date.getTimezoneOffset();
if (timezone) {
- dateTimezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset);
+ dateTimezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset());
date = convertTimezoneToLocal(date, timezone, true);
}
forEach(parts, function(value) {
fn = DATE_FORMATS[value];
text += fn ? fn(date, $locale.DATETIME_FORMATS, dateTimezoneOffset)
- : value === "''" ? "'" : value.replace(/(^'|'$)/g, '').replace(/''/g, "'");
+ : value.replace(/(^'|'$)/g, '').replace(/''/g, "'");
});
return text;
@@ -33803,7 +32372,7 @@ function limitToFilter() {
if (!isArray(input) && !isString(input)) return input;
begin = (!begin || isNaN(begin)) ? 0 : toInt(begin);
- begin = (begin < 0) ? Math.max(0, input.length + begin) : begin;
+ begin = (begin < 0 && begin >= -input.length) ? input.length + begin : begin;
if (limit >= 0) {
return input.slice(begin, begin + limit);
@@ -33826,9 +32395,8 @@ function limitToFilter() {
* Orders a specified `array` by the `expression` predicate. It is ordered alphabetically
* for strings and numerically for numbers. Note: if you notice numbers are not being sorted
* as expected, make sure they are actually being saved as numbers and not strings.
- * Array-like values (e.g. NodeLists, jQuery objects, TypedArrays, Strings, etc) are also supported.
*
- * @param {Array} array The array (or array-like object) to sort.
+ * @param {Array} array The array to sort.
* @param {function(*)|string|Array.<(function(*)|string)>=} expression A predicate to be
* used by the comparator to determine the order of elements.
*
@@ -33859,6 +32427,17 @@ function limitToFilter() {
* `reverse` is not set, which means it defaults to `false`.
<example module="orderByExample">
<file name="index.html">
+ <script>
+ angular.module('orderByExample', [])
+ .controller('ExampleController', ['$scope', function($scope) {
+ $scope.friends =
+ [{name:'John', phone:'555-1212', age:10},
+ {name:'Mary', phone:'555-9876', age:19},
+ {name:'Mike', phone:'555-4321', age:21},
+ {name:'Adam', phone:'555-5678', age:35},
+ {name:'Julie', phone:'555-8765', age:29}];
+ }]);
+ </script>
<div ng-controller="ExampleController">
<table class="friend">
<tr>
@@ -33874,17 +32453,6 @@ function limitToFilter() {
</table>
</div>
</file>
- <file name="script.js">
- angular.module('orderByExample', [])
- .controller('ExampleController', ['$scope', function($scope) {
- $scope.friends =
- [{name:'John', phone:'555-1212', age:10},
- {name:'Mary', phone:'555-9876', age:19},
- {name:'Mike', phone:'555-4321', age:21},
- {name:'Adam', phone:'555-5678', age:35},
- {name:'Julie', phone:'555-8765', age:29}];
- }]);
- </file>
</example>
*
* The predicate and reverse parameters can be controlled dynamically through scope properties,
@@ -33892,24 +32460,49 @@ function limitToFilter() {
* @example
<example module="orderByExample">
<file name="index.html">
+ <script>
+ angular.module('orderByExample', [])
+ .controller('ExampleController', ['$scope', function($scope) {
+ $scope.friends =
+ [{name:'John', phone:'555-1212', age:10},
+ {name:'Mary', phone:'555-9876', age:19},
+ {name:'Mike', phone:'555-4321', age:21},
+ {name:'Adam', phone:'555-5678', age:35},
+ {name:'Julie', phone:'555-8765', age:29}];
+ $scope.predicate = 'age';
+ $scope.reverse = true;
+ $scope.order = function(predicate) {
+ $scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
+ $scope.predicate = predicate;
+ };
+ }]);
+ </script>
+ <style type="text/css">
+ .sortorder:after {
+ content: '\25b2';
+ }
+ .sortorder.reverse:after {
+ content: '\25bc';
+ }
+ </style>
<div ng-controller="ExampleController">
<pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
<hr/>
- <button ng-click="predicate=''">Set to unsorted</button>
+ [ <a href="" ng-click="predicate=''">unsorted</a> ]
<table class="friend">
<tr>
- <th>
- <button ng-click="order('name')">Name</button>
- <span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
- </th>
- <th>
- <button ng-click="order('phone')">Phone Number</button>
- <span class="sortorder" ng-show="predicate === 'phone'" ng-class="{reverse:reverse}"></span>
- </th>
- <th>
- <button ng-click="order('age')">Age</button>
- <span class="sortorder" ng-show="predicate === 'age'" ng-class="{reverse:reverse}"></span>
- </th>
+ <th>
+ <a href="" ng-click="order('name')">Name</a>
+ <span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
+ </th>
+ <th>
+ <a href="" ng-click="order('phone')">Phone Number</a>
+ <span class="sortorder" ng-show="predicate === 'phone'" ng-class="{reverse:reverse}"></span>
+ </th>
+ <th>
+ <a href="" ng-click="order('age')">Age</a>
+ <span class="sortorder" ng-show="predicate === 'age'" ng-class="{reverse:reverse}"></span>
+ </th>
</tr>
<tr ng-repeat="friend in friends | orderBy:predicate:reverse">
<td>{{friend.name}}</td>
@@ -33919,31 +32512,6 @@ function limitToFilter() {
</table>
</div>
</file>
- <file name="script.js">
- angular.module('orderByExample', [])
- .controller('ExampleController', ['$scope', function($scope) {
- $scope.friends =
- [{name:'John', phone:'555-1212', age:10},
- {name:'Mary', phone:'555-9876', age:19},
- {name:'Mike', phone:'555-4321', age:21},
- {name:'Adam', phone:'555-5678', age:35},
- {name:'Julie', phone:'555-8765', age:29}];
- $scope.predicate = 'age';
- $scope.reverse = true;
- $scope.order = function(predicate) {
- $scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
- $scope.predicate = predicate;
- };
- }]);
- </file>
- <file name="style.css">
- .sortorder:after {
- content: '\25b2';
- }
- .sortorder.reverse:after {
- content: '\25bc';
- }
- </file>
</example>
*
* It's also possible to call the orderBy filter manually, by injecting `$filter`, retrieving the
@@ -33955,30 +32523,21 @@ function limitToFilter() {
* @example
<example module="orderByExample">
<file name="index.html">
- <div ng-controller="ExampleController">
- <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
- <table class="friend">
- <tr>
- <th>
- <button ng-click="order('name')">Name</button>
- <span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
- </th>
- <th>
- <button ng-click="order('phone')">Phone Number</button>
- <span class="sortorder" ng-show="predicate === 'phone'" ng-class="{reverse:reverse}"></span>
- </th>
- <th>
- <button ng-click="order('age')">Age</button>
- <span class="sortorder" ng-show="predicate === 'age'" ng-class="{reverse:reverse}"></span>
- </th>
- </tr>
- <tr ng-repeat="friend in friends">
- <td>{{friend.name}}</td>
- <td>{{friend.phone}}</td>
- <td>{{friend.age}}</td>
- </tr>
- </table>
- </div>
+ <div ng-controller="ExampleController">
+ <table class="friend">
+ <tr>
+ <th><a href="" ng-click="reverse=false;order('name', false)">Name</a>
+ (<a href="" ng-click="order('-name',false)">^</a>)</th>
+ <th><a href="" ng-click="reverse=!reverse;order('phone', reverse)">Phone Number</a></th>
+ <th><a href="" ng-click="reverse=!reverse;order('age',reverse)">Age</a></th>
+ </tr>
+ <tr ng-repeat="friend in friends">
+ <td>{{friend.name}}</td>
+ <td>{{friend.phone}}</td>
+ <td>{{friend.age}}</td>
+ </tr>
+ </table>
+ </div>
</file>
<file name="script.js">
@@ -33992,42 +32551,24 @@ function limitToFilter() {
{ name: 'Adam', phone: '555-5678', age: 35 },
{ name: 'Julie', phone: '555-8765', age: 29 }
];
- $scope.order = function(predicate) {
- $scope.predicate = predicate;
- $scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
- $scope.friends = orderBy($scope.friends, predicate, $scope.reverse);
+ $scope.order = function(predicate, reverse) {
+ $scope.friends = orderBy($scope.friends, predicate, reverse);
};
- $scope.order('age', true);
+ $scope.order('-age',false);
}]);
</file>
-
- <file name="style.css">
- .sortorder:after {
- content: '\25b2';
- }
- .sortorder.reverse:after {
- content: '\25bc';
- }
- </file>
</example>
*/
orderByFilter.$inject = ['$parse'];
function orderByFilter($parse) {
return function(array, sortPredicate, reverseOrder) {
- if (array == null) return array;
- if (!isArrayLike(array)) {
- throw minErr('orderBy')('notarray', 'Expected array but received: {0}', array);
- }
+ if (!(isArrayLike(array))) return array;
if (!isArray(sortPredicate)) { sortPredicate = [sortPredicate]; }
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)
@@ -34341,7 +32882,20 @@ var htmlAnchorDirective = valueFn({
* {@link guide/expression expression} inside `ngDisabled` evaluates to truthy.
*
* A special directive is necessary because we cannot use interpolation inside the `disabled`
- * attribute. See the {@link guide/interpolation interpolation guide} for more info.
+ * attribute. The following example would make the button enabled on Chrome/Firefox
+ * but not on older IEs:
+ *
+ * ```html
+ * <!-- See below for an example of ng-disabled being used correctly -->
+ * <div ng-init="isDisabled = false">
+ * <button disabled="{{isDisabled}}">Disabled</button>
+ * </div>
+ * ```
+ *
+ * This is because the HTML specification does not require browsers to preserve the values of
+ * boolean attributes such as `disabled` (Their presence means true and their absence means false.)
+ * If we put an Angular interpolation expression into such an attribute then the
+ * binding information would be lost when the browser removes the attribute.
*
* @example
<example>
@@ -34376,9 +32930,15 @@ var htmlAnchorDirective = valueFn({
* Note that this directive should not be used together with {@link ngModel `ngModel`},
* as this can lead to unexpected behavior.
*
- * A special directive is necessary because we cannot use interpolation inside the `checked`
- * attribute. See the {@link guide/interpolation interpolation guide} for more info.
+ * ### Why do we need `ngChecked`?
*
+ * The HTML specification does not require browsers to preserve the values of boolean attributes
+ * such as checked. (Their presence means true and their absence means false.)
+ * If we put an Angular interpolation expression into such an attribute then the
+ * binding information would be lost when the browser removes the attribute.
+ * The `ngChecked` directive solves this problem for the `checked` attribute.
+ * This complementary directive is not removed by the browser and so provides
+ * a permanent reliable place to store the binding information.
* @example
<example>
<file name="index.html">
@@ -34407,12 +32967,13 @@ var htmlAnchorDirective = valueFn({
* @priority 100
*
* @description
- *
- * Sets the `readOnly` attribute on the element, if the expression inside `ngReadonly` is truthy.
- *
- * A special directive is necessary because we cannot use interpolation inside the `readOnly`
- * attribute. See the {@link guide/interpolation interpolation guide} for more info.
- *
+ * The HTML specification does not require browsers to preserve the values of boolean attributes
+ * such as readonly. (Their presence means true and their absence means false.)
+ * If we put an Angular interpolation expression into such an attribute then the
+ * binding information would be lost when the browser removes the attribute.
+ * The `ngReadonly` directive solves this problem for the `readonly` attribute.
+ * This complementary directive is not removed by the browser and so provides
+ * a permanent reliable place to store the binding information.
* @example
<example>
<file name="index.html">
@@ -34441,11 +33002,13 @@ var htmlAnchorDirective = valueFn({
* @priority 100
*
* @description
- *
- * Sets the `selected` attribute on the element, if the expression inside `ngSelected` is truthy.
- *
- * A special directive is necessary because we cannot use interpolation inside the `selected`
- * attribute. See the {@link guide/interpolation interpolation guide} for more info.
+ * The HTML specification does not require browsers to preserve the values of boolean attributes
+ * such as selected. (Their presence means true and their absence means false.)
+ * If we put an Angular interpolation expression into such an attribute then the
+ * binding information would be lost when the browser removes the attribute.
+ * The `ngSelected` directive solves this problem for the `selected` attribute.
+ * This complementary directive is not removed by the browser and so provides
+ * a permanent reliable place to store the binding information.
*
* @example
<example>
@@ -34477,12 +33040,13 @@ var htmlAnchorDirective = valueFn({
* @priority 100
*
* @description
- *
- * Sets the `open` attribute on the element, if the expression inside `ngOpen` is truthy.
- *
- * A special directive is necessary because we cannot use interpolation inside the `open`
- * attribute. See the {@link guide/interpolation interpolation guide} for more info.
- *
+ * The HTML specification does not require browsers to preserve the values of boolean attributes
+ * such as open. (Their presence means true and their absence means false.)
+ * If we put an Angular interpolation expression into such an attribute then the
+ * binding information would be lost when the browser removes the attribute.
+ * The `ngOpen` directive solves this problem for the `open` attribute.
+ * This complementary directive is not removed by the browser and so provides
+ * a permanent reliable place to store the binding information.
* @example
<example>
<file name="index.html">
@@ -34626,7 +33190,6 @@ 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
@@ -34666,6 +33229,8 @@ 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 = {};
@@ -34676,7 +33241,8 @@ function FormController(element, attrs, $scope, $animate, $interpolate) {
form.$valid = true;
form.$invalid = false;
form.$submitted = false;
- form.$$parentForm = nullFormCtrl;
+
+ parentForm.$addControl(form);
/**
* @ngdoc method
@@ -34715,23 +33281,11 @@ 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. 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.
+ * Register a control with the form.
*
- * 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 responsibility 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.
+ * Input elements using ngModelController do this automatically when they are linked.
*/
form.$addControl = function(control) {
// Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored
@@ -34742,8 +33296,6 @@ function FormController(element, attrs, $scope, $animate, $interpolate) {
if (control.$name) {
form[control.$name] = control;
}
-
- control.$$parentForm = form;
};
// Private API: rename a form control
@@ -34760,18 +33312,11 @@ 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) {
@@ -34788,7 +33333,6 @@ function FormController(element, attrs, $scope, $animate, $interpolate) {
});
arrayRemove(controls, control);
- control.$$parentForm = nullFormCtrl;
};
@@ -34825,6 +33369,7 @@ function FormController(element, attrs, $scope, $animate, $interpolate) {
delete object[property];
}
},
+ parentForm: parentForm,
$animate: $animate
});
@@ -34843,7 +33388,7 @@ function FormController(element, attrs, $scope, $animate, $interpolate) {
$animate.addClass(element, DIRTY_CLASS);
form.$dirty = true;
form.$pristine = false;
- form.$$parentForm.$setDirty();
+ parentForm.$setDirty();
};
/**
@@ -34899,7 +33444,7 @@ function FormController(element, attrs, $scope, $animate, $interpolate) {
form.$setSubmitted = function() {
$animate.addClass(element, SUBMITTED_CLASS);
form.$submitted = true;
- form.$$parentForm.$setSubmitted();
+ parentForm.$setSubmitted();
};
}
@@ -34938,14 +33483,17 @@ function FormController(element, attrs, $scope, $animate, $interpolate) {
*
* In Angular, forms can be nested. This means that the outer form is valid when all of the child
* forms are valid as well. However, browsers do not allow nesting of `<form>` elements, so
- * Angular provides the {@link ng.directive:ngForm `ngForm`} directive, which behaves identically to
- * `form` but can be nested. Nested forms can be useful, for example, if the validity of a sub-group
- * of controls needs to be determined.
+ * Angular provides the {@link ng.directive:ngForm `ngForm`} directive which behaves identically to
+ * `<form>` but can be nested. This allows you to have nested forms, which is very useful when
+ * using Angular validation directives in forms that are dynamically generated using the
+ * {@link ng.directive:ngRepeat `ngRepeat`} directive. Since you cannot dynamically generate the `name`
+ * attribute of input elements using interpolation, you have to wrap each set of repeated inputs in an
+ * `ngForm` directive and nest these in an outer `form` element.
+ *
*
* # 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.
@@ -35021,6 +33569,7 @@ function FormController(element, attrs, $scope, $animate, $interpolate) {
</script>
<style>
.my-form {
+ -webkit-transition:all linear 0.5s;
transition:all linear 0.5s;
background: transparent;
}
@@ -35065,11 +33614,10 @@ function FormController(element, attrs, $scope, $animate, $interpolate) {
* related scope, under this name.
*/
var formDirectiveFactory = function(isNgForm) {
- return ['$timeout', '$parse', function($timeout, $parse) {
+ return ['$timeout', function($timeout) {
var formDirective = {
name: 'form',
restrict: isNgForm ? 'EAC' : 'E',
- require: ['form', '^^?form'], //first is the form's own ctrl, second is an optional parent form
controller: FormController,
compile: function ngFormCompile(formElement, attr) {
// Setup initial state of the control
@@ -35078,9 +33626,7 @@ var formDirectiveFactory = function(isNgForm) {
var nameAttr = attr.name ? 'name' : (isNgForm && attr.ngForm ? 'ngForm' : false);
return {
- pre: function ngFormPreLink(scope, formElement, attr, ctrls) {
- var controller = ctrls[0];
-
+ pre: function ngFormPreLink(scope, formElement, attr, controller) {
// if `action` attr is not present on the form, prevent the default action (submission)
if (!('action' in attr)) {
// we can't use jq events because if a form is destroyed during submission the default
@@ -35109,24 +33655,22 @@ var formDirectiveFactory = function(isNgForm) {
});
}
- var parentFormCtrl = ctrls[1] || controller.$$parentForm;
- parentFormCtrl.$addControl(controller);
-
- var setter = nameAttr ? getSetter(controller.$name) : noop;
+ var parentFormCtrl = controller.$$parentForm;
if (nameAttr) {
- setter(scope, controller);
+ setter(scope, controller.$name, controller, controller.$name);
attr.$observe(nameAttr, function(newValue) {
if (controller.$name === newValue) return;
- setter(scope, undefined);
- controller.$$parentForm.$$renameControl(controller, newValue);
- setter = getSetter(controller.$name);
- setter(scope, controller);
+ setter(scope, controller.$name, undefined, controller.$name);
+ parentFormCtrl.$$renameControl(controller, newValue);
+ setter(scope, controller.$name, controller, controller.$name);
});
}
formElement.on('$destroy', function() {
- controller.$$parentForm.$removeControl(controller);
- setter(scope, undefined);
+ parentFormCtrl.$removeControl(controller);
+ if (nameAttr) {
+ setter(scope, attr[nameAttr], undefined, controller.$name);
+ }
extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards
});
}
@@ -35135,14 +33679,6 @@ var formDirectiveFactory = function(isNgForm) {
};
return formDirective;
-
- function getSetter(expression) {
- if (expression === '') {
- //create an assignable expression, so forms with an empty name can be renamed later
- return $parse('this[""]').assign;
- }
- return $parse(expression).assign || noop;
- }
}];
};
@@ -35155,38 +33691,20 @@ var ngFormDirective = formDirectiveFactory(true);
DIRTY_CLASS: false,
UNTOUCHED_CLASS: false,
TOUCHED_CLASS: false,
- ngModelMinErr: false,
+ $ngModelMinErr: false,
*/
-// Regex code was initially obtained from SO prior to modification: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231
-var ISO_DATE_REGEXP = /^\d{4,}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+(?:[+-][0-2]\d:[0-5]\d|Z)$/;
-// See valid URLs in RFC3987 (http://tools.ietf.org/html/rfc3987)
-// Note: We are being more lenient, because browsers are too.
-// 1. Scheme
-// 2. Slashes
-// 3. Username
-// 4. Password
-// 5. Hostname
-// 6. Port
-// 7. Path
-// 8. Query
-// 9. Fragment
-// 1111111111111111 222 333333 44444 555555555555555555555555 666 77777777 8888888 999
-var URL_REGEXP = /^[a-z][a-z\d.+-]*:\/*(?:[^:@]+(?::[^@]+)?@)?(?:[^\s:/?#]+|\[[a-f\d:]+\])(?::\d+)?(?:\/[^?#]*)?(?:\?[^#]*)?(?:#.*)?$/i;
+// Regex code is obtained from SO: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231
+var ISO_DATE_REGEXP = /\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)/;
+var URL_REGEXP = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/;
var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;
var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/;
-var DATE_REGEXP = /^(\d{4,})-(\d{2})-(\d{2})$/;
-var DATETIMELOCAL_REGEXP = /^(\d{4,})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
-var WEEK_REGEXP = /^(\d{4,})-W(\d\d)$/;
-var MONTH_REGEXP = /^(\d{4,})-(\d\d)$/;
+var DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})$/;
+var DATETIMELOCAL_REGEXP = /^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
+var WEEK_REGEXP = /^(\d{4})-W(\d\d)$/;
+var MONTH_REGEXP = /^(\d{4})-(\d\d)$/;
var TIME_REGEXP = /^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
-var PARTIAL_VALIDATION_EVENTS = 'keydown wheel mousedown';
-var PARTIAL_VALIDATION_TYPES = createMap();
-forEach('date,datetime-local,month,time,week'.split(','), function(type) {
- PARTIAL_VALIDATION_TYPES[type] = true;
-});
-
var inputType = {
/**
@@ -35211,8 +33729,8 @@ var inputType = {
* @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
* that contains the regular expression body that will be converted to a regular expression
* as in the ngPattern directive.
- * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
- * does not match a RegExp found by evaluating the Angular expression given in the attribute value.
+ * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
+ * a RegExp found by evaluating the Angular expression given in the attribute value.
* If the expression evaluates to a RegExp object, then this is used directly.
* If the expression evaluates to a string, then it will be converted to a RegExp
* after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
@@ -35305,17 +33823,9 @@ var inputType = {
* @param {string} ngModel Assignable angular expression to data-bind to.
* @param {string=} name Property name of the form under which the control is published.
* @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
- * valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
- * (e.g. `min="{{minDate | date:'yyyy-MM-dd'}}"`). Note that `min` will also add native HTML5
- * constraint validation.
+ * valid ISO date string (yyyy-MM-dd).
* @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
- * a valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
- * (e.g. `max="{{maxDate | date:'yyyy-MM-dd'}}"`). Note that `max` will also add native HTML5
- * constraint validation.
- * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO date string
- * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
- * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO date string
- * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
+ * a valid ISO date string (yyyy-MM-dd).
* @param {string=} required Sets `required` validation error key if the value is not entered.
* @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
* the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
@@ -35407,18 +33917,10 @@ var inputType = {
*
* @param {string} ngModel Assignable angular expression to data-bind to.
* @param {string=} name Property name of the form under which the control is published.
- * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
- * This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
- * inside this attribute (e.g. `min="{{minDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
- * Note that `min` will also add native HTML5 constraint validation.
- * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
- * This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
- * inside this attribute (e.g. `max="{{maxDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
- * Note that `max` will also add native HTML5 constraint validation.
- * @param {(date|string)=} ngMin Sets the `min` validation error key to the Date / ISO datetime string
- * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
- * @param {(date|string)=} ngMax Sets the `max` validation error key to the Date / ISO datetime string
- * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
+ * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
+ * valid ISO datetime format (yyyy-MM-ddTHH:mm:ss).
+ * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
+ * a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss).
* @param {string=} required Sets `required` validation error key if the value is not entered.
* @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
* the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
@@ -35499,7 +34001,7 @@ var inputType = {
*
* @description
* Input with time validation and transformation. In browsers that do not yet support
- * the HTML5 time input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
+ * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
* local time format (HH:mm:ss), for example: `14:57:00`. Model must be a Date object. This binding will always output a
* Date object to the model of January 1, 1970, or local date `new Date(1970, 0, 1, HH, mm, ss)`.
*
@@ -35511,18 +34013,10 @@ var inputType = {
*
* @param {string} ngModel Assignable angular expression to data-bind to.
* @param {string=} name Property name of the form under which the control is published.
- * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
- * This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
- * attribute (e.g. `min="{{minTime | date:'HH:mm:ss'}}"`). Note that `min` will also add
- * native HTML5 constraint validation.
- * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
- * This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
- * attribute (e.g. `max="{{maxTime | date:'HH:mm:ss'}}"`). Note that `max` will also add
- * native HTML5 constraint validation.
- * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO time string the
- * `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
- * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO time string the
- * `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
+ * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
+ * valid ISO time format (HH:mm:ss).
+ * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be a
+ * valid ISO time format (HH:mm:ss).
* @param {string=} required Sets `required` validation error key if the value is not entered.
* @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
* the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
@@ -35542,7 +34036,7 @@ var inputType = {
}]);
</script>
<form name="myForm" ng-controller="DateController as dateCtrl">
- <label for="exampleInput">Pick a time between 8am and 5pm:</label>
+ <label for="exampleInput">Pick a between 8am and 5pm:</label>
<input type="time" id="exampleInput" name="input" ng-model="example.value"
placeholder="HH:mm:ss" min="08:00:00" max="17:00:00" required />
<div role="alert">
@@ -35614,18 +34108,10 @@ var inputType = {
*
* @param {string} ngModel Assignable angular expression to data-bind to.
* @param {string=} name Property name of the form under which the control is published.
- * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
- * This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
- * attribute (e.g. `min="{{minWeek | date:'yyyy-Www'}}"`). Note that `min` will also add
- * native HTML5 constraint validation.
- * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
- * This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
- * attribute (e.g. `max="{{maxWeek | date:'yyyy-Www'}}"`). Note that `max` will also add
- * native HTML5 constraint validation.
- * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
- * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
- * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
- * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
+ * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
+ * valid ISO week format (yyyy-W##).
+ * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
+ * a valid ISO week format (yyyy-W##).
* @param {string=} required Sets `required` validation error key if the value is not entered.
* @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
* the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
@@ -35719,19 +34205,10 @@ var inputType = {
*
* @param {string} ngModel Assignable angular expression to data-bind to.
* @param {string=} name Property name of the form under which the control is published.
- * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
- * This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
- * attribute (e.g. `min="{{minMonth | date:'yyyy-MM'}}"`). Note that `min` will also add
- * native HTML5 constraint validation.
- * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
- * This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
- * attribute (e.g. `max="{{maxMonth | date:'yyyy-MM'}}"`). Note that `max` will also add
- * native HTML5 constraint validation.
- * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
- * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
- * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
- * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
-
+ * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be
+ * a valid ISO month format (yyyy-MM).
+ * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must
+ * be a valid ISO month format (yyyy-MM).
* @param {string=} required Sets `required` validation error key if the value is not entered.
* @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
* the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
@@ -35846,8 +34323,8 @@ var inputType = {
* @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
* that contains the regular expression body that will be converted to a regular expression
* as in the ngPattern directive.
- * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
- * does not match a RegExp found by evaluating the Angular expression given in the attribute value.
+ * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
+ * a RegExp found by evaluating the Angular expression given in the attribute value.
* If the expression evaluates to a RegExp object, then this is used directly.
* If the expression evaluates to a string, then it will be converted to a RegExp
* after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
@@ -35944,8 +34421,8 @@ var inputType = {
* @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
* that contains the regular expression body that will be converted to a regular expression
* as in the ngPattern directive.
- * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
- * does not match a RegExp found by evaluating the Angular expression given in the attribute value.
+ * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
+ * a RegExp found by evaluating the Angular expression given in the attribute value.
* If the expression evaluates to a RegExp object, then this is used directly.
* If the expression evaluates to a string, then it will be converted to a RegExp
* after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
@@ -36043,8 +34520,8 @@ var inputType = {
* @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
* that contains the regular expression body that will be converted to a regular expression
* as in the ngPattern directive.
- * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
- * does not match a RegExp found by evaluating the Angular expression given in the attribute value.
+ * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
+ * a RegExp found by evaluating the Angular expression given in the attribute value.
* If the expression evaluates to a RegExp object, then this is used directly.
* If the expression evaluates to a string, then it will be converted to a RegExp
* after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
@@ -36263,7 +34740,7 @@ function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
if (!$sniffer.android) {
var composing = false;
- element.on('compositionstart', function() {
+ element.on('compositionstart', function(data) {
composing = true;
});
@@ -36273,8 +34750,6 @@ function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
});
}
- var timeout;
-
var listener = function(ev) {
if (timeout) {
$browser.defer.cancel(timeout);
@@ -36304,6 +34779,8 @@ function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
if ($sniffer.hasEvent('input')) {
element.on('input', listener);
} else {
+ var timeout;
+
var deferListener = function(ev, input, origValue) {
if (!timeout) {
timeout = $browser.defer(function() {
@@ -36335,32 +34812,8 @@ function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
// or form autocomplete on newer browser, we need "change" event to catch it
element.on('change', listener);
- // Some native input types (date-family) have the ability to change validity without
- // firing any input/change events.
- // For these event types, when native validators are present and the browser supports the type,
- // check for validity changes on various DOM events.
- if (PARTIAL_VALIDATION_TYPES[type] && ctrl.$$hasNativeValidators && type === attr.type) {
- element.on(PARTIAL_VALIDATION_EVENTS, function(ev) {
- if (!timeout) {
- var validity = this[VALIDITY_STATE_PROPERTY];
- var origBadInput = validity.badInput;
- var origTypeMismatch = validity.typeMismatch;
- timeout = $browser.defer(function() {
- timeout = null;
- if (validity.badInput !== origBadInput || validity.typeMismatch !== origTypeMismatch) {
- listener(ev);
- }
- });
- }
- });
- }
-
ctrl.$render = function() {
- // Workaround for Firefox validation #12102.
- var value = ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue;
- if (element.val() !== value) {
- element.val(value);
- }
+ element.val(ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue);
};
}
@@ -36471,7 +34924,7 @@ function createDateInputType(type, regexp, parseDate, format) {
ctrl.$formatters.push(function(value) {
if (value && !isDate(value)) {
- throw ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value);
+ throw $ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value);
}
if (isValidDate(value)) {
previousDate = value;
@@ -36513,7 +34966,7 @@ function createDateInputType(type, regexp, parseDate, format) {
}
function parseObservedDateValue(val) {
- return isDefined(val) && !isDate(val) ? parseDate(val) || undefined : val;
+ return isDefined(val) ? (isDate(val) ? val : parseDate(val)) : undefined;
}
};
}
@@ -36524,7 +34977,11 @@ function badInputChecker(scope, element, attr, ctrl) {
if (nativeValidation) {
ctrl.$parsers.push(function(value) {
var validity = element.prop(VALIDITY_STATE_PROPERTY) || {};
- return validity.badInput || validity.typeMismatch ? undefined : value;
+ // Detect bug in FF35 for input[email] (https://bugzilla.mozilla.org/show_bug.cgi?id=1064430):
+ // - also sets validity.badInput (should only be validity.typeMismatch).
+ // - see http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#e-mail-state-(type=email)
+ // - can ignore this case as we can still read out the erroneous email...
+ return validity.badInput && !validity.typeMismatch ? undefined : value;
});
}
}
@@ -36543,7 +35000,7 @@ function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
ctrl.$formatters.push(function(value) {
if (!ctrl.$isEmpty(value)) {
if (!isNumber(value)) {
- throw ngModelMinErr('numfmt', 'Expected `{0}` to be a number', value);
+ throw $ngModelMinErr('numfmt', 'Expected `{0}` to be a number', value);
}
value = value.toString();
}
@@ -36636,7 +35093,7 @@ function parseConstantExpr($parse, context, name, expression, fallback) {
if (isDefined(expression)) {
parseFn = $parse(expression);
if (!parseFn.constant) {
- throw ngModelMinErr('constexpr', 'Expected constant expression for `{0}`, but saw ' +
+ throw minErr('ngModel')('constexpr', 'Expected constant expression for `{0}`, but saw ' +
'`{1}`.', name, expression);
}
return parseFn(context);
@@ -36696,8 +35153,8 @@ function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filt
* @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
* maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
* length.
- * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
- * does not match a RegExp found by evaluating the Angular expression given in the attribute value.
+ * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
+ * a RegExp found by evaluating the Angular expression given in the attribute value.
* If the expression evaluates to a RegExp object, then this is used directly.
* If the expression evaluates to a string, then it will be converted to a RegExp
* after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
@@ -36735,8 +35192,8 @@ function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filt
* @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
* maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
* length.
- * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
- * value does not match a RegExp found by evaluating the Angular expression given in the attribute value.
+ * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
+ * a RegExp found by evaluating the Angular expression given in the attribute value.
* If the expression evaluates to a RegExp object, then this is used directly.
* If the expression evaluates to a string, then it will be converted to a RegExp
* after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
@@ -37004,7 +35461,7 @@ var ngBindDirective = ['$compile', function($compile) {
$compile.$$addBindingInfo(element, attr.ngBind);
element = element[0];
scope.$watch(attr.ngBind, function ngBindWatchAction(value) {
- element.textContent = isUndefined(value) ? '' : value;
+ element.textContent = value === undefined ? '' : value;
});
};
}
@@ -37072,7 +35529,7 @@ var ngBindTemplateDirective = ['$interpolate', '$compile', function($interpolate
$compile.$$addBindingInfo(element, interpolateFn.expressions);
element = element[0];
attr.$observe('ngBindTemplate', function(value) {
- element.textContent = isUndefined(value) ? '' : value;
+ element.textContent = value === undefined ? '' : value;
});
};
}
@@ -37375,10 +35832,9 @@ function classDirective(name, selector) {
* new classes added.
*
* @animations
- * | Animation | Occurs |
- * |----------------------------------|-------------------------------------|
- * | {@link ng.$animate#addClass addClass} | just before the class is applied to the element |
- * | {@link ng.$animate#removeClass removeClass} | just before the class is removed from the element |
+ * **add** - happens just before the class is applied to the elements
+ *
+ * **remove** - happens just before the class is removed from the element
*
* @element ANY
* @param {expression} ngClass {@link guide/expression Expression} to eval. The result
@@ -37490,6 +35946,7 @@ function classDirective(name, selector) {
</file>
<file name="style.css">
.base-class {
+ -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
}
@@ -37922,29 +36379,27 @@ var ngControllerDirective = [function() {
*
* @element html
* @description
- *
- * Angular has some features that can break certain
- * [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) rules.
- *
- * If you intend to implement these rules then you must tell Angular not to use these features.
+ * Enables [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) support.
*
* This is necessary when developing things like Google Chrome Extensions or Universal Windows Apps.
*
+ * CSP forbids apps to use `eval` or `Function(string)` generated functions (among other things).
+ * For Angular to be CSP compatible there are only two things that we need to do differently:
*
- * The following rules affect Angular:
+ * - don't use `Function` constructor to generate optimized value getters
+ * - don't inject custom stylesheet into the document
*
- * * `unsafe-eval`: this rule forbids apps to use `eval` or `Function(string)` generated functions
- * (among other things). Angular makes use of this in the {@link $parse} service to provide a 30%
- * increase in the speed of evaluating Angular expressions.
+ * AngularJS uses `Function(string)` generated functions as a speed optimization. Applying the `ngCsp`
+ * directive will cause Angular to use CSP compatibility mode. When this mode is on AngularJS will
+ * evaluate all expressions up to 30% slower than in non-CSP mode, but no security violations will
+ * be raised.
*
- * * `unsafe-inline`: this rule forbids apps from inject custom styles into the document. Angular
- * makes use of this to include some CSS rules (e.g. {@link ngCloak} and {@link ngHide}).
- * To make these directives work when a CSP rule is blocking inline styles, you must link to the
- * `angular-csp.css` in your HTML manually.
+ * CSP forbids JavaScript to inline stylesheet rules. In non CSP mode Angular automatically
+ * includes some CSS rules (e.g. {@link ng.directive:ngCloak ngCloak}).
+ * To make those directives work in CSP mode, include the `angular-csp.css` manually.
*
- * If you do not provide `ngCsp` then Angular tries to autodetect if CSP is blocking unsafe-eval
- * and automatically deactivates this feature in the {@link $parse} service. This autodetection,
- * however, triggers a CSP error to be logged in the console:
+ * Angular tries to autodetect if CSP is active and automatically turn on the CSP-safe mode. This
+ * autodetection however triggers a CSP error to be logged in the console:
*
* ```
* Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of
@@ -37953,39 +36408,11 @@ var ngControllerDirective = [function() {
* ```
*
* This error is harmless but annoying. To prevent the error from showing up, put the `ngCsp`
- * directive on an element of the HTML document that appears before the `<script>` tag that loads
- * the `angular.js` file.
+ * directive on the root element of the application or on the `angular.js` script tag, whichever
+ * appears first in the html document.
*
* *Note: This directive is only available in the `ng-csp` and `data-ng-csp` attribute form.*
*
- * You can specify which of the CSP related Angular features should be deactivated by providing
- * a value for the `ng-csp` attribute. The options are as follows:
- *
- * * no-inline-style: this stops Angular from injecting CSS styles into the DOM
- *
- * * no-unsafe-eval: this stops Angular from optimizing $parse with unsafe eval of strings
- *
- * You can use these values in the following combinations:
- *
- *
- * * No declaration means that Angular will assume that you can do inline styles, but it will do
- * a runtime check for unsafe-eval. E.g. `<body>`. This is backwardly compatible with previous versions
- * of Angular.
- *
- * * A simple `ng-csp` (or `data-ng-csp`) attribute will tell Angular to deactivate both inline
- * styles and unsafe eval. E.g. `<body ng-csp>`. This is backwardly compatible with previous versions
- * of Angular.
- *
- * * Specifying only `no-unsafe-eval` tells Angular that we must not use eval, but that we can inject
- * inline styles. E.g. `<body ng-csp="no-unsafe-eval">`.
- *
- * * Specifying only `no-inline-style` tells Angular that we must not inject styles, but that we can
- * run eval - no automatic check for unsafe eval will occur. E.g. `<body ng-csp="no-inline-style">`
- *
- * * Specifying both `no-unsafe-eval` and `no-inline-style` tells Angular that we must not inject
- * styles nor use eval, which is the same as an empty: ng-csp.
- * E.g.`<body ng-csp="no-inline-style;no-unsafe-eval">`
- *
* @example
* This example shows how to apply the `ngCsp` directive to the `html` tag.
```html
@@ -38117,7 +36544,7 @@ var ngControllerDirective = [function() {
// ngCsp is not implemented as a proper directive any more, because we need it be processed while we
// bootstrap the system (before $parse is instantiated), for this reason we just have
-// the csp() fn that looks for the `ng-csp` attribute anywhere in the current doc
+// the csp.isActive() fn that looks for ng-csp attribute anywhere in the current doc
/**
* @ngdoc directive
@@ -38637,10 +37064,8 @@ forEach(
* and `leave` effects.
*
* @animations
- * | Animation | Occurs |
- * |----------------------------------|-------------------------------------|
- * | {@link ng.$animate#enter enter} | just after the `ngIf` contents change and a new DOM element is created and injected into the `ngIf` container |
- * | {@link ng.$animate#leave leave} | just before the `ngIf` contents are removed from the DOM |
+ * enter - happens just after the `ngIf` contents change and a new DOM element is created and injected into the `ngIf` container
+ * leave - happens just before the `ngIf` contents are removed from the DOM
*
* @element ANY
* @scope
@@ -38666,6 +37091,7 @@ forEach(
}
.animate-if.ng-enter, .animate-if.ng-leave {
+ -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
}
@@ -38681,7 +37107,7 @@ forEach(
</file>
</example>
*/
-var ngIfDirective = ['$animate', '$compile', function($animate, $compile) {
+var ngIfDirective = ['$animate', function($animate) {
return {
multiElement: true,
transclude: 'element',
@@ -38697,7 +37123,7 @@ var ngIfDirective = ['$animate', '$compile', function($animate, $compile) {
if (!childScope) {
$transclude(function(clone, newScope) {
childScope = newScope;
- clone[clone.length++] = $compile.$$createComment('end ngIf', $attr.ngIf);
+ clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' ');
// Note: We only need the first/last node of the cloned nodes.
// However, we need to keep the reference to the jqlite wrapper as it might be changed later
// by a directive with templateUrl when its template arrives.
@@ -38752,10 +37178,8 @@ var ngIfDirective = ['$animate', '$compile', function($animate, $compile) {
* access on some browsers.
*
* @animations
- * | Animation | Occurs |
- * |----------------------------------|-------------------------------------|
- * | {@link ng.$animate#enter enter} | when the expression changes, on the new include |
- * | {@link ng.$animate#leave leave} | when the expression changes, on the old include |
+ * enter - animation is used to bring new content into the browser.
+ * leave - animation is used to animate existing content away.
*
* The enter and leave animation occur concurrently.
*
@@ -38765,13 +37189,7 @@ var ngIfDirective = ['$animate', '$compile', function($animate, $compile) {
* @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant,
* make sure you wrap it in **single** quotes, e.g. `src="'myPartialTemplate.html'"`.
* @param {string=} onload Expression to evaluate when a new partial is loaded.
- * <div class="alert alert-warning">
- * **Note:** When using onload on SVG elements in IE11, the browser will try to call
- * a function with the name on the window element, which will usually throw a
- * "function is undefined" error. To fix this, you can instead use `data-onload` or a
- * different form that {@link guide/directive#normalization matches} `onload`.
- * </div>
- *
+ *
* @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll
* $anchorScroll} to scroll the viewport after the content is loaded.
*
@@ -38822,6 +37240,7 @@ var ngIfDirective = ['$animate', '$compile', function($animate, $compile) {
}
.slide-animate.ng-enter, .slide-animate.ng-leave {
+ -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
position:absolute;
@@ -38963,8 +37382,6 @@ var ngIncludeDirective = ['$templateRequest', '$anchorScroll', '$animate',
//set the 2nd param to true to ignore the template request error so that the inner
//contents and scope can be cleaned up.
$templateRequest(src, true).then(function(response) {
- if (scope.$$destroyed) return;
-
if (thisChangeId !== changeCounter) return;
var newScope = scope.$new();
ctrl.template = response;
@@ -38986,8 +37403,6 @@ var ngIncludeDirective = ['$templateRequest', '$anchorScroll', '$animate',
currentScope.$emit('$includeContentLoaded', src);
scope.$eval(onloadExp);
}, function() {
- if (scope.$$destroyed) return;
-
if (thisChangeId === changeCounter) {
cleanupLastIncludeContent();
scope.$emit('$includeContentError', src);
@@ -39016,7 +37431,7 @@ var ngIncludeFillContentDirective = ['$compile',
priority: -400,
require: 'ngInclude',
link: function(scope, $element, $attr, ctrl) {
- if (toString.call($element[0]).match(/SVG/)) {
+ if (/SVG/.test($element[0].toString())) {
// WebKit: https://bugs.webkit.org/show_bug.cgi?id=135698 --- SVG elements do not
// support innerHTML, so detect this here and try to generate the contents
// specially.
@@ -39044,18 +37459,16 @@ var ngIncludeFillContentDirective = ['$compile',
* current scope.
*
* <div class="alert alert-danger">
- * This directive can be abused to add unnecessary amounts of logic into your templates.
- * There are only a few appropriate uses of `ngInit`, such as for aliasing special properties of
- * {@link ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below; and for injecting data via
- * server side scripting. Besides these few cases, you should use {@link guide/controller controllers}
- * rather than `ngInit` to initialize values on a scope.
+ * The only appropriate use of `ngInit` is for aliasing special properties of
+ * {@link ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below. Besides this case, you
+ * should use {@link guide/controller controllers} rather than `ngInit`
+ * to initialize values on a scope.
* </div>
- *
* <div class="alert alert-warning">
- * **Note**: If you have assignment in `ngInit` along with a {@link ng.$filter `filter`}, make
- * sure you have parentheses to ensure correct operator precedence:
+ * **Note**: If you have assignment in `ngInit` along with {@link ng.$filter `$filter`}, make
+ * sure you have parenthesis for correct precedence:
* <pre class="prettyprint">
- * `<div ng-init="test1 = ($index | toString)"></div>`
+ * `<div ng-init="test1 = (data | orderBy:'name')"></div>`
* </pre>
* </div>
*
@@ -39168,7 +37581,7 @@ var ngInitDirective = ngDirective({
* </file>
* </example>
*
- * ### Example - splitting on newline
+ * ### Example - splitting on whitespace
* <example name="ngList-directive-newlines">
* <file name="index.html">
* <textarea ng-model="list" ng-list="&#10;" ng-trim="false"></textarea>
@@ -39245,19 +37658,16 @@ var VALID_CLASS = 'ng-valid',
DIRTY_CLASS = 'ng-dirty',
UNTOUCHED_CLASS = 'ng-untouched',
TOUCHED_CLASS = 'ng-touched',
- PENDING_CLASS = 'ng-pending',
- EMPTY_CLASS = 'ng-empty',
- NOT_EMPTY_CLASS = 'ng-not-empty';
+ PENDING_CLASS = 'ng-pending';
+
-var ngModelMinErr = minErr('ngModel');
+var $ngModelMinErr = new minErr('ngModel');
/**
* @ngdoc type
* @name ngModel.NgModelController
*
- * @property {*} $viewValue The actual value from the control's view. For `input` elements, this is a
- * String. See {@link ngModel.NgModelController#$setViewValue} for information about when the $viewValue
- * is set.
+ * @property {string} $viewValue Actual string value in the view.
* @property {*} $modelValue The value in the model that the control is bound to.
* @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever
the control reads value from the DOM. The functions are called in array order, each passing
@@ -39471,7 +37881,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
this.$$success = {}; // keep valid keys here
this.$pending = undefined; // keep pending keys here
this.$name = $interpolate($attr.name || '', false)($scope);
- this.$$parentForm = nullFormCtrl;
+
var parsedNgModel = $parse($attr.ngModel),
parsedNgModelAssign = parsedNgModel.assign,
@@ -39496,13 +37906,13 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
};
ngModelSet = function($scope, newValue) {
if (isFunction(parsedNgModel($scope))) {
- invokeModelSetter($scope, {$$$p: newValue});
+ invokeModelSetter($scope, {$$$p: ctrl.$modelValue});
} else {
- parsedNgModelAssign($scope, newValue);
+ parsedNgModelAssign($scope, ctrl.$modelValue);
}
};
} else if (!parsedNgModel.assign) {
- throw ngModelMinErr('nonassign', "Expression '{0}' is non-assignable. Element: {1}",
+ throw $ngModelMinErr('nonassign', "Expression '{0}' is non-assignable. Element: {1}",
$attr.ngModel, startingTag($element));
}
};
@@ -39523,7 +37933,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
* the `$viewValue` are different from last time.
*
* Since `ng-model` does not do a deep watch, `$render()` is only invoked if the values of
- * `$modelValue` and `$viewValue` are actually different from their previous values. If `$modelValue`
+ * `$modelValue` and `$viewValue` are actually different from their previous value. If `$modelValue`
* or `$viewValue` are objects (rather than a string or number) then `$render()` will not be
* invoked if you only change a property on the objects.
*/
@@ -39551,18 +37961,8 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
return isUndefined(value) || value === '' || value === null || value !== value;
};
- this.$$updateEmptyClasses = function(value) {
- if (ctrl.$isEmpty(value)) {
- $animate.removeClass($element, NOT_EMPTY_CLASS);
- $animate.addClass($element, EMPTY_CLASS);
- } else {
- $animate.removeClass($element, EMPTY_CLASS);
- $animate.addClass($element, NOT_EMPTY_CLASS);
- }
- };
-
-
- var currentValidationRunId = 0;
+ var parentForm = $element.inheritedData('$formController') || nullFormCtrl,
+ currentValidationRunId = 0;
/**
* @ngdoc method
@@ -39595,6 +37995,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
unset: function(object, property) {
delete object[property];
},
+ parentForm: parentForm,
$animate: $animate
});
@@ -39632,7 +38033,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
ctrl.$pristine = false;
$animate.removeClass($element, PRISTINE_CLASS);
$animate.addClass($element, DIRTY_CLASS);
- ctrl.$$parentForm.$setDirty();
+ parentForm.$setDirty();
};
/**
@@ -39679,14 +38080,11 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
* which may be caused by a pending debounced event or because the input is waiting for a some
* future event.
*
- * If you have an input that uses `ng-model-options` to set up debounced updates or updates that
- * depend on special events such as blur, you can have a situation where there is a period when
- * the `$viewValue` is out of sync with the ngModel's `$modelValue`.
- *
- * In this case, you can use `$rollbackViewValue()` to manually cancel the debounced / future update
- * and reset the input to the last committed view value.
+ * If you have an input that uses `ng-model-options` to set up debounced events or events such
+ * as blur you can have a situation where there is a period when the `$viewValue`
+ * is out of synch with the ngModel's `$modelValue`.
*
- * It is also possible that you run into difficulties if you try to update the ngModel's `$modelValue`
+ * In this case, you can run into difficulties if you try to update the ngModel's `$modelValue`
* programmatically before these debounced/future events have resolved/occurred, because Angular's
* dirty checking mechanism is not able to tell whether the model has actually changed or not.
*
@@ -39699,63 +38097,39 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
* angular.module('cancel-update-example', [])
*
* .controller('CancelUpdateController', ['$scope', function($scope) {
- * $scope.model = {};
- *
- * $scope.setEmpty = function(e, value, rollback) {
+ * $scope.resetWithCancel = function(e) {
+ * if (e.keyCode == 27) {
+ * $scope.myForm.myInput1.$rollbackViewValue();
+ * $scope.myValue = '';
+ * }
+ * };
+ * $scope.resetWithoutCancel = function(e) {
* if (e.keyCode == 27) {
- * e.preventDefault();
- * if (rollback) {
- * $scope.myForm[value].$rollbackViewValue();
- * }
- * $scope.model[value] = '';
+ * $scope.myValue = '';
* }
* };
* }]);
* </file>
* <file name="index.html">
* <div ng-controller="CancelUpdateController">
- * <p>Both of these inputs are only updated if they are blurred. Hitting escape should
- * empty them. Follow these steps and observe the difference:</p>
- * <ol>
- * <li>Type something in the input. You will see that the model is not yet updated</li>
- * <li>Press the Escape key.
- * <ol>
- * <li> In the first example, nothing happens, because the model is already '', and no
- * update is detected. If you blur the input, the model will be set to the current view.
- * </li>
- * <li> In the second example, the pending update is cancelled, and the input is set back
- * to the last committed view value (''). Blurring the input does nothing.
- * </li>
- * </ol>
- * </li>
- * </ol>
+ * <p>Try typing something in each input. See that the model only updates when you
+ * blur off the input.
+ * </p>
+ * <p>Now see what happens if you start typing then press the Escape key</p>
*
* <form name="myForm" ng-model-options="{ updateOn: 'blur' }">
- * <div>
- * <p id="inputDescription1">Without $rollbackViewValue():</p>
- * <input name="value1" aria-describedby="inputDescription1" ng-model="model.value1"
- * ng-keydown="setEmpty($event, 'value1')">
- * value1: "{{ model.value1 }}"
- * </div>
- *
- * <div>
- * <p id="inputDescription2">With $rollbackViewValue():</p>
- * <input name="value2" aria-describedby="inputDescription2" ng-model="model.value2"
- * ng-keydown="setEmpty($event, 'value2', true)">
- * value2: "{{ model.value2 }}"
- * </div>
+ * <p id="inputDescription1">With $rollbackViewValue()</p>
+ * <input name="myInput1" aria-describedby="inputDescription1" ng-model="myValue"
+ * ng-keydown="resetWithCancel($event)"><br/>
+ * myValue: "{{ myValue }}"
+ *
+ * <p id="inputDescription2">Without $rollbackViewValue()</p>
+ * <input name="myInput2" aria-describedby="inputDescription2" ng-model="myValue"
+ * ng-keydown="resetWithoutCancel($event)"><br/>
+ * myValue: "{{ myValue }}"
* </form>
* </div>
* </file>
- <file name="style.css">
- div {
- display: table-cell;
- }
- div:nth-child(1) {
- padding-right: 30px;
- }
-
- </file>
* </example>
*/
this.$rollbackViewValue = function() {
@@ -39829,7 +38203,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
function processParseErrors() {
var errorKey = ctrl.$$parserName || 'parse';
- if (isUndefined(parserValid)) {
+ if (parserValid === undefined) {
setValidity(errorKey, null);
} else {
if (!parserValid) {
@@ -39869,13 +38243,13 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
forEach(ctrl.$asyncValidators, function(validator, name) {
var promise = validator(modelValue, viewValue);
if (!isPromiseLike(promise)) {
- throw ngModelMinErr('nopromise',
+ throw $ngModelMinErr("$asyncValidators",
"Expected asynchronous validator to return a promise but got '{0}' instead.", promise);
}
setValidity(name, undefined);
validatorPromises.push(promise.then(function() {
setValidity(name, true);
- }, function() {
+ }, function(error) {
allValid = false;
setValidity(name, false);
}));
@@ -39925,7 +38299,6 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
if (ctrl.$$lastCommittedViewValue === viewValue && (viewValue !== '' || !ctrl.$$hasNativeValidators)) {
return;
}
- ctrl.$$updateEmptyClasses(viewValue);
ctrl.$$lastCommittedViewValue = viewValue;
// change to dirty
@@ -40000,47 +38373,37 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
* @description
* Update the view value.
*
- * This method should be called when a control wants to change the view value; typically,
- * this is done from within a DOM event handler. For example, the {@link ng.directive:input input}
- * directive calls it when the value of the input changes and {@link ng.directive:select select}
- * calls it when an option is selected.
+ * This method should be called when an input directive want to change the view value; typically,
+ * this is done from within a DOM event handler.
+ *
+ * For example {@link ng.directive:input input} calls it when the value of the input changes and
+ * {@link ng.directive:select select} calls it when an option is selected.
+ *
+ * If the new `value` is an object (rather than a string or a number), we should make a copy of the
+ * object before passing it to `$setViewValue`. This is because `ngModel` does not perform a deep
+ * watch of objects, it only looks for a change of identity. If you only change the property of
+ * the object then ngModel will not realise that the object has changed and will not invoke the
+ * `$parsers` and `$validators` pipelines.
+ *
+ * For this reason, you should not change properties of the copy once it has been passed to
+ * `$setViewValue`. Otherwise you may cause the model value on the scope to change incorrectly.
*
- * When `$setViewValue` is called, the new `value` will be staged for committing through the `$parsers`
+ * When this method is called, the new `value` will be staged for committing through the `$parsers`
* and `$validators` pipelines. If there are no special {@link ngModelOptions} specified then the staged
* value sent directly for processing, finally to be applied to `$modelValue` and then the
- * **expression** specified in the `ng-model` attribute. Lastly, all the registered change listeners,
- * in the `$viewChangeListeners` list, are called.
+ * **expression** specified in the `ng-model` attribute.
+ *
+ * Lastly, all the registered change listeners, in the `$viewChangeListeners` list, are called.
*
* In case the {@link ng.directive:ngModelOptions ngModelOptions} directive is used with `updateOn`
* and the `default` trigger is not listed, all those actions will remain pending until one of the
* `updateOn` events is triggered on the DOM element.
* All these actions will be debounced if the {@link ng.directive:ngModelOptions ngModelOptions}
* directive is used with a custom debounce for this particular event.
- * Note that a `$digest` is only triggered once the `updateOn` events are fired, or if `debounce`
- * is specified, once the timer runs out.
- *
- * When used with standard inputs, the view value will always be a string (which is in some cases
- * parsed into another type, such as a `Date` object for `input[date]`.)
- * However, custom controls might also pass objects to this method. In this case, we should make
- * a copy of the object before passing it to `$setViewValue`. This is because `ngModel` does not
- * perform a deep watch of objects, it only looks for a change of identity. If you only change
- * the property of the object then ngModel will not realize that the object has changed and
- * will not invoke the `$parsers` and `$validators` pipelines. For this reason, you should
- * not change properties of the copy once it has been passed to `$setViewValue`.
- * Otherwise you may cause the model value on the scope to change incorrectly.
- *
- * <div class="alert alert-info">
- * In any case, the value passed to the method should always reflect the current value
- * of the control. For example, if you are calling `$setViewValue` for an input element,
- * you should pass the input DOM value. Otherwise, the control and the scope model become
- * out of sync. It's also important to note that `$setViewValue` does not call `$render` or change
- * the control's DOM value in any way. If we want to change the control's DOM value
- * programmatically, we should update the `ngModel` scope expression. Its new value will be
- * picked up by the model controller, which will run it through the `$formatters`, `$render` it
- * to update the DOM, and finally call `$validate` on it.
- * </div>
*
- * @param {*} value value from the view.
+ * Note that calling this function does not trigger a `$digest`.
+ *
+ * @param {string} value Value from the view.
* @param {string} trigger Event that triggered the update.
*/
this.$setViewValue = function(value, trigger) {
@@ -40108,7 +38471,6 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
viewValue = formatters[idx](viewValue);
}
if (ctrl.$viewValue !== viewValue) {
- ctrl.$$updateEmptyClasses(viewValue);
ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue;
ctrl.$render();
@@ -40139,8 +38501,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
* require.
* - Providing validation behavior (i.e. required, number, email, url).
* - Keeping the state of the control (valid/invalid, dirty/pristine, touched/untouched, validation errors).
- * - Setting related css classes on the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`, `ng-touched`,
- * `ng-untouched`, `ng-empty`, `ng-not-empty`) including animations.
+ * - Setting related css classes on the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`, `ng-touched`, `ng-untouched`) including animations.
* - Registering the control with its parent {@link ng.directive:form form}.
*
* Note: `ngModel` will try to bind to the property given by evaluating the expression on the
@@ -40168,22 +38529,6 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
* - {@link ng.directive:select select}
* - {@link ng.directive:textarea textarea}
*
- * # Complex Models (objects or collections)
- *
- * By default, `ngModel` watches the model by reference, not value. This is important to know when
- * binding inputs to models that are objects (e.g. `Date`) or collections (e.g. arrays). If only properties of the
- * object or collection change, `ngModel` will not be notified and so the input will not be re-rendered.
- *
- * The model must be assigned an entirely new object or collection before a re-rendering will occur.
- *
- * Some directives have options that will cause them to use a custom `$watchCollection` on the model expression
- * - for example, `ngOptions` will do so when a `track by` clause is included in the comprehension expression or
- * if the select is given the `multiple` attribute.
- *
- * The `$watchCollection()` method only does a shallow comparison, meaning that changing properties deeper than the
- * first level of the object (or only changing the properties of an item in the collection if it's an array) will still
- * not trigger a re-rendering of the model.
- *
* # CSS classes
* The following CSS classes are added and removed on the associated input/select/textarea element
* depending on the validity of the model.
@@ -40197,16 +38542,13 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
* - `ng-touched`: the control has been blurred
* - `ng-untouched`: the control hasn't been blurred
* - `ng-pending`: any `$asyncValidators` are unfulfilled
- * - `ng-empty`: the view does not contain a value or the value is deemed "empty", as defined
- * by the {@link ngModel.NgModelController#$isEmpty} method
- * - `ng-not-empty`: the view contains a non-empty value
*
* Keep in mind that ngAnimate can detect each of these classes when added and removed.
*
* ## Animation Hooks
*
* Animations within models are triggered when any of the associated CSS classes are added and removed
- * on the input element which is attached to the model. These classes include: `.ng-pristine`, `.ng-dirty`,
+ * on the input element which is attached to the model. These classes are: `.ng-pristine`, `.ng-dirty`,
* `.ng-invalid` and `.ng-valid` as well as any other validations that are performed on the model itself.
* The animations that are triggered within ngModel are similar to how they work in ngClass and
* animations can be hooked into using CSS transitions, keyframes as well as JS animations.
@@ -40238,6 +38580,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
</script>
<style>
.my-input {
+ -webkit-transition:all linear 0.5s;
transition:all linear 0.5s;
background: transparent;
}
@@ -40324,7 +38667,7 @@ var ngModelDirective = ['$rootScope', function($rootScope) {
return {
pre: function ngModelPreLink(scope, element, attr, ctrls) {
var modelCtrl = ctrls[0],
- formCtrl = ctrls[1] || modelCtrl.$$parentForm;
+ formCtrl = ctrls[1] || nullFormCtrl;
modelCtrl.$$setOptions(ctrls[2] && ctrls[2].$options);
@@ -40333,12 +38676,12 @@ var ngModelDirective = ['$rootScope', function($rootScope) {
attr.$observe('name', function(newValue) {
if (modelCtrl.$name !== newValue) {
- modelCtrl.$$parentForm.$$renameControl(modelCtrl, newValue);
+ formCtrl.$$renameControl(modelCtrl, newValue);
}
});
scope.$on('$destroy', function() {
- modelCtrl.$$parentForm.$removeControl(modelCtrl);
+ formCtrl.$removeControl(modelCtrl);
});
},
post: function ngModelPostLink(scope, element, attr, ctrls) {
@@ -40349,7 +38692,7 @@ var ngModelDirective = ['$rootScope', function($rootScope) {
});
}
- element.on('blur', function() {
+ element.on('blur', function(ev) {
if (modelCtrl.$touched) return;
if ($rootScope.$$phase) {
@@ -40430,13 +38773,12 @@ var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/;
</label><br />
</form>
<pre>user.name = <span ng-bind="user.name"></span></pre>
- <pre>user.data = <span ng-bind="user.data"></span></pre>
</div>
</file>
<file name="app.js">
angular.module('optionsExample', [])
.controller('ExampleController', ['$scope', function($scope) {
- $scope.user = { name: 'John', data: '' };
+ $scope.user = { name: 'say', data: '' };
$scope.cancel = function(e) {
if (e.keyCode == 27) {
@@ -40451,20 +38793,20 @@ var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/;
var other = element(by.model('user.data'));
it('should allow custom events', function() {
- input.sendKeys(' Doe');
+ input.sendKeys(' hello');
input.click();
- expect(model.getText()).toEqual('John');
+ expect(model.getText()).toEqual('say');
other.click();
- expect(model.getText()).toEqual('John Doe');
+ expect(model.getText()).toEqual('say hello');
});
it('should $rollbackViewValue when model changes', function() {
- input.sendKeys(' Doe');
- expect(input.getAttribute('value')).toEqual('John Doe');
+ input.sendKeys(' hello');
+ expect(input.getAttribute('value')).toEqual('say hello');
input.sendKeys(protractor.Key.ESCAPE);
- expect(input.getAttribute('value')).toEqual('John');
+ expect(input.getAttribute('value')).toEqual('say');
other.click();
- expect(model.getText()).toEqual('John');
+ expect(model.getText()).toEqual('say');
});
</file>
</example>
@@ -40490,7 +38832,7 @@ var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/;
<file name="app.js">
angular.module('optionsExample', [])
.controller('ExampleController', ['$scope', function($scope) {
- $scope.user = { name: 'Igor' };
+ $scope.user = { name: 'say' };
}]);
</file>
</example>
@@ -40534,7 +38876,7 @@ var ngModelOptionsDirective = function() {
var that = this;
this.$options = copy($scope.$eval($attrs.ngModelOptions));
// Allow adding/overriding bound events
- if (isDefined(this.$options.updateOn)) {
+ if (this.$options.updateOn !== undefined) {
this.$options.updateOnDefault = false;
// extract "default" pseudo-event from list of events that can trigger a model update
this.$options.updateOn = trim(this.$options.updateOn.replace(DEFAULT_REGEXP, function() {
@@ -40557,6 +38899,7 @@ function addSetValidityMethod(context) {
classCache = {},
set = context.set,
unset = context.unset,
+ parentForm = context.parentForm,
$animate = context.$animate;
classCache[INVALID_CLASS] = !(classCache[VALID_CLASS] = $element.hasClass(VALID_CLASS));
@@ -40564,7 +38907,7 @@ function addSetValidityMethod(context) {
ctrl.$setValidity = setValidity;
function setValidity(validationErrorKey, state, controller) {
- if (isUndefined(state)) {
+ if (state === undefined) {
createAndSet('$pending', validationErrorKey, controller);
} else {
unsetAndCleanup('$pending', validationErrorKey, controller);
@@ -40608,7 +38951,7 @@ function addSetValidityMethod(context) {
}
toggleValidationCss(validationErrorKey, combinedState);
- ctrl.$$parentForm.$setValidity(validationErrorKey, combinedState, ctrl);
+ parentForm.$setValidity(validationErrorKey, combinedState, ctrl);
}
function createAndSet(name, value, controller) {
@@ -40723,27 +39066,19 @@ var ngOptionsMinErr = minErr('ngOptions');
*
* ## Complex Models (objects or collections)
*
- * By default, `ngModel` watches the model by reference, not value. This is important to know when
- * binding the select to a model that is an object or a collection.
+ * **Note:** By default, `ngModel` watches the model by reference, not value. This is important when
+ * binding any input directive to a model that is an object or a collection.
*
- * One issue occurs if you want to preselect an option. For example, if you set
- * the model to an object that is equal to an object in your collection, `ngOptions` won't be able to set the selection,
- * because the objects are not identical. So by default, you should always reference the item in your collection
- * for preselections, e.g.: `$scope.selected = $scope.collection[3]`.
- *
- * Another solution is to use a `track by` clause, because then `ngOptions` will track the identity
- * of the item not by reference, but by the result of the `track by` expression. For example, if your
- * collection items have an id property, you would `track by item.id`.
- *
- * A different issue with objects or collections is that ngModel won't detect if an object property or
- * a collection item changes. For that reason, `ngOptions` additionally watches the model using
- * `$watchCollection`, when the expression contains a `track by` clause or the the select has the `multiple` attribute.
- * This allows ngOptions to trigger a re-rendering of the options even if the actual object/collection
- * has not changed identity, but only a property on the object or an item in the collection changes.
+ * Since this is a common situation for `ngOptions` the directive additionally watches the model using
+ * `$watchCollection` when the select has the `multiple` attribute or when there is a `track by` clause in
+ * the options expression. This allows ngOptions to trigger a re-rendering of the options even if the actual
+ * object/collection has not changed identity but only a property on the object or an item in the collection
+ * changes.
*
* Note that `$watchCollection` does a shallow comparison of the properties of the object (or the items in the collection
- * if the model is an array). This means that changing a property deeper than the first level inside the
- * object/collection will not trigger a re-rendering.
+ * if the model is an array). This means that changing a property deeper inside the object/collection that the
+ * first level will not trigger a re-rendering.
+ *
*
* ## `select` **`as`**
*
@@ -40756,13 +39091,17 @@ var ngOptionsMinErr = minErr('ngOptions');
* ### `select` **`as`** and **`track by`**
*
* <div class="alert alert-warning">
- * Be careful when using `select` **`as`** and **`track by`** in the same expression.
+ * Do not use `select` **`as`** and **`track by`** in the same expression. They are not designed to work together.
* </div>
*
- * Given this array of items on the $scope:
+ * Consider the following example:
+ *
+ * ```html
+ * <select ng-options="item.subItem as item.label for item in values track by item.id" ng-model="selected">
+ * ```
*
* ```js
- * $scope.items = [{
+ * $scope.values = [{
* id: 1,
* label: 'aLabel',
* subItem: { name: 'aSubItem' }
@@ -40771,33 +39110,20 @@ var ngOptionsMinErr = minErr('ngOptions');
* label: 'bLabel',
* subItem: { name: 'bSubItem' }
* }];
- * ```
- *
- * This will work:
*
- * ```html
- * <select ng-options="item as item.label for item in items track by item.id" ng-model="selected"></select>
- * ```
- * ```js
- * $scope.selected = $scope.items[0];
+ * $scope.selected = { name: 'aSubItem' };
* ```
*
- * but this will not work:
+ * With the purpose of preserving the selection, the **`track by`** expression is always applied to the element
+ * of the data source (to `item` in this example). To calculate whether an element is selected, we do the
+ * following:
*
- * ```html
- * <select ng-options="item.subItem as item.label for item in items track by item.id" ng-model="selected"></select>
- * ```
- * ```js
- * $scope.selected = $scope.items[0].subItem;
- * ```
- *
- * In both examples, the **`track by`** expression is applied successfully to each `item` in the
- * `items` array. Because the selected option has been set programmatically in the controller, the
- * **`track by`** expression is also applied to the `ngModel` value. In the first example, the
- * `ngModel` value is `items[0]` and the **`track by`** expression evaluates to `items[0].id` with
- * no issue. In the second example, the `ngModel` value is `items[0].subItem` and the **`track by`**
- * expression evaluates to `items[0].subItem.id` (which is undefined). As a result, the model value
- * is not matched against any `<option>` and the `<select>` appears as having no selected value.
+ * 1. Apply **`track by`** to the elements in the array. In the example: `[1, 2]`
+ * 2. Apply **`track by`** to the already selected value in `ngModel`.
+ * In the example: this is not possible as **`track by`** refers to `item.id`, but the selected
+ * value from `ngModel` is `{name: 'aSubItem'}`, so the **`track by`** expression is applied to
+ * a wrong object, the selected element can't be found, `<select>` is always reset to the "not
+ * selected" option.
*
*
* @param {string} ngModel Assignable angular expression to data-bind to.
@@ -41032,8 +39358,8 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
var value = optionValues[key];
- var locals = getLocals(value, key);
- var selectValue = getTrackByValueFn(value, locals);
+ var locals = getLocals(optionValues[key], key);
+ var selectValue = getTrackByValueFn(optionValues[key], locals);
watchedArray.push(selectValue);
// Only need to watch the displayFn if there is a specific label expression
@@ -41099,10 +39425,17 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
var optionTemplate = document.createElement('option'),
optGroupTemplate = document.createElement('optgroup');
- function ngOptionsPostLink(scope, selectElement, attr, ctrls) {
+ return {
+ restrict: 'A',
+ terminal: true,
+ require: ['select', '?ngModel'],
+ link: function(scope, selectElement, attr, ctrls) {
- var selectCtrl = ctrls[0];
+ // if ngModel is not defined, we don't need to do anything
var ngModelCtrl = ctrls[1];
+ if (!ngModelCtrl) return;
+
+ var selectCtrl = ctrls[0];
var multiple = attr.multiple;
// The emptyOption allows the application developer to provide their own custom "empty"
@@ -41151,6 +39484,7 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
unknownOption.remove();
};
+
// Update the controller methods for multiple selectable options
if (!multiple) {
@@ -41158,20 +39492,14 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
var option = options.getOptionFromViewValue(value);
if (option && !option.disabled) {
- // Don't update the option when it is already selected.
- // For example, the browser will select the first option by default. In that case,
- // most properties are set automatically - except the `selected` attribute, which we
- // set always
-
if (selectElement[0].value !== option.selectValue) {
removeUnknownOption();
removeEmptyOption();
selectElement[0].value = option.selectValue;
option.element.selected = true;
+ option.element.setAttribute('selected', 'selected');
}
-
- option.element.setAttribute('selected', 'selected');
} else {
if (value === null || providedEmptyOption) {
removeUnknownOption();
@@ -41231,7 +39559,7 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
forEach(selectedValues, function(value) {
var option = options.selectValueMap[value];
- if (option && !option.disabled) selections.push(options.getViewValueFromOption(option));
+ if (!option.disabled) selections.push(options.getViewValueFromOption(option));
});
return selections;
@@ -41284,16 +39612,11 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
function updateOptionElement(option, element) {
option.element = element;
element.disabled = option.disabled;
- // NOTE: The label must be set before the value, otherwise IE10/11/EDGE create unresponsive
- // selects in certain circumstances when multiple selects are next to each other and display
- // the option list in listbox style, i.e. the select is [multiple], or specifies a [size].
- // See https://github.com/angular/angular.js/issues/11314 for more info.
- // This is unfortunately untestable with unit / e2e tests
+ if (option.value !== element.value) element.value = option.selectValue;
if (option.label !== element.label) {
element.label = option.label;
element.textContent = option.label;
}
- if (option.value !== element.value) element.value = option.selectValue;
}
function addOrReuseElement(parent, current, type, templateElement) {
@@ -41331,15 +39654,10 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
var emptyOption_ = emptyOption && emptyOption[0];
var unknownOption_ = unknownOption && unknownOption[0];
- // We cannot rely on the extracted empty option being the same as the compiled empty option,
- // because the compiled empty option might have been replaced by a comment because
- // it had an "element" transclusion directive on it (such as ngIf)
if (emptyOption_ || unknownOption_) {
while (current &&
(current === emptyOption_ ||
- current === unknownOption_ ||
- current.nodeType === NODE_TYPE_COMMENT ||
- (nodeName_(current) === 'option' && current.value === ''))) {
+ current === unknownOption_)) {
current = current.nextSibling;
}
}
@@ -41368,7 +39686,7 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
var groupElement;
var optionElement;
- if (isDefined(option.group)) {
+ if (option.group) {
// This option is to live in a group
// See if we have already created this group
@@ -41429,28 +39747,14 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
// Check to see if the value has changed due to the update to the options
if (!ngModelCtrl.$isEmpty(previousValue)) {
var nextValue = selectCtrl.readValue();
- var isNotPrimitive = ngOptions.trackBy || multiple;
- if (isNotPrimitive ? !equals(previousValue, nextValue) : previousValue !== nextValue) {
+ if (ngOptions.trackBy ? !equals(previousValue, nextValue) : previousValue !== nextValue) {
ngModelCtrl.$setViewValue(nextValue);
ngModelCtrl.$render();
}
}
}
- }
- return {
- restrict: 'A',
- terminal: true,
- require: ['select', 'ngModel'],
- link: {
- pre: function ngOptionsPreLink(scope, selectElement, attr, ctrls) {
- // Deactivate the SelectController.register method to prevent
- // option directives from accidentally registering themselves
- // (and unwanted $destroy handlers etc.)
- ctrls[0].registerOption = noop;
- },
- post: ngOptionsPostLink
}
};
}];
@@ -41670,7 +39974,7 @@ var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale,
}
// If both `count` and `lastCount` are NaN, we don't need to re-register a watch.
- // In JS `NaN !== NaN`, so we have to explicitly check.
+ // In JS `NaN !== NaN`, so we have to exlicitly check.
if ((count !== lastCount) && !(countIsNaN && isNumber(lastCount) && isNaN(lastCount))) {
watchRemover();
var whenExpFn = whensExpFns[count];
@@ -41715,10 +40019,8 @@ var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale,
* | `$even` | {@type boolean} | true if the iterator position `$index` is even (otherwise false). |
* | `$odd` | {@type boolean} | true if the iterator position `$index` is odd (otherwise false). |
*
- * <div class="alert alert-info">
- * Creating aliases for these properties is possible with {@link ng.directive:ngInit `ngInit`}.
- * This may be useful when, for instance, nesting ngRepeats.
- * </div>
+ * Creating aliases for these properties is possible with {@link ng.directive:ngInit `ngInit`}.
+ * This may be useful when, for instance, nesting ngRepeats.
*
*
* # Iterating over object properties
@@ -41730,44 +40032,32 @@ var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale,
* <div ng-repeat="(key, value) in myObj"> ... </div>
* ```
*
- * However, there are a limitations compared to array iteration:
+ * You need to be aware that the JavaScript specification does not define the order of keys
+ * returned for an object. (To mitigate this in Angular 1.3 the `ngRepeat` directive
+ * used to sort the keys alphabetically.)
*
- * - The JavaScript specification does not define the order of keys
- * returned for an object, so Angular relies on the order returned by the browser
- * when running `for key in myObj`. Browsers generally follow the strategy of providing
- * keys in the order in which they were defined, although there are exceptions when keys are deleted
- * and reinstated. See the
- * [MDN page on `delete` for more info](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete#Cross-browser_notes).
+ * Version 1.4 removed the alphabetic sorting. We now rely on the order returned by the browser
+ * when running `for key in myObj`. It seems that browsers generally follow the strategy of providing
+ * keys in the order in which they were defined, although there are exceptions when keys are deleted
+ * and reinstated. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete#Cross-browser_issues
*
- * - `ngRepeat` will silently *ignore* object keys starting with `$`, because
- * it's a prefix used by Angular for public (`$`) and private (`$$`) properties.
- *
- * - The built-in filters {@link ng.orderBy orderBy} and {@link ng.filter filter} do not work with
- * objects, and will throw if used with one.
- *
- * If you are hitting any of these limitations, the recommended workaround is to convert your object into an array
- * that is sorted into the order that you prefer before providing it to `ngRepeat`. You could
+ * If this is not desired, the recommended workaround is to convert your object into an array
+ * that is sorted into the order that you prefer before providing it to `ngRepeat`. You could
* do this with a filter such as [toArrayFilter](http://ngmodules.org/modules/angular-toArrayFilter)
* or implement a `$watch` on the object yourself.
*
*
* # Tracking and Duplicates
*
- * `ngRepeat` uses {@link $rootScope.Scope#$watchCollection $watchCollection} to detect changes in
- * the collection. When a change happens, ngRepeat then makes the corresponding changes to the DOM:
+ * When the contents of the collection change, `ngRepeat` makes the corresponding changes to the DOM:
*
* * When an item is added, a new instance of the template is added to the DOM.
* * When an item is removed, its template instance is removed from the DOM.
* * When items are reordered, their respective templates are reordered in the DOM.
*
- * To minimize creation of DOM elements, `ngRepeat` uses a function
- * to "keep track" of all items in the collection and their corresponding DOM elements.
- * For example, if an item is added to the collection, ngRepeat will know that all other items
- * already have DOM elements, and will not re-render them.
- *
- * The default tracking function (which tracks items by their identity) does not allow
- * duplicate items in arrays. This is because when there are duplicates, it is not possible
- * to maintain a one-to-one mapping between collection items and DOM elements.
+ * By default, `ngRepeat` does not allow duplicate items in arrays. This is because when
+ * there are duplicates, it is not possible to maintain a one-to-one mapping between collection
+ * items and DOM elements.
*
* If you do need to repeat duplicate items, you can substitute the default tracking behavior
* with your own using the `track by` expression.
@@ -41780,7 +40070,7 @@ var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale,
* </div>
* ```
*
- * You may also use arbitrary expressions in `track by`, including references to custom functions
+ * You may use arbitrary expressions in `track by`, including references to custom functions
* on the scope:
* ```html
* <div ng-repeat="n in [42, 42, 43, 43] track by myTrackingFunction(n)">
@@ -41788,14 +40078,10 @@ var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale,
* </div>
* ```
*
- * <div class="alert alert-success">
- * If you are working with objects that have an identifier property, you should track
+ * If you are working with objects that have an identifier property, you can track
* by the identifier instead of the whole object. Should you reload your data later, `ngRepeat`
* will not have to rebuild the DOM elements for items it has already rendered, even if the
- * JavaScript objects in the collection have been substituted for new ones. For large collections,
- * this significantly improves rendering performance. If you don't have a unique identifier,
- * `track by $index` can also provide a performance boost.
- * </div>
+ * JavaScript objects in the collection have been substituted for new ones:
* ```html
* <div ng-repeat="model in collection track by model.id">
* {{model.name}}
@@ -41864,13 +40150,11 @@ var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale,
* as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**).
*
* @animations
- * | Animation | Occurs |
- * |----------------------------------|-------------------------------------|
- * | {@link ng.$animate#enter enter} | when a new item is added to the list or when an item is revealed after a filter |
- * | {@link ng.$animate#leave leave} | when an item is removed from the list or when an item is filtered out |
- * | {@link ng.$animate#move move } | when an adjacent item is filtered out causing a reorder or when the item contents are reordered |
+ * **.enter** - when a new item is added to the list or when an item is revealed after a filter
*
- * See the example below for defining CSS animations with ngRepeat.
+ * **.leave** - when an item is removed from the list or when an item is filtered out
+ *
+ * **.move** - when an adjacent item is filtered out causing a reorder or when the item contents are reordered
*
* @element ANY
* @scope
@@ -41924,11 +40208,22 @@ var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale,
* For example: `item in items | filter : x | orderBy : order | limitTo : limit as results` .
*
* @example
- * This example uses `ngRepeat` to display a list of people. A filter is used to restrict the displayed
- * results by name. New (entering) and removed (leaving) items are animated.
- <example module="ngRepeat" name="ngRepeat" deps="angular-animate.js" animations="true">
+ * This example initializes the scope to a list of names and
+ * then uses `ngRepeat` to display every person:
+ <example module="ngAnimate" deps="angular-animate.js" animations="true">
<file name="index.html">
- <div ng-controller="repeatController">
+ <div ng-init="friends = [
+ {name:'John', age:25, gender:'boy'},
+ {name:'Jessie', age:30, gender:'girl'},
+ {name:'Johanna', age:28, gender:'girl'},
+ {name:'Joy', age:15, gender:'girl'},
+ {name:'Mary', age:28, gender:'girl'},
+ {name:'Peter', age:95, gender:'boy'},
+ {name:'Sebastian', age:50, gender:'boy'},
+ {name:'Erika', age:27, gender:'girl'},
+ {name:'Patrick', age:40, gender:'boy'},
+ {name:'Samantha', age:60, gender:'girl'}
+ ]">
I have {{friends.length}} friends. They are:
<input type="search" ng-model="q" placeholder="filter friends..." aria-label="filter friends" />
<ul class="example-animate-container">
@@ -41941,22 +40236,6 @@ var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale,
</ul>
</div>
</file>
- <file name="script.js">
- angular.module('ngRepeat', ['ngAnimate']).controller('repeatController', function($scope) {
- $scope.friends = [
- {name:'John', age:25, gender:'boy'},
- {name:'Jessie', age:30, gender:'girl'},
- {name:'Johanna', age:28, gender:'girl'},
- {name:'Joy', age:15, gender:'girl'},
- {name:'Mary', age:28, gender:'girl'},
- {name:'Peter', age:95, gender:'boy'},
- {name:'Sebastian', age:50, gender:'boy'},
- {name:'Erika', age:27, gender:'girl'},
- {name:'Patrick', age:40, gender:'boy'},
- {name:'Samantha', age:60, gender:'girl'}
- ];
- });
- </file>
<file name="animations.css">
.example-animate-container {
background:white;
@@ -41967,7 +40246,7 @@ var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale,
}
.animate-repeat {
- line-height:30px;
+ line-height:40px;
list-style:none;
box-sizing:border-box;
}
@@ -41975,6 +40254,7 @@ var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale,
.animate-repeat.ng-move,
.animate-repeat.ng-enter,
.animate-repeat.ng-leave {
+ -webkit-transition:all linear 0.5s;
transition:all linear 0.5s;
}
@@ -41989,7 +40269,7 @@ var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale,
.animate-repeat.ng-move.ng-move-active,
.animate-repeat.ng-enter.ng-enter-active {
opacity:1;
- max-height:30px;
+ max-height:40px;
}
</file>
<file name="protractor.js" type="protractor">
@@ -42016,7 +40296,7 @@ var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale,
</file>
</example>
*/
-var ngRepeatDirective = ['$parse', '$animate', '$compile', function($parse, $animate, $compile) {
+var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
var NG_REMOVED = '$$NG_REMOVED';
var ngRepeatMinErr = minErr('ngRepeat');
@@ -42051,7 +40331,7 @@ var ngRepeatDirective = ['$parse', '$animate', '$compile', function($parse, $ani
$$tlb: true,
compile: function ngRepeatCompile($element, $attr) {
var expression = $attr.ngRepeat;
- var ngRepeatEndComment = $compile.$$createComment('end ngRepeat', expression);
+ var ngRepeatEndComment = document.createComment(' end ngRepeat: ' + expression + ' ');
var match = expression.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+track\s+by\s+([\s\S]+?))?\s*$/);
@@ -42146,7 +40426,7 @@ var ngRepeatDirective = ['$parse', '$animate', '$compile', function($parse, $ani
// if object, extract keys, in enumeration order, unsorted
collectionKeys = [];
for (var itemKey in collection) {
- if (hasOwnProperty.call(collection, itemKey) && itemKey.charAt(0) !== '$') {
+ if (collection.hasOwnProperty(itemKey) && itemKey.charAt(0) !== '$') {
collectionKeys.push(itemKey);
}
}
@@ -42215,7 +40495,7 @@ var ngRepeatDirective = ['$parse', '$animate', '$compile', function($parse, $ani
if (getBlockStart(block) != nextNode) {
// existing item which got moved
- $animate.move(getBlockNodes(block.clone), null, previousNode);
+ $animate.move(getBlockNodes(block.clone), null, jqLite(previousNode));
}
previousNode = getBlockEnd(block);
updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
@@ -42227,7 +40507,8 @@ var ngRepeatDirective = ['$parse', '$animate', '$compile', function($parse, $ani
var endNode = ngRepeatEndComment.cloneNode(false);
clone[clone.length++] = endNode;
- $animate.enter(clone, null, previousNode);
+ // TODO(perf): support naked previousNode in `enter` to avoid creation of jqLite wrapper?
+ $animate.enter(clone, null, jqLite(previousNode));
previousNode = endNode;
// Note: We only need the first/last node of the cloned nodes.
// However, we need to keep the reference to the jqlite wrapper as it might be changed later
@@ -42330,14 +40611,12 @@ var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
* .my-element.ng-hide-remove.ng-hide-remove-active { ... }
* ```
*
- * Keep in mind that, as of AngularJS version 1.3, there is no need to change the display
+ * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
* property to block during animation states--ngAnimate will handle the style toggling automatically for you.
*
* @animations
- * | Animation | Occurs |
- * |----------------------------------|-------------------------------------|
- * | {@link $animate#addClass addClass} `.ng-hide` | after the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden |
- * | {@link $animate#removeClass removeClass} `.ng-hide` | after the `ngShow` expression evaluates to a truthy value and just before contents are set to visible |
+ * addClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a truthy value and the just before contents are set to visible
+ * removeClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden
*
* @element ANY
* @param {expression} ngShow If the {@link guide/expression expression} is truthy
@@ -42372,7 +40651,9 @@ var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
background: white;
}
- .animate-show.ng-hide-add, .animate-show.ng-hide-remove {
+ .animate-show.ng-hide-add.ng-hide-add-active,
+ .animate-show.ng-hide-remove.ng-hide-remove-active {
+ -webkit-transition: all linear 0.5s;
transition: all linear 0.5s;
}
@@ -42496,15 +40777,12 @@ var ngShowDirective = ['$animate', function($animate) {
* .my-element.ng-hide-remove.ng-hide-remove-active { ... }
* ```
*
- * Keep in mind that, as of AngularJS version 1.3, there is no need to change the display
+ * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
* property to block during animation states--ngAnimate will handle the style toggling automatically for you.
*
* @animations
- * | Animation | Occurs |
- * |----------------------------------|-------------------------------------|
- * | {@link $animate#addClass addClass} `.ng-hide` | after the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden |
- * | {@link $animate#removeClass removeClass} `.ng-hide` | after the `ngHide` expression evaluates to a non truthy value and just before contents are set to visible |
- *
+ * removeClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden
+ * addClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a non truthy value and just before the contents are set to visible
*
* @element ANY
* @param {expression} ngHide If the {@link guide/expression expression} is truthy then
@@ -42532,6 +40810,7 @@ var ngShowDirective = ['$animate', function($animate) {
</file>
<file name="animations.css">
.animate-hide {
+ -webkit-transition: all linear 0.5s;
transition: all linear 0.5s;
line-height: 20px;
opacity: 1;
@@ -42666,10 +40945,8 @@ var ngStyleDirective = ngDirective(function(scope, element, attr) {
* </div>
* @animations
- * | Animation | Occurs |
- * |----------------------------------|-------------------------------------|
- * | {@link ng.$animate#enter enter} | after the ngSwitch contents change and the matched child element is placed inside the container |
- * | {@link ng.$animate#leave leave} | after the ngSwitch contents change and just before the former contents are removed from the DOM |
+ * enter - happens after the ngSwitch contents change and the matched child element is placed inside the container
+ * leave - happens just after the ngSwitch contents change and just before the former contents are removed from the DOM
*
* @usage
*
@@ -42732,6 +41009,7 @@ var ngStyleDirective = ngDirective(function(scope, element, attr) {
}
.animate-switch.ng-animate {
+ -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
position:absolute;
@@ -42768,7 +41046,7 @@ var ngStyleDirective = ngDirective(function(scope, element, attr) {
</file>
</example>
*/
-var ngSwitchDirective = ['$animate', '$compile', function($animate, $compile) {
+var ngSwitchDirective = ['$animate', function($animate) {
return {
require: 'ngSwitch',
@@ -42809,7 +41087,7 @@ var ngSwitchDirective = ['$animate', '$compile', function($animate, $compile) {
selectedTransclude.transclude(function(caseElement, selectedScope) {
selectedScopes.push(selectedScope);
var anchor = selectedTransclude.element;
- caseElement[caseElement.length++] = $compile.$$createComment('end ngSwitchWhen');
+ caseElement[caseElement.length++] = document.createComment(' end ngSwitchWhen: ');
var block = { clone: caseElement };
selectedElements.push(block);
@@ -42852,186 +41130,67 @@ var ngSwitchDefaultDirective = ngDirective({
* @description
* Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion.
*
- * You can specify that you want to insert a named transclusion slot, instead of the default slot, by providing the slot name
- * as the value of the `ng-transclude` or `ng-transclude-slot` attribute.
- *
- * If the transcluded content is not empty (i.e. contains one or more DOM nodes, including whitespace text nodes), any existing
- * content of this element will be removed before the transcluded content is inserted.
- * If the transcluded content is empty, the existing content is left intact. This lets you provide fallback content in the case
- * that no transcluded content is provided.
+ * Any existing content of the element that this directive is placed on will be removed before the transcluded content is inserted.
*
* @element ANY
*
- * @param {string} ngTransclude|ngTranscludeSlot the name of the slot to insert at this point. If this is not provided, is empty
- * or its value is the same as the name of the attribute then the default slot is used.
- *
* @example
- * ### Basic transclusion
- * This example demonstrates basic transclusion of content into a component directive.
- * <example name="simpleTranscludeExample" module="transcludeExample">
- * <file name="index.html">
- * <script>
- * angular.module('transcludeExample', [])
- * .directive('pane', function(){
- * return {
- * restrict: 'E',
- * transclude: true,
- * scope: { title:'@' },
- * template: '<div style="border: 1px solid black;">' +
- * '<div style="background-color: gray">{{title}}</div>' +
- * '<ng-transclude></ng-transclude>' +
- * '</div>'
- * };
- * })
- * .controller('ExampleController', ['$scope', function($scope) {
- * $scope.title = 'Lorem Ipsum';
- * $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
- * }]);
- * </script>
- * <div ng-controller="ExampleController">
- * <input ng-model="title" aria-label="title"> <br/>
- * <textarea ng-model="text" aria-label="text"></textarea> <br/>
- * <pane title="{{title}}">{{text}}</pane>
- * </div>
- * </file>
- * <file name="protractor.js" type="protractor">
- * it('should have transcluded', 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.binding('title')).getText()).toEqual('TITLE');
- * expect(element(by.binding('text')).getText()).toEqual('TEXT');
- * });
- * </file>
- * </example>
- *
- * @example
- * ### Transclude fallback content
- * This example shows how to use `NgTransclude` with fallback content, that
- * is displayed if no transcluded content is provided.
- *
- * <example module="transcludeFallbackContentExample">
- * <file name="index.html">
- * <script>
- * angular.module('transcludeFallbackContentExample', [])
- * .directive('myButton', function(){
- * return {
- * restrict: 'E',
- * transclude: true,
- * scope: true,
- * template: '<button style="cursor: pointer;">' +
- * '<ng-transclude>' +
- * '<b style="color: red;">Button1</b>' +
- * '</ng-transclude>' +
- * '</button>'
- * };
- * });
- * </script>
- * <!-- fallback button content -->
- * <my-button id="fallback"></my-button>
- * <!-- modified button content -->
- * <my-button id="modified">
- * <i style="color: green;">Button2</i>
- * </my-button>
- * </file>
- * <file name="protractor.js" type="protractor">
- * it('should have different transclude element content', function() {
- * expect(element(by.id('fallback')).getText()).toBe('Button1');
- * expect(element(by.id('modified')).getText()).toBe('Button2');
- * });
- * </file>
- * </example>
+ <example module="transcludeExample">
+ <file name="index.html">
+ <script>
+ angular.module('transcludeExample', [])
+ .directive('pane', function(){
+ return {
+ restrict: 'E',
+ transclude: true,
+ scope: { title:'@' },
+ template: '<div style="border: 1px solid black;">' +
+ '<div style="background-color: gray">{{title}}</div>' +
+ '<ng-transclude></ng-transclude>' +
+ '</div>'
+ };
+ })
+ .controller('ExampleController', ['$scope', function($scope) {
+ $scope.title = 'Lorem Ipsum';
+ $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
+ }]);
+ </script>
+ <div ng-controller="ExampleController">
+ <input ng-model="title" aria-label="title"> <br/>
+ <textarea ng-model="text" aria-label="text"></textarea> <br/>
+ <pane title="{{title}}">{{text}}</pane>
+ </div>
+ </file>
+ <file name="protractor.js" type="protractor">
+ it('should have transcluded', 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.binding('title')).getText()).toEqual('TITLE');
+ expect(element(by.binding('text')).getText()).toEqual('TEXT');
+ });
+ </file>
+ </example>
*
- * @example
- * ### Multi-slot transclusion
- * This example demonstrates using multi-slot transclusion in a component directive.
- * <example name="multiSlotTranscludeExample" module="multiSlotTranscludeExample">
- * <file name="index.html">
- * <style>
- * .title, .footer {
- * background-color: gray
- * }
- * </style>
- * <div ng-controller="ExampleController">
- * <input ng-model="title" aria-label="title"> <br/>
- * <textarea ng-model="text" aria-label="text"></textarea> <br/>
- * <pane>
- * <pane-title><a ng-href="{{link}}">{{title}}</a></pane-title>
- * <pane-body><p>{{text}}</p></pane-body>
- * </pane>
- * </div>
- * </file>
- * <file name="app.js">
- * angular.module('multiSlotTranscludeExample', [])
- * .directive('pane', function(){
- * return {
- * restrict: 'E',
- * transclude: {
- * 'title': '?paneTitle',
- * 'body': 'paneBody',
- * 'footer': '?paneFooter'
- * },
- * template: '<div style="border: 1px solid black;">' +
- * '<div class="title" ng-transclude="title">Fallback Title</div>' +
- * '<div ng-transclude="body"></div>' +
- * '<div class="footer" ng-transclude="footer">Fallback Footer</div>' +
- * '</div>'
- * };
- * })
- * .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...';
- * }]);
- * </file>
- * <file name="protractor.js" type="protractor">
- * 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');
- * });
- * </file>
- * </example>
*/
-var ngTranscludeMinErr = minErr('ngTransclude');
var ngTranscludeDirective = ngDirective({
restrict: 'EAC',
link: function($scope, $element, $attrs, controller, $transclude) {
-
- if ($attrs.ngTransclude === $attrs.$attr.ngTransclude) {
- // If the attribute is of the form: `ng-transclude="ng-transclude"`
- // then treat it like the default
- $attrs.ngTransclude = '';
- }
-
- function ngTranscludeCloneAttachFn(clone) {
- if (clone.length) {
- $element.empty();
- $element.append(clone);
- }
- }
-
if (!$transclude) {
- throw ngTranscludeMinErr('orphan',
+ throw minErr('ngTransclude')('orphan',
'Illegal use of ngTransclude directive in the template! ' +
'No parent directive that requires a transclusion found. ' +
'Element: {0}',
startingTag($element));
}
- // If there is no slot name defined or the slot name is not optional
- // then transclude the slot
- var slotName = $attrs.ngTransclude || $attrs.ngTranscludeSlot;
- $transclude(ngTranscludeCloneAttachFn, null, slotName);
+ $transclude(function(clone) {
+ $element.empty();
+ $element.append(clone);
+ });
}
});
@@ -43085,15 +41244,6 @@ var scriptDirective = ['$templateCache', function($templateCache) {
var noopNgModelController = { $setViewValue: noop, $render: noop };
-function chromeHack(optionElement) {
- // Workaround for https://code.google.com/p/chromium/issues/detail?id=381459
- // Adding an <option selected="selected"> element to a <select required="required"> should
- // automatically select the new element
- if (optionElement[0].hasAttribute('selected')) {
- optionElement[0].selected = true;
- }
-}
-
/**
* @ngdoc type
* @name select.SelectController
@@ -43103,7 +41253,7 @@ function chromeHack(optionElement) {
* added `<option>` elements, perhaps by an `ngRepeat` directive.
*/
var SelectController =
- ['$element', '$scope', function($element, $scope) {
+ ['$element', '$scope', '$attrs', function($element, $scope, $attrs) {
var self = this,
optionsMap = new HashMap();
@@ -43163,17 +41313,12 @@ var SelectController =
// Tell the select control that an option, with the given value, has been added
self.addOption = function(value, element) {
- // Skip comment nodes, as they only pollute the `optionsMap`
- if (element[0].nodeType === NODE_TYPE_COMMENT) return;
-
assertNotHasOwnProperty(value, '"option value"');
if (value === '') {
self.emptyOption = element;
}
var count = optionsMap.get(value) || 0;
optionsMap.put(value, count + 1);
- self.ngModelCtrl.$render();
- chromeHack(element);
};
// Tell the select control that an option, with the given value, has been removed
@@ -43195,39 +41340,6 @@ var SelectController =
self.hasOption = function(value) {
return !!optionsMap.get(value);
};
-
-
- self.registerOption = function(optionScope, optionElement, optionAttrs, interpolateValueFn, interpolateTextFn) {
-
- if (interpolateValueFn) {
- // The value attribute is interpolated
- var oldVal;
- optionAttrs.$observe('value', function valueAttributeObserveAction(newVal) {
- if (isDefined(oldVal)) {
- self.removeOption(oldVal);
- }
- oldVal = newVal;
- self.addOption(newVal, optionElement);
- });
- } else if (interpolateTextFn) {
- // The text content is interpolated
- optionScope.$watch(interpolateTextFn, function interpolateWatchAction(newVal, oldVal) {
- optionAttrs.$set('value', newVal);
- if (oldVal !== newVal) {
- self.removeOption(oldVal);
- }
- self.addOption(newVal, optionElement);
- });
- } else {
- // The value attribute is static
- self.addOption(optionAttrs.value, optionElement);
- }
-
- optionElement.on('$destroy', function() {
- self.removeOption(optionAttrs.value);
- self.ngModelCtrl.$render();
- });
- };
}];
/**
@@ -43238,164 +41350,31 @@ var SelectController =
* @description
* HTML `SELECT` element with angular data-binding.
*
- * The `select` directive is used together with {@link ngModel `ngModel`} to provide data-binding
- * between the scope and the `<select>` control (including setting default values).
- * It also handles dynamic `<option>` elements, which can be added using the {@link ngRepeat `ngRepeat}` or
- * {@link ngOptions `ngOptions`} directives.
- *
- * When an item in the `<select>` menu is selected, the value of the selected option will be bound
- * to the model identified by the `ngModel` directive. With static or repeated options, this is
- * the content of the `value` attribute or the textContent of the `<option>`, if the value attribute is missing.
- * If you want dynamic value attributes, you can use interpolation inside the value attribute.
+ * In many cases, `ngRepeat` can be used on `<option>` elements instead of {@link ng.directive:ngOptions
+ * ngOptions} to achieve a similar result. However, `ngOptions` provides some benefits such as reducing
+ * memory and increasing speed by not creating a new scope for each repeated instance, as well as providing
+ * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
+ * comprehension expression.
*
- * <div class="alert alert-warning">
- * Note that the value of a `select` directive used without `ngOptions` is always a string.
- * When the model needs to be bound to a non-string value, you must either explicitly convert it
- * using a directive (see example below) or use `ngOptions` to specify the set of options.
- * This is because an option element can only be bound to string values at present.
- * </div>
+ * When an item in the `<select>` menu is selected, the array element or object property
+ * represented by the selected option will be bound to the model identified by the `ngModel`
+ * directive.
*
- * If the viewValue of `ngModel` does not match any of the options, then the control
- * will automatically add an "unknown" option, which it then removes when the mismatch is resolved.
+ * If the viewValue contains a value that doesn't match any of the options then the control
+ * will automatically add an "unknown" option, which it then removes when this is resolved.
*
* Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
* be nested into the `<select>` element. This element will then represent the `null` or "not selected"
* option. See example below for demonstration.
*
* <div class="alert alert-info">
- * In many cases, `ngRepeat` can be used on `<option>` elements instead of {@link ng.directive:ngOptions
- * ngOptions} to achieve a similar result. However, `ngOptions` provides some benefits, such as
- * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
- * comprehension expression, and additionally in reducing memory and increasing speed by not creating
- * a new scope for each repeated instance.
- * </div>
- *
- *
- * @param {string} ngModel Assignable angular expression to data-bind to.
- * @param {string=} name Property name of the form under which the control is published.
- * @param {string=} multiple Allows multiple options to be selected. The selected values will be
- * bound to the model as an array.
- * @param {string=} required Sets `required` validation error key if the value is not entered.
- * @param {string=} ngRequired Adds required attribute and required validation constraint to
- * the element when the ngRequired expression evaluates to true. Use ngRequired instead of required
- * when you want to data-bind to the required attribute.
- * @param {string=} ngChange Angular expression to be executed when selected option(s) changes due to user
- * interaction with the select element.
- * @param {string=} ngOptions sets the options that the select is populated with and defines what is
- * set on the model on selection. See {@link ngOptions `ngOptions`}.
- *
- * @example
- * ### Simple `select` elements with static options
- *
- * <example name="static-select" module="staticSelect">
- * <file name="index.html">
- * <div ng-controller="ExampleController">
- * <form name="myForm">
- * <label for="singleSelect"> Single select: </label><br>
- * <select name="singleSelect" ng-model="data.singleSelect">
- * <option value="option-1">Option 1</option>
- * <option value="option-2">Option 2</option>
- * </select><br>
- *
- * <label for="singleSelect"> Single select with "not selected" option and dynamic option values: </label><br>
- * <select name="singleSelect" id="singleSelect" ng-model="data.singleSelect">
- * <option value="">---Please select---</option> <!-- not selected / blank option -->
- * <option value="{{data.option1}}">Option 1</option> <!-- interpolation -->
- * <option value="option-2">Option 2</option>
- * </select><br>
- * <button ng-click="forceUnknownOption()">Force unknown option</button><br>
- * <tt>singleSelect = {{data.singleSelect}}</tt>
- *
- * <hr>
- * <label for="multipleSelect"> Multiple select: </label><br>
- * <select name="multipleSelect" id="multipleSelect" ng-model="data.multipleSelect" multiple>
- * <option value="option-1">Option 1</option>
- * <option value="option-2">Option 2</option>
- * <option value="option-3">Option 3</option>
- * </select><br>
- * <tt>multipleSelect = {{data.multipleSelect}}</tt><br/>
- * </form>
- * </div>
- * </file>
- * <file name="app.js">
- * angular.module('staticSelect', [])
- * .controller('ExampleController', ['$scope', function($scope) {
- * $scope.data = {
- * singleSelect: null,
- * multipleSelect: [],
- * option1: 'option-1',
- * };
- *
- * $scope.forceUnknownOption = function() {
- * $scope.data.singleSelect = 'nonsense';
- * };
- * }]);
- * </file>
- *</example>
- *
- * ### Using `ngRepeat` to generate `select` options
- * <example name="ngrepeat-select" module="ngrepeatSelect">
- * <file name="index.html">
- * <div ng-controller="ExampleController">
- * <form name="myForm">
- * <label for="repeatSelect"> Repeat select: </label>
- * <select name="repeatSelect" id="repeatSelect" ng-model="data.repeatSelect">
- * <option ng-repeat="option in data.availableOptions" value="{{option.id}}">{{option.name}}</option>
- * </select>
- * </form>
- * <hr>
- * <tt>repeatSelect = {{data.repeatSelect}}</tt><br/>
+ * The value of a `select` directive used without `ngOptions` is always a string.
+ * When the model needs to be bound to a non-string value, you must either explictly convert it
+ * using a directive (see example below) or use `ngOptions` to specify the set of options.
+ * This is because an option element can only be bound to string values at present.
* </div>
- * </file>
- * <file name="app.js">
- * angular.module('ngrepeatSelect', [])
- * .controller('ExampleController', ['$scope', function($scope) {
- * $scope.data = {
- * repeatSelect: null,
- * availableOptions: [
- * {id: '1', name: 'Option A'},
- * {id: '2', name: 'Option B'},
- * {id: '3', name: 'Option C'}
- * ],
- * };
- * }]);
- * </file>
- *</example>
- *
- *
- * ### Using `select` with `ngOptions` and setting a default value
- * See the {@link ngOptions ngOptions documentation} for more `ngOptions` usage examples.
*
- * <example name="select-with-default-values" module="defaultValueSelect">
- * <file name="index.html">
- * <div ng-controller="ExampleController">
- * <form name="myForm">
- * <label for="mySelect">Make a choice:</label>
- * <select name="mySelect" id="mySelect"
- * ng-options="option.name for option in data.availableOptions track by option.id"
- * ng-model="data.selectedOption"></select>
- * </form>
- * <hr>
- * <tt>option = {{data.selectedOption}}</tt><br/>
- * </div>
- * </file>
- * <file name="app.js">
- * angular.module('defaultValueSelect', [])
- * .controller('ExampleController', ['$scope', function($scope) {
- * $scope.data = {
- * availableOptions: [
- * {id: '1', name: 'Option A'},
- * {id: '2', name: 'Option B'},
- * {id: '3', name: 'Option C'}
- * ],
- * selectedOption: {id: '3', name: 'Option C'} //This sets the default value of the select in the ui
- * };
- * }]);
- * </file>
- *</example>
- *
- *
- * ### Binding `select` to a non-string value via `ngModel` parsing / formatting
+ * ### Example (binding `select` to a non-string value)
*
* <example name="select-with-non-string-options" module="nonStringSelect">
* <file name="index.html">
@@ -43440,14 +41419,7 @@ var selectDirective = function() {
restrict: 'E',
require: ['select', '?ngModel'],
controller: SelectController,
- priority: 1,
- link: {
- pre: selectPreLink,
- post: selectPostLink
- }
- };
-
- function selectPreLink(scope, element, attr, ctrls) {
+ link: function(scope, element, attr, ctrls) {
// if ngModel is not defined, we don't need to do anything
var ngModelCtrl = ctrls[1];
@@ -43457,6 +41429,13 @@ var selectDirective = function() {
selectCtrl.ngModelCtrl = ngModelCtrl;
+ // We delegate rendering to the `writeValue` method, which can be changed
+ // if the select can have multiple selected values or if the options are being
+ // generated by `ngOptions`
+ ngModelCtrl.$render = function() {
+ selectCtrl.writeValue(ngModelCtrl.$viewValue);
+ };
+
// When the selected item(s) changes we delegate getting the value of the select control
// to the `readValue` method, which can be changed if the select can have multiple
// selected values or if the options are being generated by `ngOptions`
@@ -43510,23 +41489,7 @@ var selectDirective = function() {
}
}
-
- function selectPostLink(scope, element, attrs, ctrls) {
- // if ngModel is not defined, we don't need to do anything
- var ngModelCtrl = ctrls[1];
- if (!ngModelCtrl) return;
-
- var selectCtrl = ctrls[0];
-
- // We delegate rendering to the `writeValue` method, which can be changed
- // if the select can have multiple selected values or if the options are being
- // generated by `ngOptions`.
- // This must be done in the postLink fn to prevent $render to be called before
- // all nodes have been linked correctly.
- ngModelCtrl.$render = function() {
- selectCtrl.writeValue(ngModelCtrl.$viewValue);
- };
- }
+ };
};
@@ -43534,23 +41497,32 @@ var selectDirective = function() {
// of dynamically created (and destroyed) option elements to their containing select
// directive via its controller.
var optionDirective = ['$interpolate', function($interpolate) {
+
+ function chromeHack(optionElement) {
+ // Workaround for https://code.google.com/p/chromium/issues/detail?id=381459
+ // Adding an <option selected="selected"> element to a <select required="required"> should
+ // automatically select the new element
+ if (optionElement[0].hasAttribute('selected')) {
+ optionElement[0].selected = true;
+ }
+ }
+
return {
restrict: 'E',
priority: 100,
compile: function(element, attr) {
- if (isDefined(attr.value)) {
- // If the value attribute is defined, check if it contains an interpolation
- var interpolateValueFn = $interpolate(attr.value, true);
- } else {
- // If the value attribute is not defined then we fall back to the
- // text content of the option element, which may be interpolated
- var interpolateTextFn = $interpolate(element.text(), true);
- if (!interpolateTextFn) {
+
+ // If the value attribute is not defined then we fall back to the
+ // text content of the option element, which may be interpolated
+ if (isUndefined(attr.value)) {
+ var interpolateFn = $interpolate(element.text(), true);
+ if (!interpolateFn) {
attr.$set('value', element.text());
}
}
return function(scope, element, attr) {
+
// This is an optimization over using ^^ since we don't want to have to search
// all the way to the root of the DOM for every single option element
var selectCtrlName = '$selectController',
@@ -43558,8 +41530,30 @@ var optionDirective = ['$interpolate', function($interpolate) {
selectCtrl = parent.data(selectCtrlName) ||
parent.parent().data(selectCtrlName); // in case we are in optgroup
- if (selectCtrl) {
- selectCtrl.registerOption(scope, element, attr, interpolateValueFn, interpolateTextFn);
+ // Only update trigger option updates if this is an option within a `select`
+ // that also has `ngModel` attached
+ if (selectCtrl && selectCtrl.ngModelCtrl) {
+
+ if (interpolateFn) {
+ scope.$watch(interpolateFn, function interpolateWatchAction(newVal, oldVal) {
+ attr.$set('value', newVal);
+ if (oldVal !== newVal) {
+ selectCtrl.removeOption(oldVal);
+ }
+ selectCtrl.addOption(newVal, element);
+ selectCtrl.ngModelCtrl.$render();
+ chromeHack(element);
+ });
+ } else {
+ selectCtrl.addOption(attr.value, element);
+ selectCtrl.ngModelCtrl.$render();
+ chromeHack(element);
+ }
+
+ element.on('$destroy', function() {
+ selectCtrl.removeOption(attr.value);
+ selectCtrl.ngModelCtrl.$render();
+ });
}
};
}
@@ -43571,64 +41565,6 @@ var styleDirective = valueFn({
terminal: false
});
-/**
- * @ngdoc directive
- * @name ngRequired
- *
- * @description
- *
- * ngRequired adds the required {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
- * It is most often used for {@link input `input`} and {@link select `select`} controls, but can also be
- * applied to custom controls.
- *
- * The directive sets the `required` attribute on the element if the Angular expression inside
- * `ngRequired` evaluates to true. A special directive for setting `required` is necessary because we
- * cannot use interpolation inside `required`. See the {@link guide/interpolation interpolation guide}
- * for more info.
- *
- * The validator will set the `required` error key to true if the `required` attribute is set and
- * calling {@link ngModel.NgModelController#$isEmpty `NgModelController.$isEmpty`} with the
- * {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`} returns `true`. For example, the
- * `$isEmpty()` implementation for `input[text]` checks the length of the `$viewValue`. When developing
- * custom controls, `$isEmpty()` can be overwritten to account for a $viewValue that is not string-based.
- *
- * @example
- * <example name="ngRequiredDirective" module="ngRequiredExample">
- * <file name="index.html">
- * <script>
- * angular.module('ngRequiredExample', [])
- * .controller('ExampleController', ['$scope', function($scope) {
- * $scope.required = true;
- * }]);
- * </script>
- * <div ng-controller="ExampleController">
- * <form name="form">
- * <label for="required">Toggle required: </label>
- * <input type="checkbox" ng-model="required" id="required" />
- * <br>
- * <label for="input">This input must be filled if `required` is true: </label>
- * <input type="text" ng-model="model" id="input" name="input" ng-required="required" /><br>
- * <hr>
- * required error set? = <code>{{form.input.$error.required}}</code><br>
- * model = <code>{{model}}</code>
- * </form>
- * </div>
- * </file>
- * <file name="protractor.js" type="protractor">
- var required = element(by.binding('form.input.$error.required'));
- var model = element(by.binding('model'));
- var input = element(by.id('input'));
-
- it('should set the required error', function() {
- expect(required.getText()).toContain('true');
-
- input.sendKeys('123');
- expect(required.getText()).not.toContain('true');
- expect(model.getText()).toContain('123');
- });
- * </file>
- * </example>
- */
var requiredDirective = function() {
return {
restrict: 'A',
@@ -43648,81 +41584,7 @@ var requiredDirective = function() {
};
};
-/**
- * @ngdoc directive
- * @name ngPattern
- *
- * @description
- *
- * ngPattern adds the pattern {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
- * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
- *
- * The validator sets the `pattern` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
- * does not match a RegExp which is obtained by evaluating the Angular expression given in the
- * `ngPattern` attribute value:
- * * If the expression evaluates to a RegExp object, then this is used directly.
- * * If the expression evaluates to a string, then it will be converted to a RegExp after wrapping it
- * in `^` and `$` characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`.
- *
- * <div class="alert alert-info">
- * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
- * start at the index of the last search's match, thus not taking the whole input value into
- * account.
- * </div>
- *
- * <div class="alert alert-info">
- * **Note:** This directive is also added when the plain `pattern` attribute is used, with two
- * differences:
- * <ol>
- * <li>
- * `ngPattern` does not set the `pattern` attribute and therefore HTML5 constraint validation is
- * not available.
- * </li>
- * <li>
- * The `ngPattern` attribute must be an expression, while the `pattern` value must be
- * interpolated.
- * </li>
- * </ol>
- * </div>
- *
- * @example
- * <example name="ngPatternDirective" module="ngPatternExample">
- * <file name="index.html">
- * <script>
- * angular.module('ngPatternExample', [])
- * .controller('ExampleController', ['$scope', function($scope) {
- * $scope.regex = '\\d+';
- * }]);
- * </script>
- * <div ng-controller="ExampleController">
- * <form name="form">
- * <label for="regex">Set a pattern (regex string): </label>
- * <input type="text" ng-model="regex" id="regex" />
- * <br>
- * <label for="input">This input is restricted by the current pattern: </label>
- * <input type="text" ng-model="model" id="input" name="input" ng-pattern="regex" /><br>
- * <hr>
- * input valid? = <code>{{form.input.$valid}}</code><br>
- * model = <code>{{model}}</code>
- * </form>
- * </div>
- * </file>
- * <file name="protractor.js" type="protractor">
- var model = element(by.binding('model'));
- var input = element(by.id('input'));
-
- it('should validate the input with the default pattern', function() {
- input.sendKeys('aaa');
- expect(model.getText()).not.toContain('aaa');
- input.clear().then(function() {
- input.sendKeys('123');
- expect(model.getText()).toContain('123');
- });
- });
- * </file>
- * </example>
- */
var patternDirective = function() {
return {
restrict: 'A',
@@ -43746,80 +41608,14 @@ var patternDirective = function() {
ctrl.$validate();
});
- ctrl.$validators.pattern = function(modelValue, viewValue) {
- // HTML5 pattern constraint validates the input value, so we validate the viewValue
- return ctrl.$isEmpty(viewValue) || isUndefined(regexp) || regexp.test(viewValue);
+ ctrl.$validators.pattern = function(value) {
+ return ctrl.$isEmpty(value) || isUndefined(regexp) || regexp.test(value);
};
}
};
};
-/**
- * @ngdoc directive
- * @name ngMaxlength
- *
- * @description
- *
- * ngMaxlength adds the maxlength {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
- * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
- *
- * The validator sets the `maxlength` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
- * is longer than the integer obtained by evaluating the Angular expression given in the
- * `ngMaxlength` attribute value.
- *
- * <div class="alert alert-info">
- * **Note:** This directive is also added when the plain `maxlength` attribute is used, with two
- * differences:
- * <ol>
- * <li>
- * `ngMaxlength` does not set the `maxlength` attribute and therefore HTML5 constraint
- * validation is not available.
- * </li>
- * <li>
- * The `ngMaxlength` attribute must be an expression, while the `maxlength` value must be
- * interpolated.
- * </li>
- * </ol>
- * </div>
- *
- * @example
- * <example name="ngMaxlengthDirective" module="ngMaxlengthExample">
- * <file name="index.html">
- * <script>
- * angular.module('ngMaxlengthExample', [])
- * .controller('ExampleController', ['$scope', function($scope) {
- * $scope.maxlength = 5;
- * }]);
- * </script>
- * <div ng-controller="ExampleController">
- * <form name="form">
- * <label for="maxlength">Set a maxlength: </label>
- * <input type="number" ng-model="maxlength" id="maxlength" />
- * <br>
- * <label for="input">This input is restricted by the current maxlength: </label>
- * <input type="text" ng-model="model" id="input" name="input" ng-maxlength="maxlength" /><br>
- * <hr>
- * input valid? = <code>{{form.input.$valid}}</code><br>
- * model = <code>{{model}}</code>
- * </form>
- * </div>
- * </file>
- * <file name="protractor.js" type="protractor">
- var model = element(by.binding('model'));
- var input = element(by.id('input'));
-
- it('should validate the input with the default maxlength', function() {
- input.sendKeys('abcdef');
- expect(model.getText()).not.toContain('abcdef');
- input.clear().then(function() {
- input.sendKeys('abcde');
- expect(model.getText()).toContain('abcde');
- });
- });
- * </file>
- * </example>
- */
var maxlengthDirective = function() {
return {
restrict: 'A',
@@ -43840,70 +41636,6 @@ var maxlengthDirective = function() {
};
};
-/**
- * @ngdoc directive
- * @name ngMinlength
- *
- * @description
- *
- * ngMinlength adds the minlength {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
- * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
- *
- * The validator sets the `minlength` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
- * is shorter than the integer obtained by evaluating the Angular expression given in the
- * `ngMinlength` attribute value.
- *
- * <div class="alert alert-info">
- * **Note:** This directive is also added when the plain `minlength` attribute is used, with two
- * differences:
- * <ol>
- * <li>
- * `ngMinlength` does not set the `minlength` attribute and therefore HTML5 constraint
- * validation is not available.
- * </li>
- * <li>
- * The `ngMinlength` value must be an expression, while the `minlength` value must be
- * interpolated.
- * </li>
- * </ol>
- * </div>
- *
- * @example
- * <example name="ngMinlengthDirective" module="ngMinlengthExample">
- * <file name="index.html">
- * <script>
- * angular.module('ngMinlengthExample', [])
- * .controller('ExampleController', ['$scope', function($scope) {
- * $scope.minlength = 3;
- * }]);
- * </script>
- * <div ng-controller="ExampleController">
- * <form name="form">
- * <label for="minlength">Set a minlength: </label>
- * <input type="number" ng-model="minlength" id="minlength" />
- * <br>
- * <label for="input">This input is restricted by the current minlength: </label>
- * <input type="text" ng-model="model" id="input" name="input" ng-minlength="minlength" /><br>
- * <hr>
- * input valid? = <code>{{form.input.$valid}}</code><br>
- * model = <code>{{model}}</code>
- * </form>
- * </div>
- * </file>
- * <file name="protractor.js" type="protractor">
- var model = element(by.binding('model'));
- var input = element(by.id('input'));
-
- it('should validate the input with the default minlength', function() {
- input.sendKeys('ab');
- expect(model.getText()).not.toContain('ab');
-
- input.sendKeys('abc');
- expect(model.getText()).toContain('abc');
- });
- * </file>
- * </example>
- */
var minlengthDirective = function() {
return {
restrict: 'A',
@@ -43923,162 +41655,17 @@ var minlengthDirective = function() {
};
};
-if (window.angular.bootstrap) {
- //AngularJS is already loaded, so we can return here...
- if (window.console) {
+ if (window.angular.bootstrap) {
+ //AngularJS is already loaded, so we can return here...
console.log('WARNING: Tried to load angular more than once.');
+ return;
}
- return;
-}
-
-//try to bind to jquery now so that one can write jqLite(document).ready()
-//but we will rebind on bootstrap again.
-bindJQuery();
-
-publishExternalAPI(angular);
-
-angular.module("ngLocale", [], ["$provide", function($provide) {
-var PLURAL_CATEGORY = {ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"};
-function getDecimals(n) {
- n = n + '';
- var i = n.indexOf('.');
- return (i == -1) ? 0 : n.length - i - 1;
-}
-function getVF(n, opt_precision) {
- var v = opt_precision;
+ //try to bind to jquery now so that one can write jqLite(document).ready()
+ //but we will rebind on bootstrap again.
+ bindJQuery();
- if (undefined === v) {
- v = Math.min(getDecimals(n), 3);
- }
-
- var base = Math.pow(10, v);
- var f = ((n * base) | 0) % base;
- return {v: v, f: f};
-}
-
-$provide.value("$locale", {
- "DATETIME_FORMATS": {
- "AMPMS": [
- "AM",
- "PM"
- ],
- "DAY": [
- "Sunday",
- "Monday",
- "Tuesday",
- "Wednesday",
- "Thursday",
- "Friday",
- "Saturday"
- ],
- "ERANAMES": [
- "Before Christ",
- "Anno Domini"
- ],
- "ERAS": [
- "BC",
- "AD"
- ],
- "FIRSTDAYOFWEEK": 6,
- "MONTH": [
- "January",
- "February",
- "March",
- "April",
- "May",
- "June",
- "July",
- "August",
- "September",
- "October",
- "November",
- "December"
- ],
- "SHORTDAY": [
- "Sun",
- "Mon",
- "Tue",
- "Wed",
- "Thu",
- "Fri",
- "Sat"
- ],
- "SHORTMONTH": [
- "Jan",
- "Feb",
- "Mar",
- "Apr",
- "May",
- "Jun",
- "Jul",
- "Aug",
- "Sep",
- "Oct",
- "Nov",
- "Dec"
- ],
- "STANDALONEMONTH": [
- "January",
- "February",
- "March",
- "April",
- "May",
- "June",
- "July",
- "August",
- "September",
- "October",
- "November",
- "December"
- ],
- "WEEKENDRANGE": [
- 5,
- 6
- ],
- "fullDate": "EEEE, MMMM d, y",
- "longDate": "MMMM d, y",
- "medium": "MMM d, y h:mm:ss a",
- "mediumDate": "MMM d, y",
- "mediumTime": "h:mm:ss a",
- "short": "M/d/yy h:mm a",
- "shortDate": "M/d/yy",
- "shortTime": "h:mm a"
- },
- "NUMBER_FORMATS": {
- "CURRENCY_SYM": "$",
- "DECIMAL_SEP": ".",
- "GROUP_SEP": ",",
- "PATTERNS": [
- {
- "gSize": 3,
- "lgSize": 3,
- "maxFrac": 3,
- "minFrac": 0,
- "minInt": 1,
- "negPre": "-",
- "negSuf": "",
- "posPre": "",
- "posSuf": ""
- },
- {
- "gSize": 3,
- "lgSize": 3,
- "maxFrac": 2,
- "minFrac": 2,
- "minInt": 1,
- "negPre": "-\u00a4",
- "negSuf": "",
- "posPre": "\u00a4",
- "posSuf": ""
- }
- ]
- },
- "id": "en-us",
- "localeID": "en_US",
- "pluralCat": function(n, opt_precision) { var i = n | 0; var vf = getVF(n, opt_precision); if (i == 1 && vf.v == 0) { return PLURAL_CATEGORY.ONE; } return PLURAL_CATEGORY.OTHER;}
-});
-}]);
+ publishExternalAPI(angular);
jqLite(document).ready(function() {
angularInit(document, bootstrap);
@@ -44086,7 +41673,7 @@ $provide.value("$locale", {
})(window, document);
-!window.angular.$$csp().noInlineStyle && window.angular.element(document.head).prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide:not(.ng-hide-animate){display:none !important;}ng\\:form{display:block;}.ng-animate-shim{visibility:hidden;}.ng-anchor{position:absolute;}</style>');
+!window.angular.$$csp() && window.angular.element(document.head).prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide:not(.ng-hide-animate){display:none !important;}ng\\:form{display:block;}.ng-animate-shim{visibility:hidden;}.ng-anchor{position:absolute;}</style>');
/*!
* ionic.bundle.js is a concatenation of:
* ionic.js, angular.js, angular-animate.js,
@@ -44095,15 +41682,14 @@ $provide.value("$locale", {
*/
/**
- * @license AngularJS v1.5.3
- * (c) 2010-2016 Google, Inc. http://angularjs.org
+ * @license AngularJS v1.4.3
+ * (c) 2010-2015 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;
@@ -44118,63 +41704,13 @@ 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 PREPARE_CLASS_SUFFIX = '-prepare';
-
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;
-};
+}
-var ngMinErr = angular.$$minErr('ng');
function assertArg(arg, name, reason) {
if (!arg) {
throw ngMinErr('areq', "Argument '{0}' is {1}", (name || '?'), (reason || "required"));
@@ -44319,29 +41855,13 @@ function applyAnimationToStyles(element, options) {
}
}
-function mergeAnimationDetails(element, oldAnimation, newAnimation) {
- var target = oldAnimation.options || {};
- var newOptions = newAnimation.options || {};
-
+function mergeAnimationOptions(element, target, newOptions) {
var toAdd = (target.addClass || '') + ' ' + (newOptions.addClass || '');
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 {
@@ -44354,9 +41874,6 @@ function mergeAnimationDetails(element, oldAnimation, newAnimation) {
target.removeClass = null;
}
- oldAnimation.addClass = target.addClass;
- oldAnimation.removeClass = target.removeClass;
-
return target;
}
@@ -44422,77 +41939,20 @@ 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 queue, cancelFn;
+ var tickQueue = [];
+ var 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
- queue = queue.concat(tasks);
+ tickQueue.push([].concat(tasks));
nextTick();
}
- queue = scheduler.queue = [];
-
/* waitUntilQuiet does two things:
- * 1. It will run the FINAL `fn` value only when an uncanceled RAF has passed through
+ * 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.
*
* The motivation here is that animation code can request more time from the scheduler
@@ -44512,12 +41972,17 @@ var $$rAFSchedulerFactory = ['$$rAF', function($$rAF) {
return scheduler;
function nextTick() {
- if (!queue.length) return;
+ if (!tickQueue.length) return;
- var items = queue.shift();
- for (var i = 0; i < items.length; i++) {
- items[i]();
+ var updatedQueue = [];
+ for (var i = 0; i < tickQueue.length; i++) {
+ var innerQueue = tickQueue[i];
+ runNextTask(innerQueue);
+ if (innerQueue.length) {
+ updatedQueue.push(innerQueue);
+ }
}
+ tickQueue = updatedQueue;
if (!cancelFn) {
$$rAF(function() {
@@ -44525,109 +41990,27 @@ var $$rAFSchedulerFactory = ['$$rAF', function($$rAF) {
});
}
}
-}];
-
-/**
- * @ngdoc directive
- * @name ngAnimateChildren
- * @restrict AE
- * @element ANY
- *
- * @description
- *
- * ngAnimateChildren allows you to specify that children of this element should animate even if any
- * of the children's parents are currently animating. By default, when an element has an active `enter`, `leave`, or `move`
- * (structural) animation, child elements that also have an active structural animation are not animated.
- *
- * Note that even if `ngAnimteChildren` is set, no child animations will run when the parent element is removed from the DOM (`leave` animation).
- *
- *
- * @param {string} ngAnimateChildren If the value is empty, `true` or `on`,
- * then child animations are allowed. If the value is `false`, child animations are not allowed.
- *
- * @example
- * <example module="ngAnimateChildren" name="ngAnimateChildren" deps="angular-animate.js" animations="true">
- <file name="index.html">
- <div ng-controller="mainController as main">
- <label>Show container? <input type="checkbox" ng-model="main.enterElement" /></label>
- <label>Animate children? <input type="checkbox" ng-model="main.animateChildren" /></label>
- <hr>
- <div ng-animate-children="{{main.animateChildren}}">
- <div ng-if="main.enterElement" class="container">
- List of items:
- <div ng-repeat="item in [0, 1, 2, 3]" class="item">Item {{item}}</div>
- </div>
- </div>
- </div>
- </file>
- <file name="animations.css">
-
- .container.ng-enter,
- .container.ng-leave {
- transition: all ease 1.5s;
- }
-
- .container.ng-enter,
- .container.ng-leave-active {
- opacity: 0;
- }
-
- .container.ng-leave,
- .container.ng-enter-active {
- opacity: 1;
- }
-
- .item {
- background: firebrick;
- color: #FFF;
- margin-bottom: 10px;
- }
-
- .item.ng-enter,
- .item.ng-leave {
- transition: transform 1.5s ease;
- }
-
- .item.ng-enter {
- transform: translateX(50px);
- }
- .item.ng-enter-active {
- transform: translateX(0);
- }
- </file>
- <file name="script.js">
- angular.module('ngAnimateChildren', ['ngAnimate'])
- .controller('mainController', function() {
- this.animateChildren = false;
- this.enterElement = false;
- });
- </file>
- </example>
- */
-var $$AnimateChildrenDirective = ['$interpolate', function($interpolate) {
- return {
- link: function(scope, element, attrs) {
- var val = attrs.ngAnimateChildren;
- if (angular.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
- // animations that run right after compilation
- setData($interpolate(val)(scope));
- attrs.$observe('ngAnimateChildren', setData);
- }
+ function runNextTask(tasks) {
+ var nextTask = tasks.shift();
+ nextTask();
+ }
+}];
- function setData(value) {
+var $$AnimateChildrenDirective = [function() {
+ return function(scope, element, attrs) {
+ var val = attrs.ngAnimateChildren;
+ if (angular.isString(val) && val.length === 0) { //empty attribute
+ element.data(NG_ANIMATE_CHILDREN_DATA, true);
+ } else {
+ attrs.$observe('ngAnimateChildren', function(value) {
value = value === 'on' || value === 'true';
element.data(NG_ANIMATE_CHILDREN_DATA, value);
- }
+ });
}
};
}];
-var ANIMATE_TIMER_KEY = '$$animateCss';
-
/**
* @ngdoc service
* @name $animateCss
@@ -44793,7 +42176,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 styles 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 stlyes 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.
*
@@ -44812,10 +42195,8 @@ var ANIMATE_TIMER_KEY = '$$animateCss';
*
* * `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.)
- * * `structural` - Indicates that the `ng-` prefix will be added to the event class. Setting to `false` or omitting will turn `ng-EVENT` and
- * `ng-EVENT-active` in `EVENT` and `EVENT-active`. Unused if `event` is omitted.
* * `easing` - The CSS easing value that will be applied to the transition or keyframe animation (or both).
- * * `transitionStyle` - The raw CSS transition style that will be used (e.g. `1s linear all`).
+ * * `transition` - 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.
@@ -44830,23 +42211,63 @@ 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 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 collapse and open animation).
- * By default this value is set to `false`.
+ * `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.)
*
* @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.
*/
-var ONE_SECOND = 1000;
-var BASE_TEN = 10;
+// 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 DETECT_CSS_PROPERTIES = {
transitionDuration: TRANSITION_DURATION_PROP,
@@ -44864,15 +42285,6 @@ 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) || {};
@@ -44887,7 +42299,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 negative values to be used later on and not mistake this
+ // then we can still allow for zegative values to be used later on and not mistake this
// value for being greater than any other negative value.
if (val === 0) {
val = null;
@@ -44929,6 +42341,37 @@ 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 {
@@ -44956,31 +42399,14 @@ 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',
- '$$forceReflow', '$sniffer', '$$rAFScheduler', '$$animateQueue',
+ '$document', '$sniffer', '$$rAFScheduler',
function($window, $$jqLite, $$AnimateRunner, $timeout,
- $$forceReflow, $sniffer, $$rAFScheduler, $$animateQueue) {
+ $document, $sniffer, $$rAFScheduler) {
var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);
@@ -45003,7 +42429,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 internal tally of how many duplicate animations are detected.
+ // because we're keeping an interal tally of how many duplicate animations are detected.
gcsLookup.put(cacheKey, timings);
return timings;
}
@@ -45037,7 +42463,7 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
return stagger || {};
}
- var cancelLastRAFRequest;
+ var bod = getDomNode($document).body;
var rafWaitQueue = [];
function waitUntilQuiet(callback) {
rafWaitQueue.push(callback);
@@ -45045,19 +42471,27 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
gcsLookup.flush();
gcsStaggerLookup.flush();
- // DO NOT REMOVE THIS LINE OR REFACTOR OUT THE `pageWidth` variable.
- // PLEASE EXAMINE THE `$$forceReflow` service to understand why.
- var pageWidth = $$forceReflow();
+ //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;
// 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](pageWidth);
+ rafWaitQueue[i](width);
}
rafWaitQueue.length = 0;
});
}
+ return init;
+
function computeTimings(node, className, cacheKey) {
var timings = computeCachedCssStyles(node, className, cacheKey, DETECT_CSS_PROPERTIES);
var aD = timings.animationDelay;
@@ -45072,24 +42506,14 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
return timings;
}
- return function init(element, initialOptions) {
- // all of the animation functions should create
- // a copy of the options data, however, if a
- // parent service has already created a copy then
- // we should stick to using that
- var options = initialOptions || {};
- if (!options.$$prepared) {
- options = prepareAnimationOptions(copy(options));
- }
-
- var restoreStyles = {};
+ function init(element, options) {
var node = getDomNode(element);
- if (!node
- || !node.parentNode
- || !$$animateQueue.enabled()) {
+ if (!node || !node.parentNode) {
return closeAndReturnNoopAnimator();
}
+ options = prepareAnimationOptions(options);
+
var temporaryStyles = [];
var classes = element.attr('class');
var styles = packageStyles(options);
@@ -45102,8 +42526,6 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
var maxDelayTime;
var maxDuration;
var maxDurationTime;
- var startTime;
- var events = [];
if (options.duration === 0 || (!$sniffer.animations && !$sniffer.transitions)) {
return closeAndReturnNoopAnimator();
@@ -45118,20 +42540,20 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
var addRemoveClassName = '';
if (isStructural) {
- structuralClassName = pendClasses(method, EVENT_CLASS_PREFIX, true);
+ structuralClassName = pendClasses(method, 'ng-', true);
} else if (method) {
structuralClassName = method;
}
if (options.addClass) {
- addRemoveClassName += pendClasses(options.addClass, ADD_CLASS_SUFFIX);
+ addRemoveClassName += pendClasses(options.addClass, '-add');
}
if (options.removeClass) {
if (addRemoveClassName.length) {
addRemoveClassName += ' ';
}
- addRemoveClassName += pendClasses(options.removeClass, REMOVE_CLASS_SUFFIX);
+ addRemoveClassName += pendClasses(options.removeClass, '-remove');
}
// there may be a situation where a structural animation is combined together
@@ -45142,11 +42564,12 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
// there actually is a detected transition or keyframe animation
if (options.applyClassesEarly && addRemoveClassName.length) {
applyAnimationClasses(element, options);
+ addRemoveClassName = '';
}
- var preparationClasses = [structuralClassName, addRemoveClassName].join(' ').trim();
- var fullClassName = classes + ' ' + preparationClasses;
- var activeClasses = pendClasses(preparationClasses, ACTIVE_CLASS_SUFFIX);
+ var setupClasses = [structuralClassName, addRemoveClassName].join(' ').trim();
+ var fullClassName = classes + ' ' + setupClasses;
+ var activeClasses = pendClasses(setupClasses, '-active');
var hasToStyles = styles.to && Object.keys(styles.to).length > 0;
var containsKeyframeAnimation = (options.keyframeStyle || '').length > 0;
@@ -45155,7 +42578,7 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
// unless there a is raw keyframe value that is applied to the element.
if (!containsKeyframeAnimation
&& !hasToStyles
- && !preparationClasses) {
+ && !setupClasses) {
return closeAndReturnNoopAnimator();
}
@@ -45170,12 +42593,10 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
};
} else {
cacheKey = gcsHashFn(node, fullClassName);
- stagger = computeCachedCssStaggerStyles(node, preparationClasses, cacheKey, DETECT_STAGGER_CSS_PROPERTIES);
+ stagger = computeCachedCssStaggerStyles(node, setupClasses, cacheKey, DETECT_STAGGER_CSS_PROPERTIES);
}
- if (!options.$$skipPreparationClasses) {
- $$jqLite.addClass(element, preparationClasses);
- }
+ $$jqLite.addClass(element, setupClasses);
var applyOnlyDuration;
@@ -45214,7 +42635,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 && !options.skipBlocking) {
+ if (isFirst) {
blockTransitions(node, SAFE_FAST_FORWARD_DURATION_VALUE);
}
@@ -45256,23 +42677,6 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
return closeAndReturnNoopAnimator();
}
- if (options.delay != null) {
- var delayStyle;
- if (typeof options.delay !== "boolean") {
- delayStyle = parseFloat(options.delay);
- // number in options.delay means we have to recalculate the delay for the closing timeout
- maxDelay = Math.max(delayStyle, 0);
- }
-
- 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.
@@ -45289,19 +42693,13 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
stagger.animationDuration === 0;
}
- if (options.from) {
- if (options.cleanupStyles) {
- registerRestorableStyles(restoreStyles, node, Object.keys(options.from));
- }
- applyAnimationFromStyles(element, options);
- }
-
- if (flags.blockTransition || flags.blockKeyframeAnimation) {
- applyBlocking(maxDuration);
- } else if (!options.skipBlocking) {
+ applyAnimationFromStyles(element, options);
+ if (!flags.blockTransition) {
blockTransitions(node, false);
}
+ applyBlocking(maxDuration);
+
// TODO(matsko): for 1.5 change this code to have an animator object for better debugging
return {
$$willAnimate: true,
@@ -45343,9 +42741,7 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
animationClosed = true;
animationPaused = false;
- if (!options.$$skipPreparationClasses) {
- $$jqLite.removeClass(element, preparationClasses);
- }
+ $$jqLite.removeClass(element, setupClasses);
$$jqLite.removeClass(element, activeClasses);
blockKeyframeAnimations(node, false);
@@ -45361,13 +42757,6 @@ 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
@@ -45377,18 +42766,6 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
options.onDone();
}
- if (events && events.length) {
- // Remove the transitionend / animationend listener(s)
- element.off(events.join(' '), onAnimationProgress);
- }
-
- //Cancel the fallback closing timeout and remove the timer data
- var animationTimerData = element.data(ANIMATE_TIMER_KEY);
- if (animationTimerData) {
- $timeout.cancel(animationTimerData[0].timer);
- element.removeData(ANIMATE_TIMER_KEY);
- }
-
// if the preparation function fails then the promise is not setup
if (runner) {
runner.complete(!rejected);
@@ -45411,8 +42788,6 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
cancel: cancelFn
});
- // should flush the cache animation
- waitUntilQuiet(noop);
close();
return {
@@ -45424,33 +42799,6 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
};
}
- function onAnimationProgress(event) {
- event.stopPropagation();
- var ev = event.originalEvent || event;
-
- // we now always use `Date.now()` due to the recent changes with
- // event.timeStamp in Firefox, Webkit and Chrome (see #13494 for more info)
- var timeStamp = ev.$manualTimeStamp || Date.now();
-
- /* Firefox (or possibly just Gecko) likes to not round values up
- * when a ms measurement is used for the animation */
- var elapsedTime = parseFloat(ev.elapsedTime.toFixed(ELAPSED_TIME_MAX_DECIMAL_PLACES));
-
- /* $manualTimeStamp is a mocked timeStamp value which is set
- * within browserTrigger(). This is only here so that tests can
- * mock animations properly. Real events fallback to event.timeStamp,
- * or, if they don't, then a timeStamp is automatically created for them.
- * We're checking to see if the timeStamp surpasses the expected delay,
- * but we're using elapsedTime instead of the timeStamp on the 2nd
- * pre-condition since animationPauseds sometimes close off early */
- if (Math.max(timeStamp - startTime, 0) >= maxDelayTime && elapsedTime >= maxDuration) {
- // we set this flag to ensure that if the transition is paused then, when resumed,
- // the animation will automatically close itself since transitions cannot be paused.
- animationCompleted = true;
- close();
- }
- }
-
function start() {
if (animationClosed) return;
if (!node.parentNode) {
@@ -45458,6 +42806,8 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
return;
}
+ var startTime, events = [];
+
// even though we only pause keyframe animations here the pause flag
// will still happen when transitions are used. Only the transition will
// not be paused since that is not possible. If the animation ends when
@@ -45477,9 +42827,9 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
}
};
- // checking the stagger duration prevents an accidentally cascade of the CSS delay style
+ // checking the stagger duration prevents an accidently 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 intentional stagger delay style.
+ // rely that the delay value is an intential stagger delay style.
var maxStagger = itemIndex > 0
&& ((timings.transitionDuration && stagger.transitionDuration === 0) ||
(timings.animationDuration && stagger.animationDuration === 0))
@@ -45518,7 +42868,7 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
$$jqLite.addClass(element, activeClasses);
if (flags.recalculateTimingStyles) {
- fullClassName = node.className + ' ' + preparationClasses;
+ fullClassName = node.className + ' ' + setupClasses;
cacheKey = gcsHashFn(node, fullClassName);
timings = computeTimings(node, fullClassName, cacheKey);
@@ -45535,16 +42885,27 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
flags.hasAnimations = timings.animationDuration > 0;
}
- if (flags.applyAnimationDelay) {
+ if (flags.applyTransitionDelay || flags.applyAnimationDelay) {
relativeDelay = typeof options.delay !== "boolean" && truthyTimingValue(options.delay)
? parseFloat(options.delay)
: relativeDelay;
maxDelay = Math.max(relativeDelay, 0);
- timings.animationDelay = relativeDelay;
- delayStyle = getCssDelayStyle(relativeDelay, true);
- temporaryStyles.push(delayStyle);
- node.style[delayStyle[0]] = delayStyle[1];
+
+ 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];
+ }
}
maxDelayTime = maxDelay * ONE_SECOND;
@@ -45573,58 +42934,44 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
}
startTime = Date.now();
- var timerTime = maxDelayTime + CLOSING_TIME_BUFFER * maxDurationTime;
- var endTime = startTime + timerTime;
-
- 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);
- }
- }
+ element.on(events.join(' '), onAnimationProgress);
+ $timeout(onAnimationExpired, maxDelayTime + CLOSING_TIME_BUFFER * maxDurationTime);
- if (setupFallbackTimer) {
- var timer = $timeout(onAnimationExpired, timerTime, false);
- animationsData[0] = {
- timer: timer,
- expectedEndTime: endTime
- };
- animationsData.push(close);
- element.data(ANIMATE_TIMER_KEY, animationsData);
- }
-
- if (events.length) {
- element.on(events.join(' '), onAnimationProgress);
- }
-
- if (options.to) {
- if (options.cleanupStyles) {
- registerRestorableStyles(restoreStyles, node, Object.keys(options.to));
- }
- applyAnimationToStyles(element, options);
- }
+ applyAnimationToStyles(element, options);
}
function onAnimationExpired() {
- 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);
+ // 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();
+ }
+
+ function onAnimationProgress(event) {
+ event.stopPropagation();
+ var ev = event.originalEvent || event;
+ var timeStamp = ev.$manualTimeStamp || ev.timeStamp || Date.now();
+
+ /* Firefox (or possibly just Gecko) likes to not round values up
+ * when a ms measurement is used for the animation */
+ var elapsedTime = parseFloat(ev.elapsedTime.toFixed(ELAPSED_TIME_MAX_DECIMAL_PLACES));
+
+ /* $manualTimeStamp is a mocked timeStamp value which is set
+ * within browserTrigger(). This is only here so that tests can
+ * mock animations properly. Real events fallback to event.timeStamp,
+ * or, if they don't, then a timeStamp is automatically created for them.
+ * We're checking to see if the timeStamp surpasses the expected delay,
+ * but we're using elapsedTime instead of the timeStamp on the 2nd
+ * pre-condition since animations sometimes close off early */
+ if (Math.max(timeStamp - startTime, 0) >= maxDelayTime && elapsedTime >= maxDuration) {
+ // we set this flag to ensure that if the transition is paused then, when resumed,
+ // the animation will automatically close itself since transitions cannot be paused.
+ animationCompleted = true;
+ close();
}
}
}
- };
+ }
}];
}];
@@ -45637,27 +42984,16 @@ var $$AnimateCssDriverProvider = ['$$animationProvider', function($$animationPro
var NG_OUT_ANCHOR_CLASS_NAME = 'ng-anchor-out';
var NG_IN_ANCHOR_CLASS_NAME = 'ng-anchor-in';
- 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) {
+ this.$get = ['$animateCss', '$rootScope', '$$AnimateRunner', '$rootElement', '$document', '$sniffer',
+ function($animateCss, $rootScope, $$AnimateRunner, $rootElement, $document, $sniffer) {
// only browsers that support these properties can render animations
if (!$sniffer.animations && !$sniffer.transitions) return noop;
- var bodyNode = $document[0].body;
+ var bodyNode = getDomNode($document).body;
var rootNode = getDomNode($rootElement);
- var rootBodyElement = jqLite(
- // this is to avoid using something that exists outside of the body
- // 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
- );
-
- var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);
+ var rootBodyElement = jqLite(bodyNode.parentNode === rootNode ? bodyNode : rootNode);
return function initDriverFn(animationDetails) {
return animationDetails.from && animationDetails.to
@@ -45752,7 +43088,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 coords object when iterated
+ // all the keys for the coods object when iterated
forEach(['width','height','top','left'], function(key) {
var value = coords[key];
switch (key) {
@@ -45809,8 +43145,8 @@ var $$AnimateCssDriverProvider = ['$$animationProvider', function($$animationPro
}
function prepareFromToAnchorAnimation(from, to, classes, anchors) {
- var fromAnimation = prepareRegularAnimation(from, noop);
- var toAnimation = prepareRegularAnimation(to, noop);
+ var fromAnimation = prepareRegularAnimation(from);
+ var toAnimation = prepareRegularAnimation(to);
var anchorAnimations = [];
forEach(anchors, function(anchor) {
@@ -45866,23 +43202,19 @@ var $$AnimateCssDriverProvider = ['$$animationProvider', function($$animationPro
var options = animationDetails.options || {};
if (animationDetails.structural) {
- options.event = animationDetails.event;
- options.structural = true;
- options.applyClassesEarly = true;
+ // structural animations ensure that the CSS classes are always applied
+ // before the detection starts.
+ options.structural = 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
- if (animationDetails.event === 'leave') {
+ options.event = animationDetails.event;
+ if (options.event === 'leave') {
options.onDone = options.domOperation;
}
- }
-
- // 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);
+ } else {
+ options.event = null;
}
var animator = $animateCss(element, options);
@@ -45901,14 +43233,12 @@ var $$AnimateCssDriverProvider = ['$$animationProvider', function($$animationPro
// by the time...
var $$AnimateJsProvider = ['$animateProvider', function($animateProvider) {
- this.$get = ['$injector', '$$AnimateRunner', '$$jqLite',
- function($injector, $$AnimateRunner, $$jqLite) {
+ this.$get = ['$injector', '$$AnimateRunner', '$$rAFMutex', '$$jqLite',
+ function($injector, $$AnimateRunner, $$rAFMutex, $$jqLite) {
var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);
// $animateJs(element, 'enter');
return function(element, event, classes, options) {
- var animationClosed = false;
-
// the `classes` argument is optional and if it is not used
// then the classes will be resolved from the element's className
// property as well as options.addClass/options.removeClass.
@@ -45961,32 +43291,8 @@ var $$AnimateJsProvider = ['$animateProvider', function($animateProvider) {
applyAnimationClasses(element, options);
}
- function close() {
- animationClosed = true;
- applyOptions();
- applyAnimationStyles(element, options);
- }
-
- var runner;
-
return {
- $$willAnimate: true,
- end: function() {
- if (runner) {
- runner.end();
- } else {
- close();
- runner = new $$AnimateRunner();
- runner.complete(true);
- }
- return runner;
- },
start: function() {
- if (runner) {
- return runner;
- }
-
- runner = new $$AnimateRunner();
var closeActiveAnimations;
var chain = [];
@@ -46011,7 +43317,8 @@ var $$AnimateJsProvider = ['$animateProvider', function($animateProvider) {
});
}
- runner.setHost({
+ var animationClosed = false;
+ var runner = new $$AnimateRunner({
end: function() {
endAnimations();
},
@@ -46024,7 +43331,9 @@ var $$AnimateJsProvider = ['$animateProvider', function($animateProvider) {
return runner;
function onComplete(success) {
- close(success);
+ animationClosed = true;
+ applyOptions();
+ applyAnimationStyles(element, options);
runner.complete(success);
}
@@ -46244,7 +43553,6 @@ var NG_ANIMATE_PIN_DATA = '$ngAnimatePin';
var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
var PRE_DIGEST_STATE = 1;
var RUNNING_STATE = 2;
- var ONE_SPACE = ' ';
var rules = this.rules = {
skip: [],
@@ -46252,50 +43560,28 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
join: []
};
- function makeTruthyCssClassMap(classString) {
- if (!classString) {
- return null;
- }
-
- var keys = classString.split(ONE_SPACE);
- var map = Object.create(null);
-
- forEach(keys, function(key) {
- map[key] = true;
- });
- return map;
- }
-
- function hasMatchingClasses(newClassString, currentClassString) {
- if (newClassString && currentClassString) {
- var currentClassMap = makeTruthyCssClassMap(currentClassString);
- return newClassString.split(ONE_SPACE).some(function(className) {
- return currentClassMap[className];
- });
- }
- }
-
function isAllowed(ruleType, element, currentAnimation, previousAnimation) {
return rules[ruleType].some(function(fn) {
return fn(element, currentAnimation, previousAnimation);
});
}
- function hasAnimationClasses(animation, and) {
- var a = (animation.addClass || '').length > 0;
- var b = (animation.removeClass || '').length > 0;
+ function hasAnimationClasses(options, and) {
+ options = options || {};
+ var a = (options.addClass || '').length > 0;
+ var b = (options.removeClass || '').length > 0;
return and ? a && b : a || b;
}
rules.join.push(function(element, newAnimation, currentAnimation) {
// if the new animation is class-based then we can just tack that on
- return !newAnimation.structural && hasAnimationClasses(newAnimation);
+ return !newAnimation.structural && hasAnimationClasses(newAnimation.options);
});
rules.skip.push(function(element, newAnimation, currentAnimation) {
// there is no need to animate anything if no classes are being added and
// there is no structural animation that will be triggered
- return !newAnimation.structural && !hasAnimationClasses(newAnimation);
+ return !newAnimation.structural && !hasAnimationClasses(newAnimation.options);
});
rules.skip.push(function(element, newAnimation, currentAnimation) {
@@ -46305,8 +43591,8 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
});
rules.skip.push(function(element, newAnimation, currentAnimation) {
- // 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;
+ // if there is a current animation then skip the class-based animation
+ return currentAnimation.structural && !newAnimation.structural;
});
rules.cancel.push(function(element, newAnimation, currentAnimation) {
@@ -46321,50 +43607,22 @@ 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;
- var cR = currentAnimation.removeClass;
+ var nO = newAnimation.options;
+ var cO = currentAnimation.options;
- // early detection to save the global CPU shortage :)
- if ((isUndefined(nA) && isUndefined(nR)) || (isUndefined(cA) && isUndefined(cR))) {
- return false;
- }
-
- return hasMatchingClasses(nA, cR) || hasMatchingClasses(nR, cA);
+ // if the exact same CSS class is added/removed then it's safe to cancel it
+ return (nO.addClass && nO.addClass === cO.removeClass) || (nO.removeClass && nO.removeClass === cO.addClass);
});
this.$get = ['$$rAF', '$rootScope', '$rootElement', '$document', '$$HashMap',
- '$$animation', '$$AnimateRunner', '$templateRequest', '$$jqLite', '$$forceReflow',
+ '$$animation', '$$AnimateRunner', '$templateRequest', '$$jqLite',
function($$rAF, $rootScope, $rootElement, $document, $$HashMap,
- $$animation, $$AnimateRunner, $templateRequest, $$jqLite, $$forceReflow) {
+ $$animation, $$AnimateRunner, $templateRequest, $$jqLite) {
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();
- });
- }
- };
- }
+ var animationsEnabled = null;
// Wait until all directive and route-related templates are downloaded and
// compiled. The $templateRequest.totalPendingRequests variable keeps track of
@@ -46395,6 +43653,8 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
}
);
+ var bodyElement = jqLite($document[0].body);
+
var callbackRegistry = {};
// remember that the classNameFilter is set during the provider/config
@@ -46408,28 +43668,18 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);
- function normalizeAnimationDetails(element, animation) {
- return mergeAnimationDetails(element, animation, {});
+ function normalizeAnimationOptions(element, options) {
+ return mergeAnimationOptions(element, options, {});
}
- // IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259.
- var contains = Node.prototype.contains || function(arg) {
- // jshint bitwise: false
- return this === arg || !!(this.compareDocumentPosition(arg) & 16);
- // jshint bitwise: true
- };
-
- function findCallbacks(parent, element, event) {
+ function findCallbacks(element, event) {
var targetNode = getDomNode(element);
- var targetParentNode = getDomNode(parent);
var matches = [];
var entries = callbackRegistry[event];
if (entries) {
forEach(entries, function(entry) {
- if (contains.call(entry.node, targetNode)) {
- matches.push(entry.callback);
- } else if (event === 'leave' && contains.call(entry.node, targetParentNode)) {
+ if (entry.node.contains(targetNode)) {
matches.push(entry.callback);
}
});
@@ -46438,7 +43688,15 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
return matches;
}
- var $animate = {
+ 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);
callbackRegistry[event] = callbackRegistry[event] || [];
@@ -46446,11 +43704,6 @@ 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() {
- $animate.off(event, container, callback);
- });
},
off: function(event, container, callback) {
@@ -46509,7 +43762,12 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
bool = !recordExists;
} else {
// (element, bool) - Element setter
- disabledElementsLookup.put(node, !bool);
+ bool = !!bool;
+ if (!bool) {
+ disabledElementsLookup.put(node, true);
+ } else if (recordExists) {
+ disabledElementsLookup.remove(node);
+ }
}
}
}
@@ -46518,14 +43776,7 @@ 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
- // the input data when running `$animateCss`.
- var options = copy(initialOptions);
-
+ function queueAnimation(element, event, options) {
var node, parent;
element = stripCommentsFromElement(element);
if (element) {
@@ -46539,25 +43790,22 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
// These methods will become available after the digest has passed
var runner = new $$AnimateRunner();
- // this is used to trigger callbacks in postDigest mode
- var runInNextPostDigestOrNow = postDigestTaskFactory();
+ // 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;
+ }
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;
}
@@ -46566,14 +43814,6 @@ 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();
@@ -46585,9 +43825,7 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
// 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 || disabledElementsLookup.get(node);
var existingAnimation = (!skipAnimations && activeAnimationsLookup.get(node)) || {};
var hasExistingAnimation = !!existingAnimation.state;
@@ -46610,8 +43848,6 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
structural: isStructural,
element: element,
event: event,
- addClass: options.addClass,
- removeClass: options.removeClass,
close: close,
options: options,
runner: runner
@@ -46624,10 +43860,11 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
close();
return runner;
} else {
- mergeAnimationDetails(element, existingAnimation, newAnimation);
+ mergeAnimationOptions(element, existingAnimation.options, options);
return existingAnimation.runner;
}
}
+
var cancelAnimationFlag = isAllowed('cancel', element, newAnimation, existingAnimation);
if (cancelAnimationFlag) {
if (existingAnimation.state === RUNNING_STATE) {
@@ -46641,10 +43878,8 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
// method which will call the runner methods in async.
existingAnimation.close();
} else {
- // this will merge the new animation options into existing animation options
- mergeAnimationDetails(element, existingAnimation, newAnimation);
-
- return existingAnimation.runner;
+ // this will merge the existing animation options into this new follow-up animation
+ mergeAnimationOptions(element, newAnimation.options, existingAnimation.options);
}
} else {
// a joined animation means that this animation will take over the existing one
@@ -46653,23 +43888,18 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
var joinAnimationFlag = isAllowed('join', element, newAnimation, existingAnimation);
if (joinAnimationFlag) {
if (existingAnimation.state === RUNNING_STATE) {
- normalizeAnimationDetails(element, newAnimation);
+ normalizeAnimationOptions(element, options);
} else {
- applyGeneratedPreparationClasses(element, isStructural ? event : null, options);
-
event = newAnimation.event = existingAnimation.event;
- options = mergeAnimationDetails(element, existingAnimation, newAnimation);
-
- //we return the same runner since only the option values of this animation will
- //be fed into the `existingAnimation`.
- return existingAnimation.runner;
+ options = mergeAnimationOptions(element, existingAnimation.options, newAnimation.options);
+ return runner;
}
}
}
} else {
// normalization in this case means that it removes redundant CSS classes that
// already exist (addClass) or do not exist (removeClass) on the element
- normalizeAnimationDetails(element, newAnimation);
+ normalizeAnimationOptions(element, options);
}
// when the options are merged and cleaned up we may end up not having to do
@@ -46679,7 +43909,7 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
if (!isValidAnimation) {
// animate (from/to) can be quickly checked first, otherwise we check if any classes are present
isValidAnimation = (newAnimation.event === 'animate' && Object.keys(newAnimation.options.to || {}).length > 0)
- || hasAnimationClasses(newAnimation);
+ || hasAnimationClasses(newAnimation.options);
}
if (!isValidAnimation) {
@@ -46688,6 +43918,10 @@ 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;
@@ -46709,7 +43943,7 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
var isValidAnimation = parentElement.length > 0
&& (animationDetails.event === 'animate'
|| animationDetails.structural
- || hasAnimationClasses(animationDetails));
+ || hasAnimationClasses(animationDetails.options));
// this means that the previous animation was cancelled
// even if the follow-up animation is the same event
@@ -46741,13 +43975,16 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
// this combined multiple class to addClass / removeClass into a setClass event
// so long as a structural event did not take over the animation
- event = !animationDetails.structural && hasAnimationClasses(animationDetails, true)
+ event = !animationDetails.structural && hasAnimationClasses(animationDetails.options, true)
? '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);
@@ -46766,25 +44003,11 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
return runner;
function notifyProgress(runner, event, phase, data) {
- runInNextPostDigestOrNow(function() {
- var callbacks = findCallbacks(parent, 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);
- });
- });
- }
- });
+ triggerCallback(event, 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();
@@ -46798,15 +44021,15 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
forEach(children, function(child) {
var state = parseInt(child.getAttribute(NG_ANIMATE_ATTR_NAME));
var animationDetails = activeAnimationsLookup.get(child);
- if (animationDetails) {
- switch (state) {
- case RUNNING_STATE:
- animationDetails.runner.end();
- /* falls through */
- case PRE_DIGEST_STATE:
+ switch (state) {
+ case RUNNING_STATE:
+ animationDetails.runner.end();
+ /* falls through */
+ case PRE_DIGEST_STATE:
+ if (animationDetails) {
activeAnimationsLookup.remove(child);
- break;
- }
+ }
+ break;
}
});
}
@@ -46821,61 +44044,67 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
return getDomNode(nodeOrElmA) === getDomNode(nodeOrElmB);
}
- /**
- * This fn returns false if any of the following is true:
- * a) animations on any parent element are disabled, and animations on the element aren't explicitly allowed
- * b) a parent element has an ongoing structural animation, and animateChildren is false
- * c) the element is not a child of the body
- * d) the element is not a child of the $rootElement
- */
+ 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 bodyElement = jqLite($document[0].body);
- var bodyElementDetected = isMatchingElement(element, bodyElement) || element[0].nodeName === 'HTML';
- var rootElementDetected = isMatchingElement(element, $rootElement);
+ var bodyElementDetected = false;
+ var rootElementDetected = false;
var parentAnimationDetected = false;
var animateChildren;
- var elementDisabled = disabledElementsLookup.get(getDomNode(element));
- var parentHost = jqLite.data(element[0], NG_ANIMATE_PIN_DATA);
+ var parentHost = element.data(NG_ANIMATE_PIN_DATA);
if (parentHost) {
parentElement = parentHost;
}
- parentElement = getDomNode(parentElement);
-
- while (parentElement) {
+ while (parentElement && parentElement.length) {
if (!rootElementDetected) {
// angular doesn't want to attempt to animate elements outside of the application
// therefore we need to ensure that the rootElement is an ancestor of the current element
rootElementDetected = isMatchingElement(parentElement, $rootElement);
}
- if (parentElement.nodeType !== ELEMENT_NODE) {
+ var parentNode = parentElement[0];
+ if (parentNode.nodeType !== ELEMENT_NODE) {
// no point in inspecting the #document element
break;
}
- var details = activeAnimationsLookup.get(parentElement) || {};
+ var details = activeAnimationsLookup.get(parentNode) || {};
// either an enter, leave or move animation will commence
// therefore we can't allow any animations to take place
// but if a parent animation is class-based then that's ok
if (!parentAnimationDetected) {
- var parentElementDisabled = disabledElementsLookup.get(parentElement);
-
- if (parentElementDisabled === true && elementDisabled !== false) {
- // disable animations if the user hasn't explicitly enabled animations on the
- // current element
- elementDisabled = true;
- // element is disabled via parent element, no need to check anything else
- break;
- } else if (parentElementDisabled === false) {
- elementDisabled = false;
- }
- parentAnimationDetected = details.structural;
+ parentAnimationDetected = details.structural || disabledElementsLookup.get(parentNode);
}
if (isUndefined(animateChildren) || animateChildren === true) {
- var value = jqLite.data(parentElement, NG_ANIMATE_CHILDREN_DATA);
+ var value = parentElement.data(NG_ANIMATE_CHILDREN_DATA);
if (isDefined(value)) {
animateChildren = value;
}
@@ -46884,32 +44113,28 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
// there is no need to continue traversing at this point
if (parentAnimationDetected && animateChildren === false) break;
+ if (!rootElementDetected) {
+ // angular doesn't want to attempt to animate elements outside of the application
+ // therefore we need to ensure that the rootElement is an ancestor of the current element
+ rootElementDetected = isMatchingElement(parentElement, $rootElement);
+ if (!rootElementDetected) {
+ parentHost = parentElement.data(NG_ANIMATE_PIN_DATA);
+ if (parentHost) {
+ parentElement = parentHost;
+ }
+ }
+ }
+
if (!bodyElementDetected) {
- // we also need to ensure that the element is or will be a part of the body element
+ // we also need to ensure that the element is or will be apart of the body element
// otherwise it is pointless to even issue an animation to be rendered
bodyElementDetected = isMatchingElement(parentElement, bodyElement);
}
- if (bodyElementDetected && rootElementDetected) {
- // If both body and root have been found, any other checks are pointless,
- // as no animation data should live outside the application
- break;
- }
-
- if (!rootElementDetected) {
- // If no rootElement is detected, check if the parentElement is pinned to another element
- parentHost = jqLite.data(parentElement, NG_ANIMATE_PIN_DATA);
- if (parentHost) {
- // The pin target element becomes the next parent element
- parentElement = getDomNode(parentHost);
- continue;
- }
- }
-
- parentElement = parentElement.parentNode;
+ parentElement = parentElement.parent();
}
- var allowAnimation = (!parentAnimationDetected || animateChildren) && elementDisabled !== true;
+ var allowAnimation = !parentAnimationDetected || animateChildren;
return allowAnimation && rootElementDetected && bodyElementDetected;
}
@@ -46929,112 +44154,184 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
}];
}];
-var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
- var NG_ANIMATE_REF_ATTR = 'ng-animate-ref';
+var $$rAFMutexFactory = ['$$rAF', function($$rAF) {
+ return function() {
+ var passed = false;
+ $$rAF(function() {
+ passed = true;
+ });
+ return function(fn) {
+ passed ? fn() : $$rAF(fn);
+ };
+ };
+}];
- var drivers = this.drivers = [];
+var $$AnimateRunnerFactory = ['$q', '$$rAFMutex', function($q, $$rAFMutex) {
+ var INITIAL_STATE = 0;
+ var DONE_PENDING_STATE = 1;
+ var DONE_COMPLETE_STATE = 2;
- var RUNNER_STORAGE_KEY = '$$animationRunner';
+ AnimateRunner.chain = function(chain, callback) {
+ var index = 0;
- function setRunner(element, runner) {
- element.data(RUNNER_STORAGE_KEY, runner);
- }
+ next();
+ function next() {
+ if (index === chain.length) {
+ callback(true);
+ return;
+ }
- function removeRunner(element) {
- element.removeData(RUNNER_STORAGE_KEY);
- }
+ chain[index](function(response) {
+ if (response === false) {
+ callback(false);
+ return;
+ }
+ index++;
+ next();
+ });
+ }
+ };
- function getRunner(element) {
- return element.data(RUNNER_STORAGE_KEY);
+ AnimateRunner.all = function(runners, callback) {
+ var count = 0;
+ var status = true;
+ forEach(runners, function(runner) {
+ runner.done(onProgress);
+ });
+
+ function onProgress(response) {
+ status = status && response;
+ if (++count === runners.length) {
+ callback(status);
+ }
+ }
+ };
+
+ function AnimateRunner(host) {
+ this.setHost(host);
+
+ this._doneCallbacks = [];
+ this._runInAnimationFrame = $$rAFMutex();
+ this._state = 0;
}
- this.$get = ['$$jqLite', '$rootScope', '$injector', '$$AnimateRunner', '$$HashMap', '$$rAFScheduler',
- function($$jqLite, $rootScope, $injector, $$AnimateRunner, $$HashMap, $$rAFScheduler) {
+ AnimateRunner.prototype = {
+ setHost: function(host) {
+ this.host = host || {};
+ },
- var animationQueue = [];
- var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);
+ done: function(fn) {
+ if (this._state === DONE_COMPLETE_STATE) {
+ fn();
+ } else {
+ this._doneCallbacks.push(fn);
+ }
+ },
- 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: []
+ progress: noop,
+
+ getPromise: function() {
+ if (!this.promise) {
+ var self = this;
+ this.promise = $q(function(resolve, reject) {
+ self.done(function(status) {
+ status === false ? reject() : resolve();
+ });
});
}
+ return this.promise;
+ },
- for (i = 0; i < animations.length; i++) {
- processNode(animations[i]);
+ then: function(resolveHandler, rejectHandler) {
+ return this.getPromise().then(resolveHandler, rejectHandler);
+ },
+
+ 'catch': function(handler) {
+ return this.getPromise()['catch'](handler);
+ },
+
+ 'finally': function(handler) {
+ return this.getPromise()['finally'](handler);
+ },
+
+ pause: function() {
+ if (this.host.pause) {
+ this.host.pause();
}
+ },
- return flatten(tree);
+ resume: function() {
+ if (this.host.resume) {
+ this.host.resume();
+ }
+ },
- function processNode(entry) {
- if (entry.processed) return entry;
- entry.processed = true;
+ end: function() {
+ if (this.host.end) {
+ this.host.end();
+ }
+ this._resolve(true);
+ },
- var elementNode = entry.domNode;
- var parentNode = elementNode.parentNode;
- lookup.put(elementNode, entry);
+ cancel: function() {
+ if (this.host.cancel) {
+ this.host.cancel();
+ }
+ this._resolve(false);
+ },
- var parentEntry;
- while (parentNode) {
- parentEntry = lookup.get(parentNode);
- if (parentEntry) {
- if (!parentEntry.processed) {
- parentEntry = processNode(parentEntry);
- }
- break;
- }
- parentNode = parentNode.parentNode;
- }
+ complete: function(response) {
+ var self = this;
+ if (self._state === INITIAL_STATE) {
+ self._state = DONE_PENDING_STATE;
+ self._runInAnimationFrame(function() {
+ self._resolve(response);
+ });
+ }
+ },
- (parentEntry || tree).children.push(entry);
- return entry;
+ _resolve: function(response) {
+ if (this._state !== DONE_COMPLETE_STATE) {
+ forEach(this._doneCallbacks, function(fn) {
+ fn(response);
+ });
+ this._doneCallbacks.length = 0;
+ this._state = DONE_COMPLETE_STATE;
}
+ }
+ };
- function flatten(tree) {
- var result = [];
- var queue = [];
- var i;
+ return AnimateRunner;
+}];
- for (i = 0; i < tree.children.length; i++) {
- queue.push(tree.children[i]);
- }
+var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
+ var NG_ANIMATE_REF_ATTR = 'ng-animate-ref';
- var remainingLevelEntries = queue.length;
- var nextLevelEntries = 0;
- var row = [];
+ var drivers = this.drivers = [];
- 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--;
- }
+ var RUNNER_STORAGE_KEY = '$$animationRunner';
- if (row.length) {
- result.push(row);
- }
+ function setRunner(element, runner) {
+ element.data(RUNNER_STORAGE_KEY, runner);
+ }
- return result;
- }
- }
+ function removeRunner(element) {
+ element.removeData(RUNNER_STORAGE_KEY);
+ }
+
+ function getRunner(element) {
+ return element.data(RUNNER_STORAGE_KEY);
+ }
+
+ this.$get = ['$$jqLite', '$rootScope', '$injector', '$$AnimateRunner', '$$rAFScheduler',
+ function($$jqLite, $rootScope, $injector, $$AnimateRunner, $$rAFScheduler) {
+
+ var animationQueue = [];
+ var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);
+
+ var totalPendingClassBasedAnimations = 0;
+ var totalActiveClassBasedAnimations = 0;
+ var classBasedAnimationsQueue = [];
// TODO(matsko): document the signature in a better way
return function(element, event, options) {
@@ -47064,10 +44361,10 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
options.tempClasses = null;
}
- var prepareClassName;
- if (isStructural) {
- prepareClassName = 'ng-' + event + PREPARE_CLASS_SUFFIX;
- $$jqLite.addClass(element, prepareClassName);
+ var classBasedIndex;
+ if (!isStructural) {
+ classBasedIndex = totalPendingClassBasedAnimations;
+ totalPendingClassBasedAnimations += 1;
}
animationQueue.push({
@@ -47076,6 +44373,7 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
element: element,
classes: classes,
event: event,
+ classBasedIndex: classBasedIndex,
structural: isStructural,
options: options,
beforeStart: beforeStart,
@@ -47090,6 +44388,10 @@ 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
@@ -47097,58 +44399,67 @@ 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;
- var groupedAnimations = groupAnimations(animations);
- var toBeSortedAnimations = [];
-
- 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();
-
- var startAnimationFn, closeFn = animationEntry.close;
-
- // 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)) {
- var operation = invokeFirstDriver(animationEntry);
- if (operation) {
- startAnimationFn = operation.start;
- }
- }
+ forEach(groupAnimations(animations), function(animationEntry) {
+ if (animationEntry.structural) {
+ triggerAnimationStart();
+ } else {
+ classBasedAnimationsQueue.push({
+ node: getDomNode(animationEntry.element),
+ fn: triggerAnimationStart
+ });
- if (!startAnimationFn) {
- closeFn();
- } else {
- var animationRunner = startAnimationFn();
- animationRunner.done(function(status) {
- closeFn(!status);
- });
- updateAnimationRunners(animationEntry, animationRunner);
+ 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;
+ });
+
+ $$rAFScheduler(classBasedAnimationsQueue);
+ }
+ }
+
+ 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();
+
+ var startAnimationFn, closeFn = animationEntry.close;
+
+ // 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;
}
}
- });
- });
- // 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));
+ if (!startAnimationFn) {
+ closeFn();
+ } else {
+ var animationRunner = startAnimationFn();
+ animationRunner.done(function(status) {
+ closeFn(!status);
+ });
+ updateAnimationRunners(animationEntry, animationRunner);
+ }
+ }
+ });
});
return runner;
@@ -47234,7 +44545,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 effectively marries the two elements together to use
+ // one shared CSS class which effictively 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);
@@ -47292,10 +44603,6 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
if (tempClasses) {
$$jqLite.addClass(element, tempClasses);
}
- if (prepareClassName) {
- $$jqLite.removeClass(element, prepareClassName);
- prepareClassName = null;
- }
}
function updateAnimationRunners(animation, newRunner) {
@@ -47337,127 +44644,12 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
}];
}];
-/**
- * @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
- * <example name="ngAnimateSwap-directive" module="ngAnimateSwapExample"
- * deps="angular-animate.js"
- * animations="true" fixBase="true">
- * <file name="index.html">
- * <div class="container" ng-controller="AppCtrl">
- * <div ng-animate-swap="number" class="cell swap-animation" ng-class="colorClass(number)">
- * {{ number }}
- * </div>
- * </div>
- * </file>
- * <file name="script.js">
- * 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];
- * };
- * }]);
- * </file>
- * <file name="animations.css">
- * .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; }
- * </file>
- * </example>
- */
-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);
- });
- }
- });
- }
- };
-}];
-
/* global angularAnimateModule: true,
- ngAnimateSwapDirective,
- $$AnimateAsyncRunFactory,
+ $$rAFMutexFactory,
$$rAFSchedulerFactory,
$$AnimateChildrenDirective,
+ $$AnimateRunnerFactory,
$$AnimateQueueProvider,
$$AnimationProvider,
$AnimateCssProvider,
@@ -47472,7 +44664,7 @@ var ngAnimateSwapDirective = ['$animate', '$rootScope', function($animate, $root
* @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` the animation hooks are enabled for an Angular app.
+ * callback hooks. Animations are not enabled by default, however, by including `ngAnimate` then the animation hooks are enabled for an Angular app.
*
* <div doc-module-components="ngAnimate"></div>
*
@@ -47505,7 +44697,7 @@ var ngAnimateSwapDirective = ['$animate', '$rootScope', function($animate, $root
* 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 an element using `ng-if`:
+ * The example below shows how an `enter` animation can be made possible on a element using `ng-if`:
*
* ```html
* <div ng-if="bool" class="fade">
@@ -47640,8 +44832,8 @@ var ngAnimateSwapDirective = ['$animate', '$rootScope', function($animate, $root
* /&#42; this will have a 100ms delay between each successive leave animation &#42;/
* transition-delay: 0.1s;
*
- * /&#42; As of 1.4.4, this must always be set: it signals ngAnimate
- * to not accidentally inherit a delay property from another CSS class &#42;/
+ * /&#42; in case the stagger doesn't work then the duration value
+ * must be set to 0 to avoid an accidental CSS inheritance &#42;/
* transition-duration: 0s;
* }
* .my-animation.ng-enter.ng-enter-active {
@@ -47706,39 +44898,11 @@ var ngAnimateSwapDirective = ['$animate', '$rootScope', function($animate, $root
* the CSS class once an animation has completed.)
*
*
- * ### The `ng-[event]-prepare` class
- *
- * This is a special class that can be used to prevent unwanted flickering / flash of content before
- * the actual animation starts. The class is added as soon as an animation is initialized, but removed
- * before the actual animation starts (after waiting for a $digest).
- * It is also only added for *structural* animations (`enter`, `move`, and `leave`).
- *
- * In practice, flickering can appear when nesting elements with structural animations such as `ngIf`
- * into elements that have class-based animations such as `ngClass`.
- *
- * ```html
- * <div ng-class="{red: myProp}">
- * <div ng-class="{blue: myProp}">
- * <div class="message" ng-if="myProp"></div>
- * </div>
- * </div>
- * ```
- *
- * It is possible that during the `enter` animation, the `.message` div will be briefly visible before it starts animating.
- * In that case, you can add styles to the CSS that make sure the element stays hidden before the animation starts:
- *
- * ```css
- * .message.ng-enter-prepare {
- * opacity: 0;
- * }
- *
- * ```
- *
* ## JavaScript-based Animations
*
* ngAnimate also allows for animations to be consumed by JavaScript code. The approach is similar to CSS-based animations (where there is a shared
* CSS class that is referenced in our HTML code) but in addition we need to register the JavaScript animation on the module. By making use of the
- * `module.animation()` module function we can register the animation.
+ * `module.animation()` module function we can register the ainmation.
*
* Let's see an example of a enter/leave animation using `ngRepeat`:
*
@@ -47770,7 +44934,7 @@ var ngAnimateSwapDirective = ['$animate', '$rootScope', function($animate, $root
* jQuery(element).fadeOut(1000, doneFn);
* }
* }
- * }]);
+ * }]
* ```
*
* The nice thing about JS-based animations is that we can inject other services and make use of advanced animation libraries such as
@@ -47801,7 +44965,7 @@ var ngAnimateSwapDirective = ['$animate', '$rootScope', function($animate, $root
* // do some cool animation and call the doneFn
* }
* }
- * }]);
+ * }]
* ```
*
* ## CSS + JS Animations Together
@@ -47823,7 +44987,7 @@ var ngAnimateSwapDirective = ['$animate', '$rootScope', function($animate, $root
* jQuery(element).slideIn(1000, doneFn);
* }
* }
- * }]);
+ * }]
* ```
*
* ```css
@@ -47843,15 +45007,16 @@ var ngAnimateSwapDirective = ['$animate', '$rootScope', function($animate, $root
* ```js
* myModule.animation('.slide', ['$animateCss', function($animateCss) {
* return {
- * enter: function(element) {
+ * enter: function(element, doneFn) {
* // this will trigger `.slide.ng-enter` and `.slide.ng-enter-active`.
- * return $animateCss(element, {
+ * var runner = $animateCss(element, {
* event: 'enter',
* structural: true
- * });
+ * }).start();
+* runner.done(doneFn);
* }
* }
- * }]);
+ * }]
* ```
*
* The nice thing here is that we can save bandwidth by sticking to our CSS-based animation code and we don't need to rely on a 3rd-party animation framework.
@@ -47863,17 +45028,18 @@ var ngAnimateSwapDirective = ['$animate', '$rootScope', function($animate, $root
* ```js
* myModule.animation('.slide', ['$animateCss', function($animateCss) {
* return {
- * enter: function(element) {
- * return $animateCss(element, {
+ * enter: function(element, doneFn) {
+ * var runner = $animateCss(element, {
* event: 'enter',
- * structural: true,
* addClass: 'maroon-setting',
* from: { height:0 },
* to: { height: 200 }
- * });
+ * }).start();
+ *
+ * runner.done(doneFn);
* }
* }
- * }]);
+ * }]
* ```
*
* Now we can fill in the rest via our transition CSS code:
@@ -48151,6 +45317,31 @@ var ngAnimateSwapDirective = ['$animate', '$rootScope', function($animate, $root
* possible be sure to visit the {@link ng.$animate $animate service API page}.
*
*
+ * ### Preventing Collisions With Third Party Libraries
+ *
+ * Some third-party frameworks place animation duration defaults across many element or className
+ * selectors in order to make their code small and reuseable. This can lead to issues with ngAnimate, which
+ * is expecting actual animations on these elements and has to wait for their completion.
+ *
+ * You can prevent this unwanted behavior by using a prefix on all your animation classes:
+ *
+ * ```css
+ * /&#42; prefixed with animate- &#42;/
+ * .animate-fade-add.animate-fade-add-active {
+ * transition:1s linear all;
+ * opacity:0;
+ * }
+ * ```
+ *
+ * You then configure `$animate` to enforce this prefix:
+ *
+ * ```js
+ * $animateProvider.classNameFilter(/animate-/);
+ * ```
+ *
+ * This also may provide your application with a speed boost since only specific elements containing CSS class prefix
+ * will be evaluated for animation when any DOM changes occur in the application.
+ *
* ## Callbacks and Promises
*
* When `$animate` is called it returns a promise that can be used to capture when the animation has ended. Therefore if we were to trigger
@@ -48190,14 +45381,16 @@ var ngAnimateSwapDirective = ['$animate', '$rootScope', function($animate, $root
* @description
* The ngAnimate `$animate` service documentation is the same for the core `$animate` service.
*
- * Click here {@link ng.$animate to learn more about animations with `$animate`}.
+ * Click here {@link ng.$animate $animate to learn more about animations with `$animate`}.
*/
angular.module('ngAnimate', [])
- .directive('ngAnimateSwap', ngAnimateSwapDirective)
-
.directive('ngAnimateChildren', $$AnimateChildrenDirective)
+
+ .factory('$$rAFMutex', $$rAFMutexFactory)
.factory('$$rAFScheduler', $$rAFSchedulerFactory)
+ .factory('$$AnimateRunner', $$AnimateRunnerFactory)
+
.provider('$$animateQueue', $$AnimateQueueProvider)
.provider('$$animation', $$AnimationProvider)
@@ -48218,8 +45411,8 @@ angular.module('ngAnimate', [])
*/
/**
- * @license AngularJS v1.5.3
- * (c) 2010-2016 Google, Inc. http://angularjs.org
+ * @license AngularJS v1.4.3
+ * (c) 2010-2015 Google, Inc. http://angularjs.org
* License: MIT
*/
(function(window, angular, undefined) {'use strict';
@@ -48252,23 +45445,36 @@ var $sanitizeMinErr = angular.$$minErr('$sanitize');
* See {@link ngSanitize.$sanitize `$sanitize`} for usage.
*/
+/*
+ * HTML Parser By Misko Hevery (misko@hevery.com)
+ * based on: HTML Parser By John Resig (ejohn.org)
+ * Original code by Erik Arvidsson, Mozilla Public License
+ * http://erik.eae.net/simplehtmlparser/simplehtmlparser.js
+ *
+ * // Use like so:
+ * htmlParser(htmlString, {
+ * start: function(tag, attrs, unary) {},
+ * end: function(tag) {},
+ * chars: function(text) {},
+ * comment: function(text) {}
+ * });
+ *
+ */
+
+
/**
* @ngdoc service
* @name $sanitize
* @kind function
*
* @description
- * Sanitizes an html string by stripping all potentially dangerous tokens.
- *
* The input is sanitized by parsing the HTML into tokens. All safe tokens (from a whitelist) are
* then serialized back to properly escaped html string. This means that no unsafe input can make
- * it into the returned string.
- *
- * The whitelist for URL sanitization of attribute values is configured using the functions
- * `aHrefSanitizationWhitelist` and `imgSrcSanitizationWhitelist` of {@link ng.$compileProvider
- * `$compileProvider`}.
- *
- * The input may also contain SVG markup if this is enabled via {@link $sanitizeProvider}.
+ * it into the returned string, however, since our parser is more strict than a typical browser
+ * parser, it's possible that some obscure input, which would be recognized as valid HTML by a
+ * browser, won't make it through the sanitizer. The input may also contain SVG markup.
+ * The whitelist is configured using the functions `aHrefSanitizationWhitelist` and
+ * `imgSrcSanitizationWhitelist` of {@link ng.$compileProvider `$compileProvider`}.
*
* @param {string} html HTML input.
* @returns {string} Sanitized HTML.
@@ -48354,70 +45560,16 @@ var $sanitizeMinErr = angular.$$minErr('$sanitize');
</file>
</example>
*/
-
-
-/**
- * @ngdoc provider
- * @name $sanitizeProvider
- *
- * @description
- * Creates and configures {@link $sanitize} instance.
- */
function $SanitizeProvider() {
- var svgEnabled = false;
-
this.$get = ['$$sanitizeUri', function($$sanitizeUri) {
- if (svgEnabled) {
- angular.extend(validElements, svgElements);
- }
return function(html) {
var buf = [];
htmlParser(html, htmlSanitizeWriter(buf, function(uri, isImage) {
- return !/^unsafe:/.test($$sanitizeUri(uri, isImage));
+ return !/^unsafe/.test($$sanitizeUri(uri, isImage));
}));
return buf.join('');
};
}];
-
-
- /**
- * @ngdoc method
- * @name $sanitizeProvider#enableSvg
- * @kind function
- *
- * @description
- * Enables a subset of svg to be supported by the sanitizer.
- *
- * <div class="alert alert-warning">
- * <p>By enabling this setting without taking other precautions, you might expose your
- * application to click-hijacking attacks. In these attacks, sanitized svg elements could be positioned
- * outside of the containing element and be rendered over other elements on the page (e.g. a login
- * link). Such behavior can then result in phishing incidents.</p>
- *
- * <p>To protect against these, explicitly setup `overflow: hidden` css rule for all potential svg
- * tags within the sanitized content:</p>
- *
- * <br>
- *
- * <pre><code>
- * .rootOfTheIncludedContent svg {
- * overflow: hidden !important;
- * }
- * </code></pre>
- * </div>
- *
- * @param {boolean=} regexp New regexp to whitelist urls with.
- * @returns {boolean|ng.$sanitizeProvider} Returns the currently configured value if called
- * without an argument or self for chaining otherwise.
- */
- this.enableSvg = function(enableSvg) {
- if (angular.isDefined(enableSvg)) {
- svgEnabled = enableSvg;
- return this;
- } else {
- return svgEnabled;
- }
- };
}
function sanitizeText(chars) {
@@ -48429,9 +45581,18 @@ function sanitizeText(chars) {
// Regular Expressions for parsing tags and attributes
-var SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
+var START_TAG_REGEXP =
+ /^<((?:[a-zA-Z])[\w:-]*)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*(>?)/,
+ END_TAG_REGEXP = /^<\/\s*([\w:-]+)[^>]*>/,
+ ATTR_REGEXP = /([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g,
+ BEGIN_TAG_REGEXP = /^</,
+ BEGING_END_TAGE_REGEXP = /^<\//,
+ COMMENT_REGEXP = /<!--(.*?)-->/g,
+ DOCTYPE_REGEXP = /<!DOCTYPE([^>]*?)>/i,
+ CDATA_REGEXP = /<!\[CDATA\[(.*?)]]>/g,
+ SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
// Match everything outside of normal chars and " (quote character)
- NON_ALPHANUMERIC_REGEXP = /([^\#-~ |!])/g;
+ NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g;
// Good source of info about elements and attributes
@@ -48440,23 +45601,23 @@ var SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
// Safe Void Elements - HTML5
// http://dev.w3.org/html5/spec/Overview.html#void-elements
-var voidElements = toMap("area,br,col,hr,img,wbr");
+var voidElements = makeMap("area,br,col,hr,img,wbr");
// Elements that you can, intentionally, leave open (and which close themselves)
// http://dev.w3.org/html5/spec/Overview.html#optional-tags
-var optionalEndTagBlockElements = toMap("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"),
- optionalEndTagInlineElements = toMap("rp,rt"),
+var optionalEndTagBlockElements = makeMap("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"),
+ optionalEndTagInlineElements = makeMap("rp,rt"),
optionalEndTagElements = angular.extend({},
optionalEndTagInlineElements,
optionalEndTagBlockElements);
// Safe Block Elements - HTML5
-var blockElements = angular.extend({}, optionalEndTagBlockElements, toMap("address,article," +
+var blockElements = angular.extend({}, optionalEndTagBlockElements, makeMap("address,article," +
"aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5," +
- "h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,section,table,ul"));
+ "h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,script,section,table,ul"));
// Inline Elements - HTML5
-var inlineElements = angular.extend({}, optionalEndTagInlineElements, toMap("a,abbr,acronym,b," +
+var inlineElements = angular.extend({}, optionalEndTagInlineElements, makeMap("a,abbr,acronym,b," +
"bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s," +
"samp,small,span,strike,strong,sub,sup,time,tt,u,var"));
@@ -48464,23 +45625,24 @@ var inlineElements = angular.extend({}, optionalEndTagInlineElements, toMap("a,a
// https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Elements
// Note: the elements animate,animateColor,animateMotion,animateTransform,set are intentionally omitted.
// They can potentially allow for arbitrary javascript to be executed. See #11290
-var svgElements = toMap("circle,defs,desc,ellipse,font-face,font-face-name,font-face-src,g,glyph," +
+var svgElements = makeMap("circle,defs,desc,ellipse,font-face,font-face-name,font-face-src,g,glyph," +
"hkern,image,linearGradient,line,marker,metadata,missing-glyph,mpath,path,polygon,polyline," +
- "radialGradient,rect,stop,svg,switch,text,title,tspan");
+ "radialGradient,rect,stop,svg,switch,text,title,tspan,use");
-// Blocked Elements (will be stripped)
-var blockedElements = toMap("script,style");
+// Special Elements (can contain anything)
+var specialElements = makeMap("script,style");
var validElements = angular.extend({},
voidElements,
blockElements,
inlineElements,
- optionalEndTagElements);
+ optionalEndTagElements,
+ svgElements);
//Attributes that have href and hence need to be sanitized
-var uriAttrs = toMap("background,cite,href,longdesc,src,xlink:href");
+var uriAttrs = makeMap("background,cite,href,longdesc,src,usemap,xlink:href");
-var htmlAttrs = toMap('abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,' +
+var htmlAttrs = makeMap('abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,' +
'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,' +
'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,' +
'scope,scrolling,shape,size,span,start,summary,tabindex,target,title,type,' +
@@ -48488,7 +45650,7 @@ var htmlAttrs = toMap('abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacin
// SVG attributes (without "id" and "name" attributes)
// https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Attributes
-var svgAttrs = toMap('accent-height,accumulate,additive,alphabetic,arabic-form,ascent,' +
+var svgAttrs = makeMap('accent-height,accumulate,additive,alphabetic,arabic-form,ascent,' +
'baseProfile,bbox,begin,by,calcMode,cap-height,class,color,color-rendering,content,' +
'cx,cy,d,dx,dy,descent,display,dur,end,fill,fill-rule,font-family,font-size,font-stretch,' +
'font-style,font-variant,font-weight,from,fx,fy,g1,g2,glyph-name,gradientUnits,hanging,' +
@@ -48509,7 +45671,7 @@ var validAttrs = angular.extend({},
svgAttrs,
htmlAttrs);
-function toMap(str, lowercaseKeys) {
+function makeMap(str, lowercaseKeys) {
var obj = {}, items = str.split(','), i;
for (i = 0; i < items.length; i++) {
obj[lowercaseKeys ? angular.lowercase(items[i]) : items[i]] = true;
@@ -48517,32 +45679,11 @@ function toMap(str, lowercaseKeys) {
return obj;
}
-var inertBodyElement;
-(function(window) {
- var doc;
- if (window.document && window.document.implementation) {
- doc = window.document.implementation.createHTMLDocument("inert");
- } else {
- throw $sanitizeMinErr('noinert', "Can't create an inert html document");
- }
- var docElement = doc.documentElement || doc.getDocumentElement();
- var bodyElements = docElement.getElementsByTagName('body');
-
- // usually there should be only one body element in the document, but IE doesn't have any, so we need to create one
- if (bodyElements.length === 1) {
- inertBodyElement = bodyElements[0];
- } else {
- var html = doc.createElement('html');
- inertBodyElement = doc.createElement('body');
- html.appendChild(inertBodyElement);
- doc.appendChild(html);
- }
-})(window);
/**
* @example
* htmlParser(htmlString, {
- * start: function(tag, attrs) {},
+ * start: function(tag, attrs, unary) {},
* end: function(tag) {},
* chars: function(text) {},
* comment: function(text) {}
@@ -48552,74 +45693,169 @@ var inertBodyElement;
* @param {object} handler
*/
function htmlParser(html, handler) {
- if (html === null || html === undefined) {
- html = '';
- } else if (typeof html !== 'string') {
- html = '' + html;
+ if (typeof html !== 'string') {
+ if (html === null || typeof html === 'undefined') {
+ html = '';
+ } else {
+ html = '' + html;
+ }
}
- inertBodyElement.innerHTML = html;
+ var index, chars, match, stack = [], last = html, text;
+ stack.last = function() { return stack[stack.length - 1]; };
- //mXSS protection
- var mXSSAttempts = 5;
- do {
- if (mXSSAttempts === 0) {
- throw $sanitizeMinErr('uinput', "Failed to sanitize html because the input is unstable");
- }
- mXSSAttempts--;
+ while (html) {
+ text = '';
+ chars = true;
- // strip custom-namespaced attributes on IE<=11
- if (document.documentMode <= 11) {
- stripCustomNsAttrs(inertBodyElement);
- }
- html = inertBodyElement.innerHTML; //trigger mXSS
- inertBodyElement.innerHTML = html;
- } while (html !== inertBodyElement.innerHTML);
+ // Make sure we're not in a script or style element
+ if (!stack.last() || !specialElements[stack.last()]) {
- var node = inertBodyElement.firstChild;
- while (node) {
- switch (node.nodeType) {
- case 1: // ELEMENT_NODE
- handler.start(node.nodeName.toLowerCase(), attrToMap(node.attributes));
- break;
- case 3: // TEXT NODE
- handler.chars(node.textContent);
- break;
- }
+ // Comment
+ if (html.indexOf("<!--") === 0) {
+ // comments containing -- are not allowed unless they terminate the comment
+ index = html.indexOf("--", 4);
- var nextNode;
- if (!(nextNode = node.firstChild)) {
- if (node.nodeType == 1) {
- handler.end(node.nodeName.toLowerCase());
- }
- nextNode = node.nextSibling;
- if (!nextNode) {
- while (nextNode == null) {
- node = node.parentNode;
- if (node === inertBodyElement) break;
- nextNode = node.nextSibling;
- if (node.nodeType == 1) {
- handler.end(node.nodeName.toLowerCase());
+ if (index >= 0 && html.lastIndexOf("-->", index) === index) {
+ if (handler.comment) handler.comment(html.substring(4, index));
+ html = html.substring(index + 3);
+ chars = false;
+ }
+ // DOCTYPE
+ } else if (DOCTYPE_REGEXP.test(html)) {
+ match = html.match(DOCTYPE_REGEXP);
+
+ if (match) {
+ html = html.replace(match[0], '');
+ chars = false;
+ }
+ // end tag
+ } else if (BEGING_END_TAGE_REGEXP.test(html)) {
+ match = html.match(END_TAG_REGEXP);
+
+ if (match) {
+ html = html.substring(match[0].length);
+ match[0].replace(END_TAG_REGEXP, parseEndTag);
+ chars = false;
+ }
+
+ // start tag
+ } else if (BEGIN_TAG_REGEXP.test(html)) {
+ match = html.match(START_TAG_REGEXP);
+
+ if (match) {
+ // We only have a valid start-tag if there is a '>'.
+ if (match[4]) {
+ html = html.substring(match[0].length);
+ match[0].replace(START_TAG_REGEXP, parseStartTag);
}
+ chars = false;
+ } else {
+ // no ending tag found --- this piece should be encoded as an entity.
+ text += '<';
+ html = html.substring(1);
}
}
+
+ if (chars) {
+ index = html.indexOf("<");
+
+ text += index < 0 ? html : html.substring(0, index);
+ html = index < 0 ? "" : html.substring(index);
+
+ if (handler.chars) handler.chars(decodeEntities(text));
+ }
+
+ } else {
+ // IE versions 9 and 10 do not understand the regex '[^]', so using a workaround with [\W\w].
+ html = html.replace(new RegExp("([\\W\\w]*)<\\s*\\/\\s*" + stack.last() + "[^>]*>", 'i'),
+ function(all, text) {
+ text = text.replace(COMMENT_REGEXP, "$1").replace(CDATA_REGEXP, "$1");
+
+ if (handler.chars) handler.chars(decodeEntities(text));
+
+ return "";
+ });
+
+ parseEndTag("", stack.last());
+ }
+
+ if (html == last) {
+ throw $sanitizeMinErr('badparse', "The sanitizer was unable to parse the following block " +
+ "of html: {0}", html);
}
- node = nextNode;
+ last = html;
}
- while (node = inertBodyElement.firstChild) {
- inertBodyElement.removeChild(node);
+ // Clean up any remaining tags
+ parseEndTag();
+
+ function parseStartTag(tag, tagName, rest, unary) {
+ tagName = angular.lowercase(tagName);
+ if (blockElements[tagName]) {
+ while (stack.last() && inlineElements[stack.last()]) {
+ parseEndTag("", stack.last());
+ }
+ }
+
+ if (optionalEndTagElements[tagName] && stack.last() == tagName) {
+ parseEndTag("", tagName);
+ }
+
+ unary = voidElements[tagName] || !!unary;
+
+ if (!unary) {
+ stack.push(tagName);
+ }
+
+ var attrs = {};
+
+ rest.replace(ATTR_REGEXP,
+ function(match, name, doubleQuotedValue, singleQuotedValue, unquotedValue) {
+ var value = doubleQuotedValue
+ || singleQuotedValue
+ || unquotedValue
+ || '';
+
+ attrs[name] = decodeEntities(value);
+ });
+ if (handler.start) handler.start(tagName, attrs, unary);
}
-}
-function attrToMap(attrs) {
- var map = {};
- for (var i = 0, ii = attrs.length; i < ii; i++) {
- var attr = attrs[i];
- map[attr.name] = attr.value;
+ function parseEndTag(tag, tagName) {
+ var pos = 0, i;
+ tagName = angular.lowercase(tagName);
+ if (tagName) {
+ // Find the closest opened tag of the same type
+ for (pos = stack.length - 1; pos >= 0; pos--) {
+ if (stack[pos] == tagName) break;
+ }
+ }
+
+ if (pos >= 0) {
+ // Close all the open elements, up the stack
+ for (i = stack.length - 1; i >= pos; i--)
+ if (handler.end) handler.end(stack[i]);
+
+ // Remove the open elements from the stack
+ stack.length = pos;
+ }
}
- return map;
}
+var hiddenPre=document.createElement("pre");
+/**
+ * decodes all entities into regular string
+ * @param value
+ * @returns {string} A string with decoded entities.
+ */
+function decodeEntities(value) {
+ if (!value) { return ''; }
+
+ hiddenPre.innerHTML = value.replace(/</g,"&lt;");
+ // innerText depends on styling as it doesn't display hidden elements.
+ // Therefore, it's better to use textContent not to cause unnecessary reflows.
+ return hiddenPre.textContent;
+}
/**
* Escapes all potentially dangerous characters, so that the
@@ -48645,24 +45881,24 @@ function encodeEntities(value) {
/**
* create an HTML/XML writer which writes to buffer
- * @param {Array} buf use buf.join('') to get out sanitized html string
+ * @param {Array} buf use buf.jain('') to get out sanitized html string
* @returns {object} in the form of {
- * start: function(tag, attrs) {},
+ * start: function(tag, attrs, unary) {},
* end: function(tag) {},
* chars: function(text) {},
* comment: function(text) {}
* }
*/
function htmlSanitizeWriter(buf, uriValidator) {
- var ignoreCurrentElement = false;
+ var ignore = false;
var out = angular.bind(buf, buf.push);
return {
- start: function(tag, attrs) {
+ start: function(tag, attrs, unary) {
tag = angular.lowercase(tag);
- if (!ignoreCurrentElement && blockedElements[tag]) {
- ignoreCurrentElement = tag;
+ if (!ignore && specialElements[tag]) {
+ ignore = tag;
}
- if (!ignoreCurrentElement && validElements[tag] === true) {
+ if (!ignore && validElements[tag] === true) {
out('<');
out(tag);
angular.forEach(attrs, function(value, key) {
@@ -48677,63 +45913,29 @@ function htmlSanitizeWriter(buf, uriValidator) {
out('"');
}
});
- out('>');
+ out(unary ? '/>' : '>');
}
},
end: function(tag) {
- tag = angular.lowercase(tag);
- if (!ignoreCurrentElement && validElements[tag] === true && voidElements[tag] !== true) {
- out('</');
- out(tag);
- out('>');
- }
- if (tag == ignoreCurrentElement) {
- ignoreCurrentElement = false;
- }
- },
+ tag = angular.lowercase(tag);
+ if (!ignore && validElements[tag] === true) {
+ out('</');
+ out(tag);
+ out('>');
+ }
+ if (tag == ignore) {
+ ignore = false;
+ }
+ },
chars: function(chars) {
- if (!ignoreCurrentElement) {
- out(encodeEntities(chars));
+ if (!ignore) {
+ out(encodeEntities(chars));
+ }
}
- }
};
}
-/**
- * When IE9-11 comes across an unknown namespaced attribute e.g. 'xlink:foo' it adds 'xmlns:ns1' attribute to declare
- * ns1 namespace and prefixes the attribute with 'ns1' (e.g. 'ns1:xlink:foo'). This is undesirable since we don't want
- * to allow any of these custom attributes. This method strips them all.
- *
- * @param node Root element to process
- */
-function stripCustomNsAttrs(node) {
- if (node.nodeType === Node.ELEMENT_NODE) {
- var attrs = node.attributes;
- for (var i = 0, l = attrs.length; i < l; i++) {
- var attrNode = attrs[i];
- var attrName = attrNode.name.toLowerCase();
- if (attrName === 'xmlns:ns1' || attrName.indexOf('ns1:') === 0) {
- node.removeAttributeNode(attrNode);
- i--;
- l--;
- }
- }
- }
-
- var nextNode = node.firstChild;
- if (nextNode) {
- stripCustomNsAttrs(nextNode);
- }
-
- nextNode = node.nextSibling;
- if (nextNode) {
- stripCustomNsAttrs(nextNode);
- }
-}
-
-
-
// define ngSanitize module and register $sanitize service
angular.module('ngSanitize', []).provider('$sanitize', $SanitizeProvider);
@@ -48745,25 +45947,14 @@ angular.module('ngSanitize', []).provider('$sanitize', $SanitizeProvider);
* @kind function
*
* @description
- * Finds links in text input and turns them into html links. Supports `http/https/ftp/mailto` and
+ * Finds links in text input and turns them into html links. Supports http/https/ftp/mailto and
* plain email address links.
*
* Requires the {@link ngSanitize `ngSanitize`} module to be installed.
*
* @param {string} text Input text.
- * @param {string} target Window (`_blank|_self|_parent|_top`) or named frame to open links in.
- * @param {object|function(url)} [attributes] Add custom attributes to the link element.
- *
- * Can be one of:
- *
- * - `object`: A map of attributes
- * - `function`: Takes the url as a parameter and returns a map of attributes
- *
- * If the map of attributes contains a value for `target`, it overrides the value of
- * the target parameter.
- *
- *
- * @returns {string} Html-linkified and {@link $sanitize sanitized} text.
+ * @param {string} target Window (_blank|_self|_parent|_top) or named frame to open links in.
+ * @returns {string} Html-linkified text.
*
* @usage
<span ng-bind-html="linky_expression | linky"></span>
@@ -48771,13 +45962,25 @@ angular.module('ngSanitize', []).provider('$sanitize', $SanitizeProvider);
* @example
<example module="linkyExample" deps="angular-sanitize.js">
<file name="index.html">
+ <script>
+ angular.module('linkyExample', ['ngSanitize'])
+ .controller('ExampleController', ['$scope', function($scope) {
+ $scope.snippet =
+ 'Pretty text with some links:\n'+
+ 'http://angularjs.org/,\n'+
+ 'mailto:us@somewhere.org,\n'+
+ 'another@somewhere.org,\n'+
+ 'and one more: ftp://127.0.0.1/.';
+ $scope.snippetWithTarget = 'http://angularjs.org/';
+ }]);
+ </script>
<div ng-controller="ExampleController">
Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea>
<table>
<tr>
- <th>Filter</th>
- <th>Source</th>
- <th>Rendered</th>
+ <td>Filter</td>
+ <td>Source</td>
+ <td>Rendered</td>
</tr>
<tr id="linky-filter">
<td>linky filter</td>
@@ -48791,19 +45994,10 @@ angular.module('ngSanitize', []).provider('$sanitize', $SanitizeProvider);
<tr id="linky-target">
<td>linky target</td>
<td>
- <pre>&lt;div ng-bind-html="snippetWithSingleURL | linky:'_blank'"&gt;<br>&lt;/div&gt;</pre>
- </td>
- <td>
- <div ng-bind-html="snippetWithSingleURL | linky:'_blank'"></div>
- </td>
- </tr>
- <tr id="linky-custom-attributes">
- <td>linky custom attributes</td>
- <td>
- <pre>&lt;div ng-bind-html="snippetWithSingleURL | linky:'_self':{rel: 'nofollow'}"&gt;<br>&lt;/div&gt;</pre>
+ <pre>&lt;div ng-bind-html="snippetWithTarget | linky:'_blank'"&gt;<br>&lt;/div&gt;</pre>
</td>
<td>
- <div ng-bind-html="snippetWithSingleURL | linky:'_self':{rel: 'nofollow'}"></div>
+ <div ng-bind-html="snippetWithTarget | linky:'_blank'"></div>
</td>
</tr>
<tr id="escaped-html">
@@ -48813,18 +46007,6 @@ angular.module('ngSanitize', []).provider('$sanitize', $SanitizeProvider);
</tr>
</table>
</file>
- <file name="script.js">
- angular.module('linkyExample', ['ngSanitize'])
- .controller('ExampleController', ['$scope', function($scope) {
- $scope.snippet =
- 'Pretty text with some links:\n'+
- 'http://angularjs.org/,\n'+
- 'mailto:us@somewhere.org,\n'+
- 'another@somewhere.org,\n'+
- 'and one more: ftp://127.0.0.1/.';
- $scope.snippetWithSingleURL = 'http://angularjs.org/';
- }]);
- </file>
<file name="protractor.js" type="protractor">
it('should linkify the snippet with urls', function() {
expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()).
@@ -48852,32 +46034,20 @@ angular.module('ngSanitize', []).provider('$sanitize', $SanitizeProvider);
it('should work with the target property', function() {
expect(element(by.id('linky-target')).
- element(by.binding("snippetWithSingleURL | linky:'_blank'")).getText()).
+ element(by.binding("snippetWithTarget | linky:'_blank'")).getText()).
toBe('http://angularjs.org/');
expect(element(by.css('#linky-target a')).getAttribute('target')).toEqual('_blank');
});
-
- it('should optionally add custom attributes', function() {
- expect(element(by.id('linky-custom-attributes')).
- element(by.binding("snippetWithSingleURL | linky:'_self':{rel: 'nofollow'}")).getText()).
- toBe('http://angularjs.org/');
- expect(element(by.css('#linky-custom-attributes a')).getAttribute('rel')).toEqual('nofollow');
- });
</file>
</example>
*/
angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) {
var LINKY_URL_REGEXP =
- /((ftp|https?):\/\/|(www\.)|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"\u201d\u2019]/i,
+ /((ftp|https?):\/\/|(www\.)|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"”’]/i,
MAILTO_REGEXP = /^mailto:/i;
- var linkyMinErr = angular.$$minErr('linky');
- var isString = angular.isString;
-
- return function(text, target, attributes) {
- if (text == null || text === '') return text;
- if (!isString(text)) throw linkyMinErr('notstring', 'Expected string but received: {0}', text);
-
+ return function(text, target) {
+ if (!text) return text;
var match;
var raw = text;
var html = [];
@@ -48906,19 +46076,8 @@ angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) {
}
function addLink(url, text) {
- var key;
html.push('<a ');
- if (angular.isFunction(attributes)) {
- attributes = attributes(url);
- }
- if (angular.isObject(attributes)) {
- for (key in attributes) {
- html.push(key + '="' + attributes[key] + '" ');
- }
- } else {
- attributes = {};
- }
- if (angular.isDefined(target) && !('target' in attributes)) {
+ if (angular.isDefined(target)) {
html.push('target="',
target,
'" ');
@@ -53185,7 +50344,7 @@ angular.module('ui.router.state')
* Copyright 2015 Drifty Co.
* http://drifty.com/
*
- * Ionic, v1.3.3
+ * Ionic, v1.2.4-nightly-1917
* A powerful HTML5 mobile app framework.
* http://ionicframework.com/
*
@@ -54080,16 +51239,6 @@ function($rootScope, $state, $location, $window, $timeout, $ionicViewSwitcher, $
// it's back view would be better represented using the current view as its back view
tmp = getViewById(switchToView.backViewId);
if (tmp && switchToView.historyId !== tmp.historyId) {
- // the new view is being removed from it's old position in the history and being placed at the top,
- // so we need to update any views that reference it as a backview, otherwise there will be infinitely loops
- var viewIds = Object.keys(viewHistory.views);
- viewIds.forEach(function(viewId) {
- var view = viewHistory.views[viewId];
- if ((view.backViewId === switchToView.viewId) && (view.historyId !== switchToView.historyId)) {
- view.backViewId = null;
- }
- });
-
hist.stack[hist.cursor].backViewId = currentView.viewId;
}
@@ -54098,6 +51247,7 @@ function($rootScope, $state, $location, $window, $timeout, $ionicViewSwitcher, $
// create an element from the viewLocals template
ele = $ionicViewSwitcher.createViewEle(viewLocals);
if (this.isAbstractEle(ele, viewLocals)) {
+ void 0;
return {
action: 'abstractView',
direction: DIRECTION_NONE,
@@ -54137,7 +51287,6 @@ function($rootScope, $state, $location, $window, $timeout, $ionicViewSwitcher, $
direction = DIRECTION_FORWARD;
} else if (currentView.historyId !== hist.historyId) {
- // DB: this is a new view in a different tab
direction = DIRECTION_ENTER;
tmp = getHistoryById(currentView.historyId);
@@ -54219,6 +51368,8 @@ function($rootScope, $state, $location, $window, $timeout, $ionicViewSwitcher, $
}
}
+ void 0;
+
hist.cursor = viewHistory.currentView.index;
return {
@@ -54404,44 +51555,13 @@ function($rootScope, $state, $location, $window, $timeout, $ionicViewSwitcher, $
if (clearStateIds.length) {
$timeout(function() {
self.clearCache(clearStateIds);
- }, 300);
+ }, 600);
}
}
viewHistory.backView && viewHistory.backView.go();
},
- /**
- * @ngdoc method
- * @name $ionicHistory#removeBackView
- * @description Remove the previous view from the history completely, including the
- * cached element and scope (if they exist).
- */
- removeBackView: function() {
- var self = this;
- var currentHistory = viewHistory.histories[this.currentHistoryId()];
- var currentCursor = currentHistory.cursor;
-
- var currentView = currentHistory.stack[currentCursor];
- var backView = currentHistory.stack[currentCursor - 1];
- var replacementView = currentHistory.stack[currentCursor - 2];
-
- // fail if we dont have enough views in the history
- if (!backView || !replacementView) {
- return;
- }
-
- // remove the old backView and the cached element/scope
- currentHistory.stack.splice(currentCursor - 1, 1);
- self.clearCache([backView.viewId]);
- // make the replacementView and currentView point to each other (bypass the old backView)
- currentView.backViewId = replacementView.viewId;
- currentView.index = currentView.index - 1;
- replacementView.forwardViewId = currentView.viewId;
- // update the cursor and set new backView
- viewHistory.backView = replacementView;
- currentHistory.currentCursor += -1;
- },
enabledBack: function(view) {
var backView = getBackView(view);
@@ -54608,10 +51728,6 @@ function($rootScope, $state, $location, $window, $timeout, $ionicViewSwitcher, $
if (ele && ele.attr('can-swipe-back') === 'false') {
return false;
}
- var eleChild = ele.find('ion-view');
- if (eleChild && eleChild.attr('can-swipe-back') === 'false') {
- return false;
- }
return true;
}
@@ -54721,7 +51837,7 @@ function($rootScope, $state, $location, $document, $ionicPlatform, $ionicHistory
* $ionicConfigProvider.views.maxCache(10);
* ```
*
- * Additionally, each platform can have its own config within the `$ionicConfigProvider.platform`
+ * Additionally, each platform can have it's own config within the `$ionicConfigProvider.platform`
* property. The config below would only apply to Android devices.
*
* ```js
@@ -54785,16 +51901,6 @@ function($rootScope, $state, $location, $document, $ionicPlatform, $ionicHistory
* @returns {boolean}
*/
- /**
- * @ngdoc method
- * @name $ionicConfigProvider#views.swipeBackEnabled
- * @description By default on iOS devices, swipe to go back functionality is enabled by default.
- * This method can be used to disable it globally, or on a per-view basis.
- * Note: This functionality is only supported on iOS.
- * @param {boolean} value
- * @returns {boolean}
- */
-
/**
* @ngdoc method
* @name $ionicConfigProvider#scrolling.jsScrolling
@@ -55194,28 +52300,27 @@ IonicModule
provider.transitions.views.android = function(enteringEle, leavingEle, direction, shouldAnimate) {
shouldAnimate = shouldAnimate && (direction == 'forward' || direction == 'back');
- function setStyles(ele, x, opacity) {
+ function setStyles(ele, x) {
var css = {};
css[ionic.CSS.TRANSITION_DURATION] = d.shouldAnimate ? '' : 0;
css[ionic.CSS.TRANSFORM] = 'translate3d(' + x + '%,0,0)';
- css.opacity = opacity;
ionic.DomUtil.cachedStyles(ele, css);
}
var d = {
run: function(step) {
if (direction == 'forward') {
- setStyles(enteringEle, (1 - step) * 99, 1); // starting at 98% prevents a flicker
- setStyles(leavingEle, step * -100, 1);
+ setStyles(enteringEle, (1 - step) * 99); // starting at 98% prevents a flicker
+ setStyles(leavingEle, step * -100);
} else if (direction == 'back') {
- setStyles(enteringEle, (1 - step) * -100, 1);
- setStyles(leavingEle, step * 100, 1);
+ setStyles(enteringEle, (1 - step) * -100);
+ setStyles(leavingEle, step * 100);
} else {
// swap, enter, exit
- setStyles(enteringEle, 0, 1);
- setStyles(leavingEle, 0, 0);
+ setStyles(enteringEle, 0);
+ setStyles(leavingEle, 0);
}
},
shouldAnimate: shouldAnimate
@@ -55373,6 +52478,10 @@ var LOADING_TPL =
'</div>' +
'</div>';
+var LOADING_HIDE_DEPRECATED = '$ionicLoading instance.hide() has been deprecated. Use $ionicLoading.hide().';
+var LOADING_SHOW_DEPRECATED = '$ionicLoading instance.show() has been deprecated. Use $ionicLoading.show().';
+var LOADING_SET_DEPRECATED = '$ionicLoading instance.setContent() has been deprecated. Use $ionicLoading.show({ template: \'my content\' }).';
+
/**
* @ngdoc service
* @name $ionicLoading
@@ -55387,16 +52496,11 @@ var LOADING_TPL =
* .controller('LoadingCtrl', function($scope, $ionicLoading) {
* $scope.show = function() {
* $ionicLoading.show({
- * template: 'Loading...',
- * duration: 3000
- * }).then(function(){
- * console.log("The loading indicator is now displayed");
+ * template: 'Loading...'
* });
* };
* $scope.hide = function(){
- * $ionicLoading.hide().then(function(){
- * console.log("The loading indicator is now hidden");
- * });
+ * $ionicLoading.hide();
* };
* });
* ```
@@ -55416,10 +52520,7 @@ var LOADING_TPL =
* });
* app.controller('AppCtrl', function($scope, $ionicLoading) {
* $scope.showLoading = function() {
- * //options default to values in $ionicLoadingConfig
- * $ionicLoading.show().then(function(){
- * console.log("The loading indicator is now displayed");
- * });
+ * $ionicLoading.show(); //options default to values in $ionicLoadingConfig
* };
* });
* ```
@@ -55454,8 +52555,9 @@ function($ionicLoadingConfig, $ionicBody, $ionicTemplateLoader, $ionicBackdrop,
* @ngdoc method
* @name $ionicLoading#show
* @description Shows a loading indicator. If the indicator is already shown,
- * it will set the options given and keep the indicator shown.
- * @returns {promise} A promise which is resolved when the loading indicator is presented.
+ * it will set the options given and keep the indicator shown. Note: While this
+ * function still returns an $ionicLoading instance for backwards compatiblity,
+ * its use has been deprecated.
* @param {object} opts The options for the loading indicator. Available properties:
* - `{string=}` `template` The html content of the indicator.
* - `{string=}` `templateUrl` The url of an html template to load as the content of the indicator.
@@ -55472,7 +52574,6 @@ function($ionicLoadingConfig, $ionicBody, $ionicTemplateLoader, $ionicBackdrop,
* @ngdoc method
* @name $ionicLoading#hide
* @description Hides the loading indicator, if shown.
- * @returns {promise} A promise which is resolved when the loading indicator is hidden.
*/
hide: hideLoader,
/**
@@ -55570,8 +52671,6 @@ function($ionicLoadingConfig, $ionicBody, $ionicTemplateLoader, $ionicBackdrop,
function showLoader(options) {
options = extend({}, $ionicLoadingConfig || {}, options || {});
- // use a default delay of 100 to avoid some issues reported on github
- // https://github.com/driftyco/ionic/issues/3717
var delay = options.delay || options.showDelay || 0;
deregisterStateListener1();
@@ -55584,17 +52683,34 @@ function($ionicLoadingConfig, $ionicBody, $ionicTemplateLoader, $ionicBackdrop,
//If loading.show() was called previously, cancel it and show with our new options
$timeout.cancel(loadingShowDelay);
loadingShowDelay = $timeout(noop, delay);
- return loadingShowDelay.then(getLoader).then(function(loader) {
+ loadingShowDelay.then(getLoader).then(function(loader) {
return loader.show(options);
});
+
+ return {
+ hide: function deprecatedHide() {
+ $log.error(LOADING_HIDE_DEPRECATED);
+ return hideLoader.apply(this, arguments);
+ },
+ show: function deprecatedShow() {
+ $log.error(LOADING_SHOW_DEPRECATED);
+ return showLoader.apply(this, arguments);
+ },
+ setContent: function deprecatedSetContent(content) {
+ $log.error(LOADING_SET_DEPRECATED);
+ return getLoader().then(function(loader) {
+ loader.show({ template: content });
+ });
+ }
+ };
}
function hideLoader() {
deregisterStateListener1();
deregisterStateListener2();
$timeout.cancel(loadingShowDelay);
- return getLoader().then(function(loader) {
- return loader.hide();
+ getLoader().then(function(loader) {
+ loader.hide();
});
}
}]);
@@ -55603,7 +52719,6 @@ function($ionicLoadingConfig, $ionicBody, $ionicTemplateLoader, $ionicBackdrop,
* @ngdoc service
* @name $ionicModal
* @module ionic
- * @codepen gblny
* @description
*
* Related: {@link ionic.controller:ionicModal ionicModal controller}.
@@ -55649,7 +52764,7 @@ function($ionicLoadingConfig, $ionicBody, $ionicTemplateLoader, $ionicBackdrop,
* $scope.closeModal = function() {
* $scope.modal.hide();
* };
- * // Cleanup the modal when we're done with it!
+ * //Cleanup the modal when we're done with it!
* $scope.$on('$destroy', function() {
* $scope.modal.remove();
* });
@@ -55783,7 +52898,6 @@ function($rootScope, $ionicBody, $compile, $timeout, $ionicPlatform, $ionicTempl
self.el.classList.add('active');
self.scope.$broadcast('$ionicHeader.align');
self.scope.$broadcast('$ionicFooter.align');
- self.scope.$broadcast('$ionic.modalPresented');
}, 20);
return $timeout(function() {
@@ -55826,8 +52940,6 @@ function($rootScope, $ionicBody, $compile, $timeout, $ionicPlatform, $ionicTempl
if (self._isShown) return;
modalEl.addClass('ng-leave-active')
.removeClass('ng-enter ng-enter-active active');
-
- self.scope.$broadcast('$ionic.modalRemoved');
}, 20, false);
self.$el.off('click');
@@ -55843,9 +52955,7 @@ function($rootScope, $ionicBody, $compile, $timeout, $ionicPlatform, $ionicTempl
}
return $timeout(function() {
- if (!modalStack.length) {
- $ionicBody.removeClass(self.viewType + '-open');
- }
+ $ionicBody.removeClass(self.viewType + '-open');
self.el.classList.add('hide');
}, self.hideDelay || 320);
},
@@ -55857,24 +52967,10 @@ function($rootScope, $ionicBody, $compile, $timeout, $ionicPlatform, $ionicTempl
* @returns {promise} A promise which is resolved when the modal is finished animating out.
*/
remove: function() {
- var self = this,
- deferred, promise;
+ var self = this;
self.scope.$parent && self.scope.$parent.$broadcast(self.viewType + '.removed', self);
- // Only hide modal, when it is actually shown!
- // The hide function shows a click-block-div for a split second, because on iOS,
- // clicks will sometimes bleed through/ghost click on underlying elements.
- // However, this will make the app unresponsive for short amount of time.
- // We don't want that, if the modal window is already hidden.
- if (self._isShown) {
- promise = self.hide();
- } else {
- deferred = $$q.defer();
- deferred.resolve();
- promise = deferred.promise;
- }
-
- return promise.then(function() {
+ return self.hide().then(function() {
self.scope.$destroy();
self.$el.remove();
});
@@ -56200,8 +53296,8 @@ IonicModule
* @description
* Add Cordova event listeners, such as `pause`, `resume`, `volumedownbutton`, `batterylow`,
* `offline`, etc. More information about available event types can be found in
- * [Cordova's event documentation](https://cordova.apache.org/docs/en/latest/cordova/events/events.html).
- * @param {string} type Cordova [event type](https://cordova.apache.org/docs/en/latest/cordova/events/events.html).
+ * [Cordova's event documentation](https://cordova.apache.org/docs/en/edge/cordova_events_events.md.html#Events).
+ * @param {string} type Cordova [event type](https://cordova.apache.org/docs/en/edge/cordova_events_events.md.html#Events).
* @param {function} callback Called when the Cordova event is fired.
* @returns {function} Returns a deregistration function to remove the event listener.
*/
@@ -56229,11 +53325,6 @@ IonicModule
var q = $q.defer();
ionic.Platform.ready(function() {
-
- window.addEventListener('statusTap', function() {
- $ionicScrollDelegate.scrollTop(true);
- });
-
q.resolve();
cb && cb();
});
@@ -56242,6 +53333,10 @@ IonicModule
}
};
+ window.addEventListener('statusTap', function() {
+ $ionicScrollDelegate.scrollTop(true);
+ });
+
return self;
}]
};
@@ -56312,7 +53407,7 @@ IonicModule
* $scope.$on('$destroy', function() {
* $scope.popover.remove();
* });
- * // Execute action on hidden popover
+ * // Execute action on hide popover
* $scope.$on('popover.hidden', function() {
* // Execute action
* });
@@ -57189,7 +54284,6 @@ IonicModule
* @returns {object} The scroll position of this view, with the following properties:
* - `{number}` `left` The distance the user has scrolled from the left (starts at 0).
* - `{number}` `top` The distance the user has scrolled from the top (starts at 0).
- * - `{number}` `zoom` The current zoom level.
*/
'getScrollPosition',
/**
@@ -58067,67 +55161,32 @@ function($timeout, $document, $q, $ionicClickBlock, $ionicConfig, $ionicNavBarDe
},
- emit: function(step, enteringData, leavingData) {
- var enteringScope = getScopeForElement(enteringEle, enteringData);
- var leavingScope = getScopeForElement(leavingEle, leavingData);
-
- var prefixesAreEqual;
+ emit: function(step, enteringData, leavingData) {
+ var enteringScope = enteringEle.scope(),
+ leavingScope = leavingEle && leavingEle.scope();
- if ( !enteringData.viewId || enteringData.abstractView ) {
- // it's an abstract view, so treat it accordingly
+ if (step == 'after') {
+ if (enteringScope) {
+ enteringScope.$emit('$ionicView.enter', enteringData);
+ }
- // we only get access to the leaving scope once in the transition,
- // so dispatch all events right away if it exists
- if ( leavingScope ) {
- leavingScope.$emit('$ionicView.beforeLeave', leavingData);
+ if (leavingScope) {
leavingScope.$emit('$ionicView.leave', leavingData);
- leavingScope.$emit('$ionicView.afterLeave', leavingData);
- leavingScope.$broadcast('$ionicParentView.beforeLeave', leavingData);
- leavingScope.$broadcast('$ionicParentView.leave', leavingData);
- leavingScope.$broadcast('$ionicParentView.afterLeave', leavingData);
+
+ } else if (enteringScope && leavingData && leavingData.viewId) {
+ enteringScope.$emit('$ionicNavView.leave', leavingData);
}
}
- else {
- // it's a regular view, so do the normal process
- if (step == 'after') {
- if (enteringScope) {
- enteringScope.$emit('$ionicView.enter', enteringData);
- enteringScope.$broadcast('$ionicParentView.enter', enteringData);
- }
- if (leavingScope) {
- leavingScope.$emit('$ionicView.leave', leavingData);
- leavingScope.$broadcast('$ionicParentView.leave', leavingData);
- }
- else if (enteringScope && leavingData && leavingData.viewId && enteringData.stateName !== leavingData.stateName) {
- // we only want to dispatch this when we are doing a single-tier
- // state change such as changing a tab, so compare the state
- // for the same state-prefix but different suffix
- prefixesAreEqual = compareStatePrefixes(enteringData.stateName, leavingData.stateName);
- if ( prefixesAreEqual ) {
- enteringScope.$emit('$ionicNavView.leave', leavingData);
- }
- }
- }
+ if (enteringScope) {
+ enteringScope.$emit('$ionicView.' + step + 'Enter', enteringData);
+ }
- if (enteringScope) {
- enteringScope.$emit('$ionicView.' + step + 'Enter', enteringData);
- enteringScope.$broadcast('$ionicParentView.' + step + 'Enter', enteringData);
- }
+ if (leavingScope) {
+ leavingScope.$emit('$ionicView.' + step + 'Leave', leavingData);
- if (leavingScope) {
- leavingScope.$emit('$ionicView.' + step + 'Leave', leavingData);
- leavingScope.$broadcast('$ionicParentView.' + step + 'Leave', leavingData);
-
- } else if (enteringScope && leavingData && leavingData.viewId && enteringData.stateName !== leavingData.stateName) {
- // we only want to dispatch this when we are doing a single-tier
- // state change such as changing a tab, so compare the state
- // for the same state-prefix but different suffix
- prefixesAreEqual = compareStatePrefixes(enteringData.stateName, leavingData.stateName);
- if ( prefixesAreEqual ) {
- enteringScope.$emit('$ionicNavView.' + step + 'Leave', leavingData);
- }
- }
+ } else if (enteringScope && leavingData && leavingData.viewId) {
+ enteringScope.$emit('$ionicNavView.' + step + 'Leave', leavingData);
}
},
@@ -58211,15 +55270,6 @@ function($timeout, $document, $q, $ionicClickBlock, $ionicConfig, $ionicNavBarDe
containerEle.innerHTML = viewLocals.$template;
if (containerEle.children.length === 1) {
containerEle.children[0].classList.add('pane');
- if ( viewLocals.$$state && viewLocals.$$state.self && viewLocals.$$state.self['abstract'] ) {
- angular.element(containerEle.children[0]).attr("abstract", "true");
- }
- else {
- if ( viewLocals.$$state && viewLocals.$$state.self ) {
- angular.element(containerEle.children[0]).attr("state", viewLocals.$$state.self.name);
- }
-
- }
return jqLite(containerEle.children[0]);
}
}
@@ -58304,69 +55354,6 @@ function($timeout, $document, $q, $ionicClickBlock, $ionicConfig, $ionicNavBarDe
}
}
- function compareStatePrefixes(enteringStateName, exitingStateName) {
- var enteringStateSuffixIndex = enteringStateName.lastIndexOf('.');
- var exitingStateSuffixIndex = exitingStateName.lastIndexOf('.');
-
- // if either of the prefixes are empty, just return false
- if ( enteringStateSuffixIndex < 0 || exitingStateSuffixIndex < 0 ) {
- return false;
- }
-
- var enteringPrefix = enteringStateName.substring(0, enteringStateSuffixIndex);
- var exitingPrefix = exitingStateName.substring(0, exitingStateSuffixIndex);
-
- return enteringPrefix === exitingPrefix;
- }
-
- function getScopeForElement(element, stateData) {
- if ( !element ) {
- return null;
- }
- // check if it's abstract
- var attributeValue = angular.element(element).attr("abstract");
- var stateValue = angular.element(element).attr("state");
-
- if ( attributeValue !== "true" ) {
- // it's not an abstract view, so make sure the element
- // matches the state. Due to abstract view weirdness,
- // sometimes it doesn't. If it doesn't, don't dispatch events
- // so leave the scope undefined
- if ( stateValue === stateData.stateName ) {
- return angular.element(element).scope();
- }
- return null;
- }
- else {
- // it is an abstract element, so look for element with the "state" attributeValue
- // set to the name of the stateData state
- var elements = aggregateNavViewChildren(element);
- for ( var i = 0; i < elements.length; i++ ) {
- var state = angular.element(elements[i]).attr("state");
- if ( state === stateData.stateName ) {
- stateData.abstractView = true;
- return angular.element(elements[i]).scope();
- }
- }
- // we didn't find a match, so return null
- return null;
- }
- }
-
- function aggregateNavViewChildren(element) {
- var aggregate = [];
- var navViews = angular.element(element).find("ion-nav-view");
- for ( var i = 0; i < navViews.length; i++ ) {
- var children = angular.element(navViews[i]).children();
- var childrenAggregated = [];
- for ( var j = 0; j < children.length; j++ ) {
- childrenAggregated = childrenAggregated.concat(children[j]);
- }
- aggregate = aggregate.concat(childrenAggregated);
- }
- return aggregate;
- }
-
}]);
/**
@@ -58597,20 +55584,10 @@ function($scope, $element, $attrs, $q, $ionicConfig, $ionicHistory) {
self.titleTextWidth = function() {
- var element = getEle(TITLE);
- if ( element ) {
- // If the element has a nav-bar-title, use that instead
- // to calculate the width of the title
- var children = angular.element(element).children();
- for ( var i = 0; i < children.length; i++ ) {
- if ( angular.element(children[i]).hasClass('nav-bar-title') ) {
- element = children[i];
- break;
- }
- }
+ if (!titleTextWidth) {
+ var bounds = ionic.DomUtil.getTextBounds(getEle(TITLE));
+ titleTextWidth = Math.min(bounds && bounds.width || 30);
}
- var bounds = ionic.DomUtil.getTextBounds(element);
- titleTextWidth = Math.min(bounds && bounds.width || 30);
return titleTextWidth;
};
@@ -59648,8 +56625,7 @@ IonicModule
'$ionicViewSwitcher',
'$ionicConfig',
'$ionicScrollDelegate',
- '$ionicSideMenuDelegate',
-function($scope, $element, $attrs, $compile, $controller, $ionicNavBarDelegate, $ionicNavViewDelegate, $ionicHistory, $ionicViewSwitcher, $ionicConfig, $ionicScrollDelegate, $ionicSideMenuDelegate) {
+function($scope, $element, $attrs, $compile, $controller, $ionicNavBarDelegate, $ionicNavViewDelegate, $ionicHistory, $ionicViewSwitcher, $ionicConfig, $ionicScrollDelegate) {
var DATA_ELE_IDENTIFIER = '$eleId';
var DATA_DESTROY_ELE = '$destroyEle';
@@ -59700,8 +56676,8 @@ function($scope, $element, $attrs, $compile, $controller, $ionicNavBarDelegate,
$scope.$on('$ionicTabs.leave', onTabsLeave);
ionic.Platform.ready(function() {
- if ( ionic.Platform.isWebView() && ionic.Platform.isIOS() ) {
- self.initSwipeBack();
+ if (ionic.Platform.isWebView() && $ionicConfig.views.swipeBackEnabled()) {
+ self.initSwipeBack();
}
});
@@ -59843,7 +56819,6 @@ function($scope, $element, $attrs, $compile, $controller, $ionicNavBarDelegate,
if (navViewAttr(viewElement) == VIEW_STATUS_ACTIVE) {
viewScope = viewElement.scope();
viewScope && viewScope.$emit(ev.name.replace('Tabs', 'View'), data);
- viewScope && viewScope.$broadcast(ev.name.replace('Tabs', 'ParentView'), data);
break;
}
}
@@ -59992,8 +56967,7 @@ function($scope, $element, $attrs, $compile, $controller, $ionicNavBarDelegate,
var cancelData = {};
function onDragStart(ev) {
- if (!isPrimary || !$ionicConfig.views.swipeBackEnabled() || $ionicSideMenuDelegate.isOpenRight() ) return;
-
+ if (!isPrimary) return;
startDragX = getDragX(ev);
if (startDragX > swipeBackHitWidth) return;
@@ -60319,7 +57293,7 @@ IonicModule
}
function overscroll(val) {
- scrollChild.style[ionic.CSS.TRANSFORM] = 'translate3d(0px, ' + val + 'px, 0px)';
+ scrollChild.style[ionic.CSS.TRANSFORM] = 'translateY(' + val + 'px)';
lastOverscroll = val;
}
@@ -60455,17 +57429,13 @@ IonicModule
};
function destroy() {
- if ( scrollChild ) {
- ionic.off(touchStartEvent, handleTouchstart, scrollChild);
- ionic.off(touchMoveEvent, handleTouchmove, scrollChild);
- ionic.off(touchEndEvent, handleTouchend, scrollChild);
- ionic.off('mousedown', handleMousedown, scrollChild);
- ionic.off('mousemove', handleTouchmove, scrollChild);
- ionic.off('mouseup', handleTouchend, scrollChild);
- }
- if ( scrollParent ) {
- ionic.off('scroll', handleScroll, scrollParent);
- }
+ ionic.off(touchStartEvent, handleTouchstart, scrollChild);
+ ionic.off(touchMoveEvent, handleTouchmove, scrollChild);
+ ionic.off(touchEndEvent, handleTouchend, scrollChild);
+ ionic.off('mousedown', handleMousedown, scrollChild);
+ ionic.off('mousemove', handleTouchmove, scrollChild);
+ ionic.off('mouseup', handleTouchend, scrollChild);
+ ionic.off('scroll', handleScroll, scrollParent);
scrollParent = null;
scrollChild = null;
}
@@ -60617,17 +57587,8 @@ function($scope,
deregisterInstance();
scrollView && scrollView.__cleanup && scrollView.__cleanup();
angular.element($window).off('resize', resize);
- if ( $element ) {
- $element.off('scroll', scrollFunc);
- }
- if ( self._scrollViewOptions ) {
- self._scrollViewOptions.el = null;
- }
- if ( scrollViewOptions ) {
- scrollViewOptions.el = null;
- }
-
- scrollView = self.scrollView = scrollViewOptions = self._scrollViewOptions = element = self.$element = $element = null;
+ $element.off('scroll', scrollFunc);
+ scrollView = self.scrollView = scrollViewOptions = self._scrollViewOptions = scrollViewOptions.el = self._scrollViewOptions.el = $element = self.$element = element = null;
});
$timeout(function() {
@@ -60955,15 +57916,20 @@ function($scope, $attrs, $ionicSideMenuDelegate, $ionicPlatform, $ionicBody, $io
self.content.setTranslateX(amount);
- leftShowing = amount > 0;
- rightShowing = amount < 0;
+ if (amount >= 0) {
+ leftShowing = true;
+ rightShowing = false;
- if (amount > 0) {
- // Push the z-index of the right menu down
- self.right && self.right.pushDown && self.right.pushDown();
- // Bring the z-index of the left menu up
- self.left && self.left.bringUp && self.left.bringUp();
+ if (amount > 0) {
+ // Push the z-index of the right menu down
+ self.right && self.right.pushDown && self.right.pushDown();
+ // Bring the z-index of the left menu up
+ self.left && self.left.bringUp && self.left.bringUp();
+ }
} else {
+ rightShowing = true;
+ leftShowing = false;
+
// Bring the z-index of the right menu up
self.right && self.right.bringUp && self.right.bringUp();
// Push the z-index of the left menu down
@@ -61532,8 +58498,7 @@ function($scope, $attrs, $ionicSideMenuDelegate, $ionicPlatform, $ionicBody, $io
var animations = {
android: function(ele) {
- // Note that this is called as a function, not a constructor.
- var self = {};
+ var self = this;
this.stop = false;
@@ -61769,8 +58734,6 @@ function($scope, $element, $ionicHistory) {
uiSref: tab.uiSref
});
}
-
- $scope.$broadcast("tabSelected", { selectedTab: tab, selectedTabIndex: tabIndex});
}
};
@@ -63254,17 +60217,13 @@ IonicModule.directive('exposeAsideWhen', ['$window', function($window) {
require: '^ionSideMenus',
link: function($scope, $element, $attr, sideMenuCtrl) {
- var prevInnerWidth = $window.innerWidth;
- var prevInnerHeight = $window.innerHeight;
-
- ionic.on('resize', function() {
- if (prevInnerWidth === $window.innerWidth && prevInnerHeight === $window.innerHeight) {
- return;
- }
- prevInnerWidth = $window.innerWidth;
- prevInnerHeight = $window.innerHeight;
+ // Setup a match media query listener that triggers a ui change only when a change
+ // in media matching status occurs
+ var mq = $attr.exposeAsideWhen == 'large' ? '(min-width:768px)' : $attr.exposeAsideWhen;
+ var mql = $window.matchMedia(mq);
+ mql.addListener(function() {
onResize();
- }, $window);
+ });
function checkAsideExpose() {
var mq = $attr.exposeAsideWhen == 'large' ? '(min-width:768px)' : $attr.exposeAsideWhen;
@@ -63626,7 +60585,7 @@ IonicModule
* <button class="button">Right Button</button>
* </div>
* </ion-header-bar>
- * <ion-content class="has-header">
+ * <ion-content>
* Some content!
* </ion-content>
* ```
@@ -63653,7 +60612,7 @@ IonicModule
*
* @usage
* ```html
- * <ion-content class="has-footer">
+ * <ion-content>
* Some content!
* </ion-content>
* <ion-footer-bar align-title="left" class="bar-assertive">
@@ -63791,7 +60750,7 @@ function headerFooterBarDirective(isHeader) {
* reach to trigger the on-infinite expression. Default: 1%.
* @param {string=} spinner The {@link ionic.directive:ionSpinner} to show while loading. The SVG
* {@link ionic.directive:ionSpinner} is now the default, replacing rotating font icons.
- * @param {string=} icon The icon to show while loading. Default: 'ion-load-d'. This is depreciated
+ * @param {string=} icon The icon to show while loading. Default: 'ion-load-d'. This is depreicated
* in favor of the SVG {@link ionic.directive:ionSpinner}.
* @param {boolean=} immediate-check Whether to check the infinite scroll bounds immediately on load.
*
@@ -63893,7 +60852,7 @@ IonicModule
* <ion-list>
* <ion-input>
* <input type="text" placeholder="First Name">
-* </ion-input>
+* <ion-input>
*
* <ion-input>
* <ion-label>Username</ion-label>
@@ -64249,9 +61208,9 @@ IonicModule.directive('ionOptionButton', [function() {
return function($scope, $element, $attr, itemCtrl) {
if (!itemCtrl.optionsContainer) {
itemCtrl.optionsContainer = jqLite(ITEM_TPL_OPTION_BUTTONS);
- itemCtrl.$element.prepend(itemCtrl.optionsContainer);
+ itemCtrl.$element.append(itemCtrl.optionsContainer);
}
- itemCtrl.optionsContainer.prepend($element);
+ itemCtrl.optionsContainer.append($element);
itemCtrl.$element.addClass('item-right-editable');
@@ -64961,6 +61920,7 @@ IonicModule
* @param {boolean=} no-tap-scroll By default, the navbar will scroll the content
* to the top when tapped. Set no-tap-scroll to true to disable this behavior.
*
+ * </table><br/>
*/
IonicModule
.directive('ionNavBar', function() {
@@ -65335,9 +62295,6 @@ IonicModule
* <iframe width="560" height="315" src="//www.youtube.com/embed/dqJRoh8MnBo"
* frameborder="0" allowfullscreen></iframe>
*
- * Note: We do not recommend using [resolve](https://github.com/angular-ui/ui-router/wiki#resolve)
- * of AngularUI Router. The recommended approach is to execute any logic needed before beginning the state transition.
- *
* @param {string=} name A view name. The name should be unique amongst the other views in the
* same state. You can have views of the same name that live in different states. For more
* information, see ui-router's
@@ -65634,7 +62591,7 @@ IonicModule
* is now the default, replacing rotating font icons. Set to `none` to disable both the
* spinner and the icon.
* @param {string=} refreshing-icon The font icon to display after user lets go of the
- * refresher. This is deprecated in favor of the SVG {@link ionic.directive:ionSpinner}.
+ * refresher. This is depreicated in favor of the SVG {@link ionic.directive:ionSpinner}.
* @param {boolean=} disable-pulling-rotation Disables the rotation animation of the pulling
* icon when it reaches its activated threshold. To be used with a custom `pulling-icon`.
*
@@ -65679,9 +62636,7 @@ IonicModule
$scope.$on('scroll.refreshComplete', function() {
$scope.$evalAsync(function() {
- if(scrollCtrl.scrollView){
- scrollCtrl.scrollView.finishPullToRefresh();
- }
+ scrollCtrl.scrollView.finishPullToRefresh();
});
});
}
@@ -65723,7 +62678,6 @@ IonicModule
* @param {boolean=} paging Whether to scroll with paging.
* @param {expression=} on-refresh Called on pull-to-refresh, triggered by an {@link ionic.directive:ionRefresher}.
* @param {expression=} on-scroll Called whenever the user scrolls.
- * @param {expression=} on-scroll-complete Called whenever the scrolling paging is completed.
* @param {boolean=} scrollbar-x Whether to show the horizontal scrollbar. Default true.
* @param {boolean=} scrollbar-y Whether to show the vertical scrollbar. Default true.
* @param {boolean=} zooming Whether to support pinch-to-zoom
@@ -65759,7 +62713,6 @@ function($timeout, $controller, $ionicBind, $ionicConfig) {
direction: '@',
paging: '@',
$onScroll: '&onScroll',
- $onScrollComplete: '&onScrollComplete',
scroll: '@',
scrollbarX: '@',
scrollbarY: '@',
@@ -65801,8 +62754,7 @@ function($timeout, $controller, $ionicBind, $ionicConfig) {
maxZoom: $scope.$eval($scope.maxZoom) || 3,
minZoom: $scope.$eval($scope.minZoom) || 0.5,
preventDefault: true,
- nativeScrolling: nativeScrolling,
- scrollingComplete: onScrollComplete
+ nativeScrolling: nativeScrolling
};
if (isPaging) {
@@ -65810,17 +62762,10 @@ function($timeout, $controller, $ionicBind, $ionicConfig) {
scrollViewOptions.bouncing = false;
}
- var scrollCtrl = $controller('$ionicScroll', {
+ $controller('$ionicScroll', {
$scope: $scope,
scrollViewOptions: scrollViewOptions
});
-
- function onScrollComplete() {
- $scope.$onScrollComplete && $scope.$onScrollComplete({
- scrollTop: scrollCtrl.scrollView.__scrollTop,
- scrollLeft: scrollCtrl.scrollView.__scrollLeft
- });
- }
}
}
};
@@ -66246,7 +63191,6 @@ IonicModule
* @ngdoc directive
* @name ionSlideBox
* @module ionic
- * @codepen AjgEB
* @deprecated will be removed in the next Ionic release in favor of the new ion-slides component.
* Don't depend on the internal behavior of this widget.
* @delegate ionic.service:$ionicSlideBoxDelegate
@@ -66254,6 +63198,7 @@ IonicModule
* @description
* The Slide Box is a multi-page container where each page can be swiped or dragged between:
*
+ * ![SlideBox](http://ionicframework.com.s3.amazonaws.com/docs/controllers/slideBox.gif)
*
* @usage
* ```html
@@ -66402,7 +63347,7 @@ function($animate, $timeout, $compile, $ionicSlideBoxDelegate, $ionicHistory, $i
link: function($scope, $element, $attr) {
// Disable ngAnimate for slidebox and its children
- $animate.enabled($element, false);
+ $animate.enabled(false, $element);
// if showPager is undefined, show the pager
if (!isDefined($attr.showPager)) {
@@ -66479,6 +63424,7 @@ function($animate, $timeout, $compile, $ionicSlideBoxDelegate, $ionicHistory, $i
* @ngdoc directive
* @name ionSlides
* @module ionic
+ * @delegate ionic.service:$ionicSlideBoxDelegate
* @restrict E
* @description
* The Slides component is a powerful multi-page container where each page can be swiped or dragged between.
@@ -66490,84 +63436,22 @@ function($animate, $timeout, $compile, $ionicSlideBoxDelegate, $ionicHistory, $i
*
* @usage
* ```html
- * <ion-content scroll="false">
- * <ion-slides options="options" slider="data.slider">
- * <ion-slide-page>
- * <div class="box blue"><h1>BLUE</h1></div>
- * </ion-slide-page>
- * <ion-slide-page>
- * <div class="box yellow"><h1>YELLOW</h1></div>
- * </ion-slide-page>
- * <ion-slide-page>
- * <div class="box pink"><h1>PINK</h1></div>
- * </ion-slide-page>
- * </ion-slides>
- * </ion-content>
- * ```
- *
- * ```js
- * $scope.options = {
- * loop: false,
- * effect: 'fade',
- * speed: 500,
- * }
- *
- * $scope.$on("$ionicSlides.sliderInitialized", function(event, data){
- * // data.slider is the instance of Swiper
- * $scope.slider = data.slider;
- * });
- *
- * $scope.$on("$ionicSlides.slideChangeStart", function(event, data){
- * console.log('Slide change is beginning');
- * });
- *
- * $scope.$on("$ionicSlides.slideChangeEnd", function(event, data){
- * // note: the indexes are 0-based
- * $scope.activeIndex = data.slider.activeIndex;
- * $scope.previousIndex = data.slider.previousIndex;
- * });
- *
- * ```
- *
- * ## Slide Events
- *
- * The slides component dispatches events when the active slide changes
- *
- * <table class="table">
- * <tr>
- * <td><code>$ionicSlides.slideChangeStart</code></td>
- * <td>This event is emitted when a slide change begins</td>
- * </tr>
- * <tr>
- * <td><code>$ionicSlides.slideChangeEnd</code></td>
- * <td>This event is emitted when a slide change completes</td>
- * </tr>
- * <tr>
- * <td><code>$ionicSlides.sliderInitialized</code></td>
- * <td>This event is emitted when the slider is initialized. It provides access to an instance of the slider.</td>
- * </tr>
- * </table>
- *
- *
- * ## Updating Slides Dynamically
- * When applying data to the slider at runtime, typically everything will work as expected.
- *
- * In the event that the slides are looped, use the `updateLoop` method on the slider to ensure the slides update correctly.
- *
- * ```
- * $scope.$on("$ionicSlides.sliderInitialized", function(event, data){
- * // grab an instance of the slider
- * $scope.slider = data.slider;
- * });
- *
- * function dataChangeHandler(){
- * // call this function when data changes, such as an HTTP request, etc
- * if ( $scope.slider ){
- * $scope.slider.updateLoop();
- * }
- * }
+ * <ion-slides on-slide-changed="slideHasChanged($index)">
+ * <ion-slide-page>
+ * <div class="box blue"><h1>BLUE</h1></div>
+ * </ion-slide-page>
+ * <ion-slide-page>
+ * <div class="box yellow"><h1>YELLOW</h1></div>
+ * </ion-slide-page>
+ * <ion-slide-page>
+ * <div class="box pink"><h1>PINK</h1></div>
+ * </ion-slide-page>
+ * </ion-slides>
* ```
*
+ * @param {string=} delegate-handle The handle used to identify this slideBox
+ * with {@link ionic.service:$ionicSlideBoxDelegate}.
+ * @param {object=} options to pass to the widget. See the full ist here: [http://www.idangero.us/swiper/api/](http://www.idangero.us/swiper/api/)
*/
IonicModule
.directive('ionSlides', [
@@ -66601,17 +63485,10 @@ function($animate, $timeout, $compile) {
_this.__slider.createLoop();
}
- var slidesLength = _this.__slider.slides.length;
-
// Don't allow pager to show with > 10 slides
- if (slidesLength > 10) {
+ if (_this.__slider.slides.length > 10) {
$scope.showPager = false;
}
-
- // When slide index is greater than total then slide to last index
- if (_this.__slider.activeIndex > slidesLength - 1) {
- _this.__slider.slideTo(slidesLength - 1);
- }
});
};
@@ -66626,7 +63503,7 @@ function($animate, $timeout, $compile) {
var options = $scope.options || {};
var newOptions = angular.extend({
- pagination: $element.children().children()[1],
+ pagination: '.swiper-pagination',
paginationClickable: true,
lazyLoading: true,
preloadImages: false
@@ -66637,24 +63514,17 @@ function($animate, $timeout, $compile) {
$timeout(function() {
var slider = new ionic.views.Swiper($element.children()[0], newOptions, $scope, $compile);
- $scope.$emit("$ionicSlides.sliderInitialized", { slider: slider });
-
_this.__slider = slider;
$scope.slider = _this.__slider;
$scope.$on('$destroy', function() {
slider.destroy();
- _this.__slider = null;
});
});
- $timeout(function() {
- // if it's a loop, render the slides again just incase
- _this.rapidUpdate();
- }, 200);
-
}],
+
link: function($scope) {
$scope.showPager = true;
// Disable ngAnimate for slidebox and its children
@@ -66671,10 +63541,6 @@ function($animate, $timeout, $compile) {
template: '<div class="swiper-slide" ng-transclude></div>',
link: function($scope, $element, $attr, ionSlidesCtrl) {
ionSlidesCtrl.rapidUpdate();
-
- $scope.$on('$destroy', function() {
- ionSlidesCtrl.rapidUpdate();
- });
}
};
}]);
@@ -67089,10 +63955,11 @@ IonicModule
replace: true,
require: ['^ionTabs', '^ionTab'],
template:
- '<a ng-class="{\'has-badge\':badge, \'tab-hidden\':isHidden(), \'tab-item-active\': isTabActive()}" ' +
+ '<a ng-class="{\'tab-item-active\': isTabActive(), \'has-badge\':badge, \'tab-hidden\':isHidden()}" ' +
' ng-disabled="disabled()" class="tab-item">' +
'<span class="badge {{badgeStyle}}" ng-if="badge">{{badge}}</span>' +
- '<i class="icon {{getIcon()}}" ng-if="getIcon()"></i>' +
+ '<i class="icon {{getIconOn()}}" ng-if="getIconOn() && isTabActive()"></i>' +
+ '<i class="icon {{getIconOff()}}" ng-if="getIconOff() && !isTabActive()"></i>' +
'<span class="tab-title" ng-bind-html="title"></span>' +
'</a>',
scope: {
@@ -67140,17 +64007,6 @@ IonicModule
$scope.isTabActive = function() {
return tabsCtrl.selectedTab() === tabCtrl.$scope;
};
-
- $scope.getIcon = function() {
- if ( tabsCtrl.selectedTab() === tabCtrl.$scope ) {
- // active
- return $scope.iconOn || $scope.icon;
- }
- else {
- // inactive
- return $scope.iconOff || $scope.icon;
- }
- };
}
};
}]);
@@ -67449,10 +64305,6 @@ function($timeout, $ionicConfig) {
* show. Also contained is transition data, such as the transition type and
* direction that will be or was used.
*
- * Life cycle events are emitted upwards from the transitioning view's scope. In some cases, it is
- * desirable for a child/nested view to be notified of the event.
- * For this use case, `$ionicParentView` life cycle events are broadcast downwards.
- *
* <table class="table">
* <tr>
* <td><code>$ionicView.loaded</code></td>
@@ -67493,56 +64345,8 @@ function($timeout, $ionicConfig) {
* <td>The view's controller has been destroyed and its element has been
* removed from the DOM.</td>
* </tr>
- * <tr>
- * <td><code>$ionicParentView.enter</code></td>
- * <td>The parent view has fully entered and is now the active view.
- * This event will fire, whether it was the first load or a cached view.</td>
- * </tr>
- * <tr>
- * <td><code>$ionicParentView.leave</code></td>
- * <td>The parent view has finished leaving and is no longer the
- * active view. This event will fire, whether it is cached or destroyed.</td>
- * </tr>
- * <tr>
- * <td><code>$ionicParentView.beforeEnter</code></td>
- * <td>The parent view is about to enter and become the active view.</td>
- * </tr>
- * <tr>
- * <td><code>$ionicParentView.beforeLeave</code></td>
- * <td>The parent view is about to leave and no longer be the active view.</td>
- * </tr>
- * <tr>
- * <td><code>$ionicParentView.afterEnter</code></td>
- * <td>The parent view has fully entered and is now the active view.</td>
- * </tr>
- * <tr>
- * <td><code>$ionicParentView.afterLeave</code></td>
- * <td>The parent view has finished leaving and is no longer the active view.</td>
- * </tr>
* </table>
*
- * ## LifeCycle Event Usage
- *
- * Below is an example of how to listen to life cycle events and
- * access state parameter data
- *
- * ```js
- * $scope.$on("$ionicView.beforeEnter", function(event, data){
- * // handle event
- * console.log("State Params: ", data.stateParams);
- * });
- *
- * $scope.$on("$ionicView.enter", function(event, data){
- * // handle event
- * console.log("State Params: ", data.stateParams);
- * });
- *
- * $scope.$on("$ionicView.afterEnter", function(event, data){
- * // handle event
- * console.log("State Params: ", data.stateParams);
- * });
- * ```
- *
* ## Caching
*
* Caching can be disabled and enabled in multiple ways. By default, Ionic will