Commit ba05196a authored by Gergely Ujvari's avatar Gergely Ujvari

Upgrade angularjs to 1.2.28

parent 7b2d635d
/** /**
* @license AngularJS v1.2.25 * @license AngularJS v1.2.28
* (c) 2010-2014 Google, Inc. http://angularjs.org * (c) 2010-2014 Google, Inc. http://angularjs.org
* License: MIT * License: MIT
*/ */
...@@ -68,7 +68,7 @@ function minErr(module) { ...@@ -68,7 +68,7 @@ function minErr(module) {
return match; return match;
}); });
message = message + '\nhttp://errors.angularjs.org/1.2.25/' + message = message + '\nhttp://errors.angularjs.org/1.2.28/' +
(module ? module + '/' : '') + code; (module ? module + '/' : '') + code;
for (i = 2; i < arguments.length; i++) { for (i = 2; i < arguments.length; i++) {
message = message + (i == 2 ? '?' : '&') + 'p' + (i-2) + '=' + message = message + (i == 2 ? '?' : '&') + 'p' + (i-2) + '=' +
...@@ -234,8 +234,8 @@ if ('i' !== 'I'.toLowerCase()) { ...@@ -234,8 +234,8 @@ if ('i' !== 'I'.toLowerCase()) {
} }
var /** holds major version number for IE or NaN for real browsers */ var
msie, msie, // holds major version number for IE, or NaN if UA is not IE.
jqLite, // delay binding since jQuery could be loaded after us. jqLite, // delay binding since jQuery could be loaded after us.
jQuery, // delay binding jQuery, // delay binding
slice = [].slice, slice = [].slice,
...@@ -417,7 +417,7 @@ function setHashKey(obj, h) { ...@@ -417,7 +417,7 @@ function setHashKey(obj, h) {
* @kind function * @kind function
* *
* @description * @description
* Extends the destination object `dst` by copying all of the properties from the `src` object(s) * Extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
* to `dst`. You can specify multiple `src` objects. * to `dst`. You can specify multiple `src` objects.
* *
* @param {Object} dst Destination object. * @param {Object} dst Destination object.
...@@ -1987,11 +1987,11 @@ function setupModuleLoader(window) { ...@@ -1987,11 +1987,11 @@ function setupModuleLoader(window) {
* - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat". * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
*/ */
var version = { var version = {
full: '1.2.25', // all of these placeholder strings will be replaced by grunt's full: '1.2.28', // all of these placeholder strings will be replaced by grunt's
major: 1, // package task major: 1, // package task
minor: 2, minor: 2,
dot: 25, dot: 28,
codeName: 'hypnotic-gesticulation' codeName: 'finnish-disembarkation'
}; };
...@@ -2158,7 +2158,7 @@ function publishExternalAPI(angular){ ...@@ -2158,7 +2158,7 @@ function publishExternalAPI(angular){
* - [`children()`](http://api.jquery.com/children/) - Does not support selectors * - [`children()`](http://api.jquery.com/children/) - Does not support selectors
* - [`clone()`](http://api.jquery.com/clone/) * - [`clone()`](http://api.jquery.com/clone/)
* - [`contents()`](http://api.jquery.com/contents/) * - [`contents()`](http://api.jquery.com/contents/)
* - [`css()`](http://api.jquery.com/css/) * - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyles()`
* - [`data()`](http://api.jquery.com/data/) * - [`data()`](http://api.jquery.com/data/)
* - [`empty()`](http://api.jquery.com/empty/) * - [`empty()`](http://api.jquery.com/empty/)
* - [`eq()`](http://api.jquery.com/eq/) * - [`eq()`](http://api.jquery.com/eq/)
...@@ -3202,13 +3202,13 @@ HashMap.prototype = { ...@@ -3202,13 +3202,13 @@ HashMap.prototype = {
* @kind function * @kind function
* *
* @description * @description
* Creates an injector function that can be used for retrieving services as well as for * Creates an injector object that can be used for retrieving services as well as for
* dependency injection (see {@link guide/di dependency injection}). * dependency injection (see {@link guide/di dependency injection}).
* *
* @param {Array.<string|Function>} modules A list of module functions or their aliases. See * @param {Array.<string|Function>} modules A list of module functions or their aliases. See
* {@link angular.module}. The `ng` module must be explicitly added. * {@link angular.module}. The `ng` module must be explicitly added.
* @returns {function()} Injector function. See {@link auto.$injector $injector}. * @returns {injector} Injector object. See {@link auto.$injector $injector}.
* *
* @example * @example
* Typical usage * Typical usage
...@@ -3296,7 +3296,6 @@ function annotate(fn) { ...@@ -3296,7 +3296,6 @@ function annotate(fn) {
/** /**
* @ngdoc service * @ngdoc service
* @name $injector * @name $injector
* @kind function
* *
* @description * @description
* *
...@@ -3311,7 +3310,7 @@ function annotate(fn) { ...@@ -3311,7 +3310,7 @@ function annotate(fn) {
* expect($injector.get('$injector')).toBe($injector); * expect($injector.get('$injector')).toBe($injector);
* expect($injector.invoke(function($injector){ * expect($injector.invoke(function($injector){
* return $injector; * return $injector;
* }).toBe($injector); * })).toBe($injector);
* ``` * ```
* *
* # Injection Function Annotation * # Injection Function Annotation
...@@ -3378,8 +3377,8 @@ function annotate(fn) { ...@@ -3378,8 +3377,8 @@ function annotate(fn) {
* @description * @description
* Allows the user to query if the particular service exists. * Allows the user to query if the particular service exists.
* *
* @param {string} Name of the service to query. * @param {string} name Name of the service to query.
* @returns {boolean} returns true if injector has given service. * @returns {boolean} `true` if injector has given service.
*/ */
/** /**
...@@ -4044,6 +4043,19 @@ function $AnchorScrollProvider() { ...@@ -4044,6 +4043,19 @@ function $AnchorScrollProvider() {
var autoScrollingEnabled = true; var autoScrollingEnabled = true;
/**
* @ngdoc method
* @name $anchorScrollProvider#disableAutoScrolling
*
* @description
* By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to
* {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.<br />
* Use this method to disable automatic scrolling.
*
* If automatic scrolling is disabled, one must explicitly call
* {@link ng.$anchorScroll $anchorScroll()} in order to scroll to the element related to the
* current hash.
*/
this.disableAutoScrolling = function() { this.disableAutoScrolling = function() {
autoScrollingEnabled = false; autoScrollingEnabled = false;
}; };
...@@ -4348,6 +4360,8 @@ function $$AsyncCallbackProvider(){ ...@@ -4348,6 +4360,8 @@ function $$AsyncCallbackProvider(){
}]; }];
} }
/* global stripHash: true */
/** /**
* ! This is a private undocumented service ! * ! This is a private undocumented service !
* *
...@@ -4472,7 +4486,7 @@ function Browser(window, document, $log, $sniffer) { ...@@ -4472,7 +4486,7 @@ function Browser(window, document, $log, $sniffer) {
var lastBrowserUrl = location.href, var lastBrowserUrl = location.href,
baseElement = document.find('base'), baseElement = document.find('base'),
newLocation = null; reloadLocation = null;
/** /**
* @name $browser#url * @name $browser#url
...@@ -4501,8 +4515,13 @@ function Browser(window, document, $log, $sniffer) { ...@@ -4501,8 +4515,13 @@ function Browser(window, document, $log, $sniffer) {
// setter // setter
if (url) { if (url) {
if (lastBrowserUrl == url) return; if (lastBrowserUrl == url) return;
var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url);
lastBrowserUrl = url; lastBrowserUrl = url;
if ($sniffer.history) { // Don't use history API if only the hash changed
// due to a bug in IE10/IE11 which leads
// to not firing a `hashchange` nor `popstate` event
// in some cases (see #9143).
if (!sameBase && $sniffer.history) {
if (replace) history.replaceState(null, '', url); if (replace) history.replaceState(null, '', url);
else { else {
history.pushState(null, '', url); history.pushState(null, '', url);
...@@ -4510,7 +4529,9 @@ function Browser(window, document, $log, $sniffer) { ...@@ -4510,7 +4529,9 @@ function Browser(window, document, $log, $sniffer) {
baseElement.attr('href', baseElement.attr('href')); baseElement.attr('href', baseElement.attr('href'));
} }
} else { } else {
newLocation = url; if (!sameBase) {
reloadLocation = url;
}
if (replace) { if (replace) {
location.replace(url); location.replace(url);
} else { } else {
...@@ -4520,10 +4541,10 @@ function Browser(window, document, $log, $sniffer) { ...@@ -4520,10 +4541,10 @@ function Browser(window, document, $log, $sniffer) {
return self; return self;
// getter // getter
} else { } else {
// - newLocation is a workaround for an IE7-9 issue with location.replace and location.href // - reloadLocation is needed as browsers don't allow to read out
// methods not updating location.href synchronously. // the new location.href if a reload happened.
// - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172 // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
return newLocation || location.href.replace(/%27/g,"'"); return reloadLocation || location.href.replace(/%27/g,"'");
} }
}; };
...@@ -4531,7 +4552,6 @@ function Browser(window, document, $log, $sniffer) { ...@@ -4531,7 +4552,6 @@ function Browser(window, document, $log, $sniffer) {
urlChangeInit = false; urlChangeInit = false;
function fireUrlChange() { function fireUrlChange() {
newLocation = null;
if (lastBrowserUrl == self.url()) return; if (lastBrowserUrl == self.url()) return;
lastBrowserUrl = self.url(); lastBrowserUrl = self.url();
...@@ -5107,7 +5127,8 @@ function $CacheFactoryProvider() { ...@@ -5107,7 +5127,8 @@ function $CacheFactoryProvider() {
* ``` * ```
* *
* **Note:** the `script` tag containing the template does not need to be included in the `head` of * **Note:** the `script` tag containing the template does not need to be included in the `head` of
* the document, but it must be below the `ng-app` definition. * the document, but it must be a descendent of the {@link ng.$rootElement $rootElement} (IE,
* element with ng-app attribute), otherwise the template will be ignored.
* *
* Adding via the $templateCache service: * Adding via the $templateCache service:
* *
...@@ -5392,8 +5413,13 @@ function $TemplateCacheProvider() { ...@@ -5392,8 +5413,13 @@ function $TemplateCacheProvider() {
* scope. This makes it possible for the widget to have private state, and the transclusion to * scope. This makes it possible for the widget to have private state, and the transclusion to
* be bound to the parent (pre-`isolate`) scope. * be bound to the parent (pre-`isolate`) scope.
* *
* * `true` - transclude the content of the directive. * There are two kinds of transclusion depending upon whether you want to transclude just the contents of the
* * `'element'` - transclude the whole element including any directives defined at lower priority. * 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.
* *
* <div class="alert alert-warning"> * <div class="alert alert-warning">
* **Note:** When testing an element transclude directive you must not place the directive at the root of the * **Note:** When testing an element transclude directive you must not place the directive at the root of the
...@@ -5529,7 +5555,7 @@ function $TemplateCacheProvider() { ...@@ -5529,7 +5555,7 @@ function $TemplateCacheProvider() {
* } * }
* ``` * ```
* *
* Below is an example using `$compileProvider`. * ## Example
* *
* <div class="alert alert-warning"> * <div class="alert alert-warning">
* **Note**: Typically directives are registered with `module.directive`. The example below is * **Note**: Typically directives are registered with `module.directive`. The example below is
...@@ -5785,6 +5811,21 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { ...@@ -5785,6 +5811,21 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
}; };
Attributes.prototype = { Attributes.prototype = {
/**
* @ngdoc method
* @name $compile.directive.Attributes#$normalize
* @kind function
*
* @description
* Converts an attribute name (e.g. dash/colon/underscore-delimited string, optionally prefixed with `x-` or
* `data-`) to its normalized, camelCase form.
*
* Also there is special case for Moz prefix starting with upper case letter.
*
* For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}
*
* @param {string} name Name to normalize
*/
$normalize: directiveNormalize, $normalize: directiveNormalize,
...@@ -7118,13 +7159,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { ...@@ -7118,13 +7159,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
var PREFIX_REGEXP = /^(x[\:\-_]|data[\:\-_])/i; var PREFIX_REGEXP = /^(x[\:\-_]|data[\:\-_])/i;
/** /**
* Converts all accepted directives format into proper directive name. * Converts all accepted directives format into proper directive name.
* All of these will become 'myDirective':
* my:Directive
* my-directive
* x-my-directive
* data-my:directive
*
* Also there is special case for Moz prefix starting with upper case letter.
* @param name Name to normalize * @param name Name to normalize
*/ */
function directiveNormalize(name) { function directiveNormalize(name) {
...@@ -7510,9 +7544,18 @@ function $HttpProvider() { ...@@ -7510,9 +7544,18 @@ function $HttpProvider() {
}; };
/** /**
* Are ordered by request, i.e. they are applied in the same order as the * @ngdoc property
* @name $httpProvider#interceptors
* @description
*
* Array containing service factories for all synchronous or asynchronous {@link ng.$http $http}
* pre-processing of request or postprocessing of responses.
*
* These service factories are ordered by request, i.e. they are applied in the same order as the
* array, on request, but reverse order, on response. * array, on request, but reverse order, on response.
*/ *
* {@link ng.$http#interceptors Interceptors detailed info}
**/
var interceptorFactories = this.interceptors = []; var interceptorFactories = this.interceptors = [];
/** /**
...@@ -7674,6 +7717,21 @@ function $HttpProvider() { ...@@ -7674,6 +7717,21 @@ function $HttpProvider() {
* In addition, you can supply a `headers` property in the config object passed when * In addition, you can supply a `headers` property in the config object passed when
* calling `$http(config)`, which overrides the defaults without changing them globally. * calling `$http(config)`, which overrides the defaults without changing them globally.
* *
* To explicitly remove a header automatically added via $httpProvider.defaults.headers on a per request basis,
* Use the `headers` property, setting the desired header to `undefined`. For example:
*
* ```js
* var req = {
* method: 'POST',
* url: 'http://example.com',
* headers: {
* 'Content-Type': undefined
* },
* data: { test: 'test' },
* }
*
* $http(req).success(function(){...}).error(function(){...});
* ```
* *
* # Transforming Requests and Responses * # Transforming Requests and Responses
* *
...@@ -8037,12 +8095,13 @@ function $HttpProvider() { ...@@ -8037,12 +8095,13 @@ function $HttpProvider() {
expect(data.getText()).toMatch(/Hello, \$http!/); expect(data.getText()).toMatch(/Hello, \$http!/);
}); });
it('should make a JSONP request to angularjs.org', function() { // Commented out due to flakes. See https://github.com/angular/angular.js/issues/9185
sampleJsonpBtn.click(); // it('should make a JSONP request to angularjs.org', function() {
fetchBtn.click(); // sampleJsonpBtn.click();
expect(status.getText()).toMatch('200'); // fetchBtn.click();
expect(data.getText()).toMatch(/Super Hero!/); // expect(status.getText()).toMatch('200');
}); // expect(data.getText()).toMatch(/Super Hero!/);
// });
it('should make JSONP request to invalid URL and invoke the error handler', it('should make JSONP request to invalid URL and invoke the error handler',
function() { function() {
...@@ -8252,18 +8311,31 @@ function $HttpProvider() { ...@@ -8252,18 +8311,31 @@ function $HttpProvider() {
* @param {Object=} config Optional configuration object * @param {Object=} config Optional configuration object
* @returns {HttpPromise} Future object * @returns {HttpPromise} Future object
*/ */
createShortMethodsWithData('post', 'put');
/** /**
* @ngdoc property * @ngdoc method
* @name $http#defaults * @name $http#patch
* *
* @description * @description
* Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of * Shortcut method to perform `PATCH` request.
* default headers, withCredentials as well as request and response transformations. *
* * @param {string} url Relative or absolute URL specifying the destination of the request
* See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above. * @param {*} data Request content
*/ * @param {Object=} config Optional configuration object
* @returns {HttpPromise} Future object
*/
createShortMethodsWithData('post', 'put', 'patch');
/**
* @ngdoc property
* @name $http#defaults
*
* @description
* Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of
* default headers, withCredentials as well as request and response transformations.
*
* See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above.
*/
$http.defaults = defaults; $http.defaults = defaults;
...@@ -8949,33 +9021,33 @@ function $IntervalProvider() { ...@@ -8949,33 +9021,33 @@ function $IntervalProvider() {
* // Don't start a new fight if we are already fighting * // Don't start a new fight if we are already fighting
* if ( angular.isDefined(stop) ) return; * if ( angular.isDefined(stop) ) return;
* *
* stop = $interval(function() { * stop = $interval(function() {
* if ($scope.blood_1 > 0 && $scope.blood_2 > 0) { * if ($scope.blood_1 > 0 && $scope.blood_2 > 0) {
* $scope.blood_1 = $scope.blood_1 - 3; * $scope.blood_1 = $scope.blood_1 - 3;
* $scope.blood_2 = $scope.blood_2 - 4; * $scope.blood_2 = $scope.blood_2 - 4;
* } else { * } else {
* $scope.stopFight(); * $scope.stopFight();
* }
* }, 100);
* };
*
* $scope.stopFight = function() {
* if (angular.isDefined(stop)) {
* $interval.cancel(stop);
* stop = undefined;
* } * }
* }, 100); * };
* };
* *
* $scope.stopFight = function() { * $scope.resetFight = function() {
* if (angular.isDefined(stop)) { * $scope.blood_1 = 100;
* $interval.cancel(stop); * $scope.blood_2 = 120;
* stop = undefined; * };
* }
* };
* *
* $scope.resetFight = function() { * $scope.$on('$destroy', function() {
* $scope.blood_1 = 100; * // Make sure that the interval is destroyed too
* $scope.blood_2 = 120; * $scope.stopFight();
* }; * });
* * }])
* $scope.$on('$destroy', function() {
* // Make sure that the interval is destroyed too
* $scope.stopFight();
* });
* }])
* // Register the 'myCurrentTime' directive factory method. * // Register the 'myCurrentTime' directive factory method.
* // We inject $interval and dateFilter service since the factory method is DI. * // We inject $interval and dateFilter service since the factory method is DI.
* .directive('myCurrentTime', ['$interval', 'dateFilter', * .directive('myCurrentTime', ['$interval', 'dateFilter',
...@@ -9277,21 +9349,26 @@ function LocationHtml5Url(appBase, basePrefix) { ...@@ -9277,21 +9349,26 @@ function LocationHtml5Url(appBase, basePrefix) {
this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/' this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/'
}; };
this.$$rewrite = function(url) { this.$$parseLinkUrl = function(url, relHref) {
var appUrl, prevAppUrl; var appUrl, prevAppUrl;
var rewrittenUrl;
if ( (appUrl = beginsWith(appBase, url)) !== undefined ) { if ( (appUrl = beginsWith(appBase, url)) !== undefined ) {
prevAppUrl = appUrl; prevAppUrl = appUrl;
if ( (appUrl = beginsWith(basePrefix, appUrl)) !== undefined ) { if ( (appUrl = beginsWith(basePrefix, appUrl)) !== undefined ) {
return appBaseNoFile + (beginsWith('/', appUrl) || appUrl); rewrittenUrl = appBaseNoFile + (beginsWith('/', appUrl) || appUrl);
} else { } else {
return appBase + prevAppUrl; rewrittenUrl = appBase + prevAppUrl;
} }
} else if ( (appUrl = beginsWith(appBaseNoFile, url)) !== undefined ) { } else if ( (appUrl = beginsWith(appBaseNoFile, url)) !== undefined ) {
return appBaseNoFile + appUrl; rewrittenUrl = appBaseNoFile + appUrl;
} else if (appBaseNoFile == url + '/') { } else if (appBaseNoFile == url + '/') {
return appBaseNoFile; rewrittenUrl = appBaseNoFile;
}
if (rewrittenUrl) {
this.$$parse(rewrittenUrl);
} }
return !!rewrittenUrl;
}; };
} }
...@@ -9381,10 +9458,12 @@ function LocationHashbangUrl(appBase, hashPrefix) { ...@@ -9381,10 +9458,12 @@ function LocationHashbangUrl(appBase, hashPrefix) {
this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : ''); this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : '');
}; };
this.$$rewrite = function(url) { this.$$parseLinkUrl = function(url, relHref) {
if(stripHash(appBase) == stripHash(url)) { if(stripHash(appBase) == stripHash(url)) {
return url; this.$$parse(url);
return true;
} }
return false;
}; };
} }
...@@ -9404,16 +9483,21 @@ function LocationHashbangInHtml5Url(appBase, hashPrefix) { ...@@ -9404,16 +9483,21 @@ function LocationHashbangInHtml5Url(appBase, hashPrefix) {
var appBaseNoFile = stripFile(appBase); var appBaseNoFile = stripFile(appBase);
this.$$rewrite = function(url) { this.$$parseLinkUrl = function(url, relHref) {
var rewrittenUrl;
var appUrl; var appUrl;
if ( appBase == stripHash(url) ) { if ( appBase == stripHash(url) ) {
return url; rewrittenUrl = url;
} else if ( (appUrl = beginsWith(appBaseNoFile, url)) ) { } else if ( (appUrl = beginsWith(appBaseNoFile, url)) ) {
return appBase + hashPrefix + appUrl; rewrittenUrl = appBase + hashPrefix + appUrl;
} else if ( appBaseNoFile === url + '/') { } else if ( appBaseNoFile === url + '/') {
return appBaseNoFile; rewrittenUrl = appBaseNoFile;
}
if (rewrittenUrl) {
this.$$parse(rewrittenUrl);
} }
return !!rewrittenUrl;
}; };
this.$$compose = function() { this.$$compose = function() {
...@@ -9541,7 +9625,7 @@ LocationHashbangInHtml5Url.prototype = ...@@ -9541,7 +9625,7 @@ LocationHashbangInHtml5Url.prototype =
* @return {string} path * @return {string} path
*/ */
path: locationGetterSetter('$$path', function(path) { path: locationGetterSetter('$$path', function(path) {
path = path ? path.toString() : ''; path = path !== null ? path.toString() : '';
return path.charAt(0) == '/' ? path : '/' + path; return path.charAt(0) == '/' ? path : '/' + path;
}), }),
...@@ -9638,7 +9722,7 @@ LocationHashbangInHtml5Url.prototype = ...@@ -9638,7 +9722,7 @@ LocationHashbangInHtml5Url.prototype =
* @return {string} hash * @return {string} hash
*/ */
hash: locationGetterSetter('$$hash', function(hash) { hash: locationGetterSetter('$$hash', function(hash) {
return hash ? hash.toString() : ''; return hash !== null ? hash.toString() : '';
}), }),
/** /**
...@@ -9786,7 +9870,7 @@ function $LocationProvider(){ ...@@ -9786,7 +9870,7 @@ function $LocationProvider(){
LocationMode = LocationHashbangUrl; LocationMode = LocationHashbangUrl;
} }
$location = new LocationMode(appBase, '#' + hashPrefix); $location = new LocationMode(appBase, '#' + hashPrefix);
$location.$$parse($location.$$rewrite(initialUrl)); $location.$$parseLinkUrl(initialUrl, initialUrl);
var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i; var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i;
...@@ -9805,6 +9889,9 @@ function $LocationProvider(){ ...@@ -9805,6 +9889,9 @@ function $LocationProvider(){
} }
var absHref = elm.prop('href'); var absHref = elm.prop('href');
// get the actual href attribute - see
// http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx
var relHref = elm.attr('href') || elm.attr('xlink:href');
if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') { if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') {
// SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during // SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during
...@@ -9815,50 +9902,18 @@ function $LocationProvider(){ ...@@ -9815,50 +9902,18 @@ function $LocationProvider(){
// Ignore when url is started with javascript: or mailto: // Ignore when url is started with javascript: or mailto:
if (IGNORE_URI_REGEXP.test(absHref)) return; if (IGNORE_URI_REGEXP.test(absHref)) return;
// Make relative links work in HTML5 mode for legacy browsers (or at least IE8 & 9) if (absHref && !elm.attr('target') && !event.isDefaultPrevented()) {
// The href should be a regular url e.g. /link/somewhere or link/somewhere or ../somewhere or if ($location.$$parseLinkUrl(absHref, relHref)) {
// somewhere#anchor or http://example.com/somewhere // We do a preventDefault for all urls that are part of the angular application,
if (LocationMode === LocationHashbangInHtml5Url) { // in html5mode and also without, so that we are able to abort navigation without
// get the actual href attribute - see // getting double entries in the location history.
// http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx event.preventDefault();
var href = elm.attr('href') || elm.attr('xlink:href');
if (href && href.indexOf('://') < 0) { // Ignore absolute URLs
var prefix = '#' + hashPrefix;
if (href[0] == '/') {
// absolute path - replace old path
absHref = appBase + prefix + href;
} else if (href[0] == '#') {
// local anchor
absHref = appBase + prefix + ($location.path() || '/') + href;
} else {
// relative path - join with current path
var stack = $location.path().split("/"),
parts = href.split("/");
if (stack.length === 2 && !stack[1]) stack.length = 1;
for (var i=0; i<parts.length; i++) {
if (parts[i] == ".")
continue;
else if (parts[i] == "..")
stack.pop();
else if (parts[i].length)
stack.push(parts[i]);
}
absHref = appBase + prefix + stack.join('/');
}
}
}
var rewrittenUrl = $location.$$rewrite(absHref);
if (absHref && !elm.attr('target') && rewrittenUrl && !event.isDefaultPrevented()) {
event.preventDefault();
if (rewrittenUrl != $browser.url()) {
// update location manually // update location manually
$location.$$parse(rewrittenUrl); if ($location.absUrl() != $browser.url()) {
$rootScope.$apply(); $rootScope.$apply();
// hack to work around FF6 bug 684208 when scenario runner clicks on links // hack to work around FF6 bug 684208 when scenario runner clicks on links
window.angular['ff-684208-preventDefault'] = true; window.angular['ff-684208-preventDefault'] = true;
}
} }
} }
}); });
...@@ -10088,7 +10143,7 @@ var promiseWarning; ...@@ -10088,7 +10143,7 @@ var promiseWarning;
// Sandboxing Angular Expressions // Sandboxing Angular Expressions
// ------------------------------ // ------------------------------
// Angular expressions are generally considered safe because these expressions only have direct // Angular expressions are generally considered safe because these expressions only have direct
// access to $scope and locals. However, one can obtain the ability to execute arbitrary JS code by // access to `$scope` and locals. However, one can obtain the ability to execute arbitrary JS code by
// obtaining a reference to native JS functions such as the Function constructor. // obtaining a reference to native JS functions such as the Function constructor.
// //
// As an example, consider the following Angular expression: // As an example, consider the following Angular expression:
...@@ -10097,7 +10152,7 @@ var promiseWarning; ...@@ -10097,7 +10152,7 @@ var promiseWarning;
// //
// This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits // This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits
// against the expression language, but not to prevent exploits that were enabled by exposing // against the expression language, but not to prevent exploits that were enabled by exposing
// sensitive JavaScript or browser apis on Scope. Exposing such objects on a Scope is never a good // sensitive JavaScript or browser APIs on Scope. Exposing such objects on a Scope is never a good
// practice and therefore we are not even trying to protect against interaction with an object // practice and therefore we are not even trying to protect against interaction with an object
// explicitly exposed in this way. // explicitly exposed in this way.
// //
...@@ -10105,6 +10160,8 @@ var promiseWarning; ...@@ -10105,6 +10160,8 @@ var promiseWarning;
// window or some DOM object that has a reference to window is published onto a Scope. // window or some DOM object that has a reference to window is published onto a Scope.
// Similarly we prevent invocations of function known to be dangerous, as well as assignments to // Similarly we prevent invocations of function known to be dangerous, as well as assignments to
// native objects. // native objects.
//
// See https://docs.angularjs.org/guide/security
function ensureSafeMemberName(name, fullExpression) { function ensureSafeMemberName(name, fullExpression) {
...@@ -10841,7 +10898,7 @@ Parser.prototype = { ...@@ -10841,7 +10898,7 @@ Parser.prototype = {
ensureSafeObject(context, parser.text); ensureSafeObject(context, parser.text);
ensureSafeFunction(fnPtr, parser.text); ensureSafeFunction(fnPtr, parser.text);
// IE stupidity! (IE doesn't have apply for some native functions) // IE doesn't have apply for some native functions
var v = fnPtr.apply var v = fnPtr.apply
? fnPtr.apply(context, args) ? fnPtr.apply(context, args)
: fnPtr(args[0], args[1], args[2], args[3], args[4]); : fnPtr(args[0], args[1], args[2], args[3], args[4]);
...@@ -10955,7 +11012,12 @@ function setter(obj, path, setValue, fullExp, options) { ...@@ -10955,7 +11012,12 @@ function setter(obj, path, setValue, fullExp, options) {
return setValue; return setValue;
} }
var getterFnCache = {}; var getterFnCacheDefault = {};
var getterFnCacheExpensive = {};
function isPossiblyDangerousMemberName(name) {
return name == 'constructor';
}
/** /**
* Implementation of the "Black Hole" variant from: * Implementation of the "Black Hole" variant from:
...@@ -10968,29 +11030,38 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, options) { ...@@ -10968,29 +11030,38 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, options) {
ensureSafeMemberName(key2, fullExp); ensureSafeMemberName(key2, fullExp);
ensureSafeMemberName(key3, fullExp); ensureSafeMemberName(key3, fullExp);
ensureSafeMemberName(key4, fullExp); ensureSafeMemberName(key4, fullExp);
var eso = function(o) {
return ensureSafeObject(o, fullExp);
};
var expensiveChecks = options.expensiveChecks;
var eso0 = (expensiveChecks || isPossiblyDangerousMemberName(key0)) ? eso : identity;
var eso1 = (expensiveChecks || isPossiblyDangerousMemberName(key1)) ? eso : identity;
var eso2 = (expensiveChecks || isPossiblyDangerousMemberName(key2)) ? eso : identity;
var eso3 = (expensiveChecks || isPossiblyDangerousMemberName(key3)) ? eso : identity;
var eso4 = (expensiveChecks || isPossiblyDangerousMemberName(key4)) ? eso : identity;
return !options.unwrapPromises return !options.unwrapPromises
? function cspSafeGetter(scope, locals) { ? function cspSafeGetter(scope, locals) {
var pathVal = (locals && locals.hasOwnProperty(key0)) ? locals : scope; var pathVal = (locals && locals.hasOwnProperty(key0)) ? locals : scope;
if (pathVal == null) return pathVal; if (pathVal == null) return pathVal;
pathVal = pathVal[key0]; pathVal = eso0(pathVal[key0]);
if (!key1) return pathVal; if (!key1) return pathVal;
if (pathVal == null) return undefined; if (pathVal == null) return undefined;
pathVal = pathVal[key1]; pathVal = eso1(pathVal[key1]);
if (!key2) return pathVal; if (!key2) return pathVal;
if (pathVal == null) return undefined; if (pathVal == null) return undefined;
pathVal = pathVal[key2]; pathVal = eso2(pathVal[key2]);
if (!key3) return pathVal; if (!key3) return pathVal;
if (pathVal == null) return undefined; if (pathVal == null) return undefined;
pathVal = pathVal[key3]; pathVal = eso3(pathVal[key3]);
if (!key4) return pathVal; if (!key4) return pathVal;
if (pathVal == null) return undefined; if (pathVal == null) return undefined;
pathVal = pathVal[key4]; pathVal = eso4(pathVal[key4]);
return pathVal; return pathVal;
} }
...@@ -11000,73 +11071,81 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, options) { ...@@ -11000,73 +11071,81 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, options) {
if (pathVal == null) return pathVal; if (pathVal == null) return pathVal;
pathVal = pathVal[key0]; pathVal = eso0(pathVal[key0]);
if (pathVal && pathVal.then) { if (pathVal && pathVal.then) {
promiseWarning(fullExp); promiseWarning(fullExp);
if (!("$$v" in pathVal)) { if (!("$$v" in pathVal)) {
promise = pathVal; promise = pathVal;
promise.$$v = undefined; promise.$$v = undefined;
promise.then(function(val) { promise.$$v = val; }); promise.then(function(val) { promise.$$v = eso0(val); });
} }
pathVal = pathVal.$$v; pathVal = eso0(pathVal.$$v);
} }
if (!key1) return pathVal; if (!key1) return pathVal;
if (pathVal == null) return undefined; if (pathVal == null) return undefined;
pathVal = pathVal[key1]; pathVal = eso1(pathVal[key1]);
if (pathVal && pathVal.then) { if (pathVal && pathVal.then) {
promiseWarning(fullExp); promiseWarning(fullExp);
if (!("$$v" in pathVal)) { if (!("$$v" in pathVal)) {
promise = pathVal; promise = pathVal;
promise.$$v = undefined; promise.$$v = undefined;
promise.then(function(val) { promise.$$v = val; }); promise.then(function(val) { promise.$$v = eso1(val); });
} }
pathVal = pathVal.$$v; pathVal = eso1(pathVal.$$v);
} }
if (!key2) return pathVal; if (!key2) return pathVal;
if (pathVal == null) return undefined; if (pathVal == null) return undefined;
pathVal = pathVal[key2]; pathVal = eso2(pathVal[key2]);
if (pathVal && pathVal.then) { if (pathVal && pathVal.then) {
promiseWarning(fullExp); promiseWarning(fullExp);
if (!("$$v" in pathVal)) { if (!("$$v" in pathVal)) {
promise = pathVal; promise = pathVal;
promise.$$v = undefined; promise.$$v = undefined;
promise.then(function(val) { promise.$$v = val; }); promise.then(function(val) { promise.$$v = eso2(val); });
} }
pathVal = pathVal.$$v; pathVal = eso2(pathVal.$$v);
} }
if (!key3) return pathVal; if (!key3) return pathVal;
if (pathVal == null) return undefined; if (pathVal == null) return undefined;
pathVal = pathVal[key3]; pathVal = eso3(pathVal[key3]);
if (pathVal && pathVal.then) { if (pathVal && pathVal.then) {
promiseWarning(fullExp); promiseWarning(fullExp);
if (!("$$v" in pathVal)) { if (!("$$v" in pathVal)) {
promise = pathVal; promise = pathVal;
promise.$$v = undefined; promise.$$v = undefined;
promise.then(function(val) { promise.$$v = val; }); promise.then(function(val) { promise.$$v = eso3(val); });
} }
pathVal = pathVal.$$v; pathVal = eso3(pathVal.$$v);
} }
if (!key4) return pathVal; if (!key4) return pathVal;
if (pathVal == null) return undefined; if (pathVal == null) return undefined;
pathVal = pathVal[key4]; pathVal = eso4(pathVal[key4]);
if (pathVal && pathVal.then) { if (pathVal && pathVal.then) {
promiseWarning(fullExp); promiseWarning(fullExp);
if (!("$$v" in pathVal)) { if (!("$$v" in pathVal)) {
promise = pathVal; promise = pathVal;
promise.$$v = undefined; promise.$$v = undefined;
promise.then(function(val) { promise.$$v = val; }); promise.then(function(val) { promise.$$v = eso4(val); });
} }
pathVal = pathVal.$$v; pathVal = eso4(pathVal.$$v);
} }
return pathVal; return pathVal;
}; };
} }
function getterFnWithExtraArgs(fn, fullExpression) {
return function(s, l) {
return fn(s, l, promiseWarning, ensureSafeObject, fullExpression);
};
}
function getterFn(path, options, fullExp) { function getterFn(path, options, fullExp) {
var expensiveChecks = options.expensiveChecks;
var getterFnCache = (expensiveChecks ? getterFnCacheExpensive : getterFnCacheDefault);
// Check whether the cache has this getter already. // Check whether the cache has this getter already.
// We can use hasOwnProperty directly on the cache because we ensure, // We can use hasOwnProperty directly on the cache because we ensure,
// see below, that the cache never stores a path called 'hasOwnProperty' // see below, that the cache never stores a path called 'hasOwnProperty'
...@@ -11098,35 +11177,48 @@ function getterFn(path, options, fullExp) { ...@@ -11098,35 +11177,48 @@ function getterFn(path, options, fullExp) {
} }
} else { } else {
var code = 'var p;\n'; var code = 'var p;\n';
if (expensiveChecks) {
code += 's = eso(s, fe);\nl = eso(l, fe);\n';
}
var needsEnsureSafeObject = expensiveChecks;
forEach(pathKeys, function(key, index) { forEach(pathKeys, function(key, index) {
ensureSafeMemberName(key, fullExp); ensureSafeMemberName(key, fullExp);
code += 'if(s == null) return undefined;\n' + var lookupJs = (index
's='+ (index
// we simply dereference 's' on any .dot notation // we simply dereference 's' on any .dot notation
? 's' ? 's'
// but if we are first then we check locals first, and if so read it first // but if we are first then we check locals first, and if so read it first
: '((k&&k.hasOwnProperty("' + key + '"))?k:s)') + '["' + key + '"]' + ';\n' + : '((l&&l.hasOwnProperty("' + key + '"))?l:s)') + '["' + key + '"]';
(options.unwrapPromises var wrapWithEso = expensiveChecks || isPossiblyDangerousMemberName(key);
? 'if (s && s.then) {\n' + if (wrapWithEso) {
lookupJs = 'eso(' + lookupJs + ', fe)';
needsEnsureSafeObject = true;
}
code += 'if(s == null) return undefined;\n' +
's=' + lookupJs + ';\n';
if (options.unwrapPromises) {
code += 'if (s && s.then) {\n' +
' pw("' + fullExp.replace(/(["\r\n])/g, '\\$1') + '");\n' + ' pw("' + fullExp.replace(/(["\r\n])/g, '\\$1') + '");\n' +
' if (!("$$v" in s)) {\n' + ' if (!("$$v" in s)) {\n' +
' p=s;\n' + ' p=s;\n' +
' p.$$v = undefined;\n' + ' p.$$v = undefined;\n' +
' p.then(function(v) {p.$$v=v;});\n' + ' p.then(function(v) {p.$$v=' + (wrapWithEso ? 'eso(v)' : 'v') + ';});\n' +
'}\n' + '}\n' +
' s=s.$$v\n' + ' s=' + (wrapWithEso ? 'eso(s.$$v)' : 's.$$v') + '\n' +
'}\n' '}\n';
: '');
}
}); });
code += 'return s;'; code += 'return s;';
/* jshint -W054 */ /* jshint -W054 */
var evaledFnGetter = new Function('s', 'k', 'pw', code); // s=scope, k=locals, pw=promiseWarning // s=scope, l=locals, pw=promiseWarning, eso=ensureSafeObject, fe=fullExpression
var evaledFnGetter = new Function('s', 'l', 'pw', 'eso', 'fe', code);
/* jshint +W054 */ /* jshint +W054 */
evaledFnGetter.toString = valueFn(code); evaledFnGetter.toString = valueFn(code);
fn = options.unwrapPromises ? function(scope, locals) { if (needsEnsureSafeObject || options.unwrapPromises) {
return evaledFnGetter(scope, locals, promiseWarning); evaledFnGetter = getterFnWithExtraArgs(evaledFnGetter, fullExp);
} : evaledFnGetter; }
fn = evaledFnGetter;
} }
// Only cache the value if it's not going to mess up the cache object // Only cache the value if it's not going to mess up the cache object
...@@ -11190,12 +11282,14 @@ function getterFn(path, options, fullExp) { ...@@ -11190,12 +11282,14 @@ function getterFn(path, options, fullExp) {
* service. * service.
*/ */
function $ParseProvider() { function $ParseProvider() {
var cache = {}; var cacheDefault = {};
var cacheExpensive = {};
var $parseOptions = { var $parseOptions = {
csp: false, csp: false,
unwrapPromises: false, unwrapPromises: false,
logPromiseWarnings: true logPromiseWarnings: true,
expensiveChecks: false
}; };
...@@ -11282,6 +11376,12 @@ function $ParseProvider() { ...@@ -11282,6 +11376,12 @@ function $ParseProvider() {
this.$get = ['$filter', '$sniffer', '$log', function($filter, $sniffer, $log) { this.$get = ['$filter', '$sniffer', '$log', function($filter, $sniffer, $log) {
$parseOptions.csp = $sniffer.csp; $parseOptions.csp = $sniffer.csp;
var $parseOptionsExpensive = {
csp: $parseOptions.csp,
unwrapPromises: $parseOptions.unwrapPromises,
logPromiseWarnings: $parseOptions.logPromiseWarnings,
expensiveChecks: true
};
promiseWarning = function promiseWarningFn(fullExp) { promiseWarning = function promiseWarningFn(fullExp) {
if (!$parseOptions.logPromiseWarnings || promiseWarningCache.hasOwnProperty(fullExp)) return; if (!$parseOptions.logPromiseWarnings || promiseWarningCache.hasOwnProperty(fullExp)) return;
...@@ -11290,18 +11390,20 @@ function $ParseProvider() { ...@@ -11290,18 +11390,20 @@ function $ParseProvider() {
'Automatic unwrapping of promises in Angular expressions is deprecated.'); 'Automatic unwrapping of promises in Angular expressions is deprecated.');
}; };
return function(exp) { return function(exp, expensiveChecks) {
var parsedExpression; var parsedExpression;
switch (typeof exp) { switch (typeof exp) {
case 'string': case 'string':
var cache = (expensiveChecks ? cacheExpensive : cacheDefault);
if (cache.hasOwnProperty(exp)) { if (cache.hasOwnProperty(exp)) {
return cache[exp]; return cache[exp];
} }
var lexer = new Lexer($parseOptions); var parseOptions = expensiveChecks ? $parseOptionsExpensive : $parseOptions;
var parser = new Parser(lexer, $filter, $parseOptions); var lexer = new Lexer(parseOptions);
var parser = new Parser(lexer, $filter, parseOptions);
parsedExpression = parser.parse(exp); parsedExpression = parser.parse(exp);
if (exp !== 'hasOwnProperty') { if (exp !== 'hasOwnProperty') {
...@@ -11328,7 +11430,11 @@ function $ParseProvider() { ...@@ -11328,7 +11430,11 @@ function $ParseProvider() {
* @requires $rootScope * @requires $rootScope
* *
* @description * @description
* A promise/deferred implementation inspired by [Kris Kowal's Q](https://github.com/kriskowal/q). * A service that helps you run functions asynchronously, and use their return values (or exceptions)
* when they are done processing.
*
* This is an implementation of promises/deferred objects inspired by
* [Kris Kowal's Q](https://github.com/kriskowal/q).
* *
* [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an
* interface for interacting with an object that represents the result of an action that is * interface for interacting with an object that represents the result of an action that is
...@@ -11422,6 +11528,10 @@ function $ParseProvider() { ...@@ -11422,6 +11528,10 @@ function $ParseProvider() {
* *
* - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)` * - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)`
* *
* Because `catch` is a reserved word in JavaScript and reserved keywords are not supported as
* property names by ES3, you'll need to invoke the method like `promise['catch'](callback)` or
* `promise.then(null, errorCallback)` to make your code IE8 and Android 2.x compatible.
*
* - `finally(callback)` – allows you to observe either the fulfillment or rejection of a promise, * - `finally(callback)` – allows you to observe either the fulfillment or rejection of a promise,
* but to do so without modifying the final value. This is useful to release resources or do some * but to do so without modifying the final value. This is useful to release resources or do some
* clean-up that needs to be done whether the promise was rejected or resolved. See the [full * clean-up that needs to be done whether the promise was rejected or resolved. See the [full
...@@ -12853,8 +12963,11 @@ function $RootScopeProvider(){ ...@@ -12853,8 +12963,11 @@ function $RootScopeProvider(){
var self = this; var self = this;
return function() { return function() {
namedListeners[indexOf(namedListeners, listener)] = null; var indexOfListener = indexOf(namedListeners, listener);
decrementListenerCount(self, 1, name); if (indexOfListener !== -1) {
namedListeners[indexOfListener] = null;
decrementListenerCount(self, 1, name);
}
}; };
}, },
...@@ -14795,17 +14908,17 @@ function filterFilter() { ...@@ -14795,17 +14908,17 @@ function filterFilter() {
} }
var search = function(obj, text){ var search = function(obj, text){
if (typeof text == 'string' && text.charAt(0) === '!') { if (typeof text === 'string' && text.charAt(0) === '!') {
return !search(obj, text.substr(1)); return !search(obj, text.substr(1));
} }
switch (typeof obj) { switch (typeof obj) {
case "boolean": case 'boolean':
case "number": case 'number':
case "string": case 'string':
return comparator(obj, text); return comparator(obj, text);
case "object": case 'object':
switch (typeof text) { switch (typeof text) {
case "object": case 'object':
return comparator(obj, text); return comparator(obj, text);
default: default:
for ( var objKey in obj) { for ( var objKey in obj) {
...@@ -14816,7 +14929,7 @@ function filterFilter() { ...@@ -14816,7 +14929,7 @@ function filterFilter() {
break; break;
} }
return false; return false;
case "array": case 'array':
for ( var i = 0; i < obj.length; i++) { for ( var i = 0; i < obj.length; i++) {
if (search(obj[i], text)) { if (search(obj[i], text)) {
return true; return true;
...@@ -14828,13 +14941,13 @@ function filterFilter() { ...@@ -14828,13 +14941,13 @@ function filterFilter() {
} }
}; };
switch (typeof expression) { switch (typeof expression) {
case "boolean": case 'boolean':
case "number": case 'number':
case "string": case 'string':
// Set up expression object and fall through // Set up expression object and fall through
expression = {$:expression}; expression = {$:expression};
// jshint -W086 // jshint -W086
case "object": case 'object':
// jshint +W086 // jshint +W086
for (var key in expression) { for (var key in expression) {
(function(path) { (function(path) {
...@@ -15385,9 +15498,9 @@ var uppercaseFilter = valueFn(uppercase); ...@@ -15385,9 +15498,9 @@ var uppercaseFilter = valueFn(uppercase);
}]); }]);
</script> </script>
<div ng-controller="ExampleController"> <div ng-controller="ExampleController">
Limit {{numbers}} to: <input type="integer" ng-model="numLimit"> Limit {{numbers}} to: <input type="number" step="1" ng-model="numLimit">
<p>Output numbers: {{ numbers | limitTo:numLimit }}</p> <p>Output numbers: {{ numbers | limitTo:numLimit }}</p>
Limit {{letters}} to: <input type="integer" ng-model="letterLimit"> Limit {{letters}} to: <input type="number" step="1" ng-model="letterLimit">
<p>Output letters: {{ letters | limitTo:letterLimit }}</p> <p>Output letters: {{ letters | limitTo:letterLimit }}</p>
</div> </div>
</file> </file>
...@@ -15404,14 +15517,15 @@ var uppercaseFilter = valueFn(uppercase); ...@@ -15404,14 +15517,15 @@ var uppercaseFilter = valueFn(uppercase);
expect(limitedLetters.getText()).toEqual('Output letters: abc'); expect(limitedLetters.getText()).toEqual('Output letters: abc');
}); });
it('should update the output when -3 is entered', function() { // There is a bug in safari and protractor that doesn't like the minus key
numLimitInput.clear(); // it('should update the output when -3 is entered', function() {
numLimitInput.sendKeys('-3'); // numLimitInput.clear();
letterLimitInput.clear(); // numLimitInput.sendKeys('-3');
letterLimitInput.sendKeys('-3'); // letterLimitInput.clear();
expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]'); // letterLimitInput.sendKeys('-3');
expect(limitedLetters.getText()).toEqual('Output letters: ghi'); // expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]');
}); // expect(limitedLetters.getText()).toEqual('Output letters: ghi');
// });
it('should not exceed the maximum size of input array', function() { it('should not exceed the maximum size of input array', function() {
numLimitInput.clear(); numLimitInput.clear();
...@@ -15479,7 +15593,7 @@ function limitToFilter(){ ...@@ -15479,7 +15593,7 @@ function limitToFilter(){
* correctly, make sure they are actually being saved as numbers and not strings. * correctly, make sure they are actually being saved as numbers and not strings.
* *
* @param {Array} array The array to sort. * @param {Array} array The array to sort.
* @param {function(*)|string|Array.<(function(*)|string)>} expression A predicate to be * @param {function(*)|string|Array.<(function(*)|string)>=} expression A predicate to be
* used by the comparator to determine the order of elements. * used by the comparator to determine the order of elements.
* *
* Can be one of: * Can be one of:
...@@ -15492,10 +15606,13 @@ function limitToFilter(){ ...@@ -15492,10 +15606,13 @@ function limitToFilter(){
* is interpreted as a property name to be used in comparisons (for example `"special name"` * is interpreted as a property name to be used in comparisons (for example `"special name"`
* to sort object by the value of their `special name` property). An expression can be * to sort object by the value of their `special name` property). An expression can be
* optionally prefixed with `+` or `-` to control ascending or descending sort order * optionally prefixed with `+` or `-` to control ascending or descending sort order
* (for example, `+name` or `-name`). * (for example, `+name` or `-name`). If no property is provided, (e.g. `'+'`) then the array
* element itself is used to compare where sorting.
* - `Array`: An array of function or string predicates. The first predicate in the array * - `Array`: An array of function or string predicates. The first predicate in the array
* is used for sorting, but when two items are equivalent, the next predicate is used. * is used for sorting, but when two items are equivalent, the next predicate is used.
* *
* If the predicate is missing or empty then it defaults to `'+'`.
*
* @param {boolean=} reverse Reverse the order of the array. * @param {boolean=} reverse Reverse the order of the array.
* @returns {Array} Sorted copy of the source array. * @returns {Array} Sorted copy of the source array.
* *
...@@ -15584,8 +15701,8 @@ orderByFilter.$inject = ['$parse']; ...@@ -15584,8 +15701,8 @@ orderByFilter.$inject = ['$parse'];
function orderByFilter($parse){ function orderByFilter($parse){
return function(array, sortPredicate, reverseOrder) { return function(array, sortPredicate, reverseOrder) {
if (!(isArrayLike(array))) return array; if (!(isArrayLike(array))) return array;
if (!sortPredicate) return array;
sortPredicate = isArray(sortPredicate) ? sortPredicate: [sortPredicate]; sortPredicate = isArray(sortPredicate) ? sortPredicate: [sortPredicate];
if (sortPredicate.length === 0) { sortPredicate = ['+']; }
sortPredicate = map(sortPredicate, function(predicate){ sortPredicate = map(sortPredicate, function(predicate){
var descending = false, get = predicate || identity; var descending = false, get = predicate || identity;
if (isString(predicate)) { if (isString(predicate)) {
...@@ -15593,6 +15710,12 @@ function orderByFilter($parse){ ...@@ -15593,6 +15710,12 @@ function orderByFilter($parse){
descending = predicate.charAt(0) == '-'; descending = predicate.charAt(0) == '-';
predicate = predicate.substring(1); predicate = predicate.substring(1);
} }
if ( predicate === '' ) {
// Effectively no predicate was passed so we compare identity
return reverseComparator(function(a,b) {
return compare(a, b);
}, descending);
}
get = $parse(predicate); get = $parse(predicate);
if (get.constant) { if (get.constant) {
var key = get(); var key = get();
...@@ -15605,9 +15728,7 @@ function orderByFilter($parse){ ...@@ -15605,9 +15728,7 @@ function orderByFilter($parse){
return compare(get(a),get(b)); return compare(get(a),get(b));
}, descending); }, descending);
}); });
var arrayCopy = []; return slice.call(array).sort(reverseComparator(comparator, reverseOrder));
for ( var i = 0; i < array.length; i++) { arrayCopy.push(array[i]); }
return arrayCopy.sort(reverseComparator(comparator, reverseOrder));
function comparator(o1, o2){ function comparator(o1, o2){
for ( var i = 0; i < sortPredicate.length; i++) { for ( var i = 0; i < sortPredicate.length; i++) {
...@@ -15711,9 +15832,8 @@ var htmlAnchorDirective = valueFn({ ...@@ -15711,9 +15832,8 @@ var htmlAnchorDirective = valueFn({
* make the link go to the wrong URL if the user clicks it before * make the link go to the wrong URL if the user clicks it before
* Angular has a chance to replace the `{{hash}}` markup with its * Angular has a chance to replace the `{{hash}}` markup with its
* value. Until Angular replaces the markup the link will be broken * value. Until Angular replaces the markup the link will be broken
* and will most likely return a 404 error. * and will most likely return a 404 error. The `ngHref` directive
* * solves this problem.
* The `ngHref` directive solves this problem.
* *
* The wrong way to write it: * The wrong way to write it:
* ```html * ```html
...@@ -17496,7 +17616,7 @@ var VALID_CLASS = 'ng-valid', ...@@ -17496,7 +17616,7 @@ var VALID_CLASS = 'ng-valid',
* *
* We are using the {@link ng.service:$sce $sce} service here and include the {@link ngSanitize $sanitize} * We are using the {@link ng.service:$sce $sce} service here and include the {@link ngSanitize $sanitize}
* module to automatically remove "bad" content like inline event listener (e.g. `<span onclick="...">`). * module to automatically remove "bad" content like inline event listener (e.g. `<span onclick="...">`).
* However, as we are using `$sce` the model can still decide to to provide unsafe content if it marks * However, as we are using `$sce` the model can still decide to provide unsafe content if it marks
* that content using the `$sce` service. * that content using the `$sce` service.
* *
* <example name="NgModelController" module="customControl" deps="angular-sanitize.js"> * <example name="NgModelController" module="customControl" deps="angular-sanitize.js">
...@@ -17528,7 +17648,7 @@ var VALID_CLASS = 'ng-valid', ...@@ -17528,7 +17648,7 @@ var VALID_CLASS = 'ng-valid',
// Listen for change events to enable binding // Listen for change events to enable binding
element.on('blur keyup change', function() { element.on('blur keyup change', function() {
scope.$apply(read); scope.$evalAsync(read);
}); });
read(); // initialize read(); // initialize
...@@ -17811,7 +17931,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$ ...@@ -17811,7 +17931,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
* *
* For best practices on using `ngModel`, see: * For best practices on using `ngModel`, see:
* *
* - [https://github.com/angular/angular.js/wiki/Understanding-Scopes] * - [Understanding Scopes](https://github.com/angular/angular.js/wiki/Understanding-Scopes)
* *
* For basic examples, how to use `ngModel`, see: * For basic examples, how to use `ngModel`, see:
* *
...@@ -18311,7 +18431,10 @@ var ngBindTemplateDirective = ['$interpolate', function($interpolate) { ...@@ -18311,7 +18431,10 @@ var ngBindTemplateDirective = ['$interpolate', function($interpolate) {
* element in a secure way. By default, the innerHTML-ed content will be sanitized using the {@link * element in a secure way. By default, the innerHTML-ed content will be sanitized using the {@link
* ngSanitize.$sanitize $sanitize} service. To utilize this functionality, ensure that `$sanitize` * ngSanitize.$sanitize $sanitize} service. To utilize this functionality, ensure that `$sanitize`
* is available, for example, by including {@link ngSanitize} in your module's dependencies (not in * is available, for example, by including {@link ngSanitize} in your module's dependencies (not in
* core Angular.) You may also bypass sanitization for values you know are safe. To do so, bind to * core Angular). In order to use {@link ngSanitize} in your module's dependencies, you need to
* include "angular-sanitize.js" in your application.
*
* You may also bypass sanitization for values you know are safe. To do so, bind to
* an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}. See the example * an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}. See the example
* under {@link ng.$sce#Example Strict Contextual Escaping (SCE)}. * under {@link ng.$sce#Example Strict Contextual Escaping (SCE)}.
* *
...@@ -18813,6 +18936,7 @@ var ngCloakDirective = ngDirective({ ...@@ -18813,6 +18936,7 @@ var ngCloakDirective = ngDirective({
* *
* @element ANY * @element ANY
* @scope * @scope
* @priority 500
* @param {expression} ngController Name of a globally accessible constructor function or an * @param {expression} ngController Name of a globally accessible constructor function or an
* {@link guide/expression expression} that on the current scope evaluates to a * {@link guide/expression expression} that on the current scope evaluates to a
* constructor function. The controller instance can be published into a scope property * constructor function. The controller instance can be published into a scope property
...@@ -19105,10 +19229,8 @@ var ngControllerDirective = [function() { ...@@ -19105,10 +19229,8 @@ var ngControllerDirective = [function() {
</example> </example>
*/ */
/* /*
* A directive that allows creation of custom onclick handlers that are defined as angular * A collection of directives that allows creation of custom event handlers that are defined as
* expressions and are compiled and executed within the current scope. * angular expressions and are compiled and executed within the current scope.
*
* Events that are handled via these handler are always configured not to propagate further.
*/ */
var ngEventDirectives = {}; var ngEventDirectives = {};
...@@ -19126,7 +19248,11 @@ forEach( ...@@ -19126,7 +19248,11 @@ forEach(
ngEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) { ngEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) {
return { return {
compile: function($element, attr) { compile: function($element, attr) {
var fn = $parse(attr[directiveName]); // We expose the powerful $event object on the scope that provides access to the Window,
// etc. that isn't protected by the fast paths in $parse. We explicitly request better
// checks at the cost of speed since event handler expressions are not executed as
// frequently as regular change detection.
var fn = $parse(attr[directiveName], /* expensiveChecks */ true);
return function ngEventHandler(scope, element) { return function ngEventHandler(scope, element) {
element.on(eventName, function(event) { element.on(eventName, function(event) {
var callback = function() { var callback = function() {
...@@ -19572,7 +19698,7 @@ forEach( ...@@ -19572,7 +19698,7 @@ forEach(
* Note that when an element is removed using `ngIf` its scope is destroyed and a new scope * Note that when an element is removed using `ngIf` its scope is destroyed and a new scope
* is created when the element is restored. The scope created within `ngIf` inherits from * is created when the element is restored. The scope created within `ngIf` inherits from
* its parent scope using * its parent scope using
* [prototypal inheritance](https://github.com/angular/angular.js/wiki/The-Nuances-of-Scope-Prototypal-Inheritance). * [prototypal inheritance](https://github.com/angular/angular.js/wiki/Understanding-Scopes#javascript-prototypal-inheritance).
* An important implication of this is if `ngModel` is used within `ngIf` to bind to * An important implication of this is if `ngModel` is used within `ngIf` to bind to
* a javascript primitive defined in the parent scope. In this case any modifications made to the * a javascript primitive defined in the parent scope. In this case any modifications made to the
* variable within the child scope will override (hide) the value in the parent scope. * variable within the child scope will override (hide) the value in the parent scope.
...@@ -19586,8 +19712,8 @@ forEach( ...@@ -19586,8 +19712,8 @@ forEach(
* and `leave` effects. * and `leave` effects.
* *
* @animations * @animations
* enter - happens just after the ngIf contents change and a new DOM element is created and injected into the ngIf container * 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 * leave - happens just before the `ngIf` contents are removed from the DOM
* *
* @element ANY * @element ANY
* @scope * @scope
...@@ -21327,7 +21453,6 @@ var scriptDirective = ['$templateCache', function($templateCache) { ...@@ -21327,7 +21453,6 @@ var scriptDirective = ['$templateCache', function($templateCache) {
compile: function(element, attr) { compile: function(element, attr) {
if (attr.type == 'text/ng-template') { if (attr.type == 'text/ng-template') {
var templateUrl = attr.id, var templateUrl = attr.id,
// IE is not consistent, in scripts we have to read .text but in other nodes we have to read .textContent
text = element[0].text; text = element[0].text;
$templateCache.put(templateUrl, text); $templateCache.put(templateUrl, text);
...@@ -21887,6 +22012,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) { ...@@ -21887,6 +22012,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
lastElement = existingOption.element; lastElement = existingOption.element;
if (existingOption.label !== option.label) { if (existingOption.label !== option.label) {
lastElement.text(existingOption.label = option.label); lastElement.text(existingOption.label = option.label);
lastElement.prop('label', existingOption.label);
} }
if (existingOption.id !== option.id) { if (existingOption.id !== option.id) {
lastElement.val(existingOption.id = option.id); lastElement.val(existingOption.id = option.id);
...@@ -21916,6 +22042,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) { ...@@ -21916,6 +22042,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
.val(option.id) .val(option.id)
.prop('selected', option.selected) .prop('selected', option.selected)
.attr('selected', option.selected) .attr('selected', option.selected)
.prop('label', option.label)
.text(option.label); .text(option.label);
} }
...@@ -21925,6 +22052,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) { ...@@ -21925,6 +22052,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
id: option.id, id: option.id,
selected: option.selected selected: option.selected
}); });
selectCtrl.addOption(option.label, element);
if (lastElement) { if (lastElement) {
lastElement.after(element); lastElement.after(element);
} else { } else {
...@@ -21936,7 +22064,9 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) { ...@@ -21936,7 +22064,9 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
// remove any excessive OPTIONs in a group // remove any excessive OPTIONs in a group
index++; // increment since the existingOptions[0] is parent element not OPTION index++; // increment since the existingOptions[0] is parent element not OPTION
while(existingOptions.length > index) { while(existingOptions.length > index) {
existingOptions.pop().element.remove(); option = existingOptions.pop();
selectCtrl.removeOption(option.label);
option.element.remove();
} }
} }
// remove any excessive OPTGROUPs from select // remove any excessive OPTGROUPs from select
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment