From ecceda68e650800a42168b0998ffc771e9a49133 Mon Sep 17 00:00:00 2001 From: MichaelDaum Date: Mon, 24 Nov 2014 13:45:14 +0100 Subject: [PATCH] Item13033: updated to angular-1.3.4 --- data/System/AngularPlugin.txt | 2 +- lib/Foswiki/Plugins/AngularPlugin/Animate.pm | 2 +- lib/Foswiki/Plugins/AngularPlugin/Aria.pm | 2 +- lib/Foswiki/Plugins/AngularPlugin/Cookies.pm | 2 +- lib/Foswiki/Plugins/AngularPlugin/Core.pm | 2 +- lib/Foswiki/Plugins/AngularPlugin/Loader.pm | 2 +- lib/Foswiki/Plugins/AngularPlugin/Messages.pm | 2 +- lib/Foswiki/Plugins/AngularPlugin/Mocks.pm | 2 +- lib/Foswiki/Plugins/AngularPlugin/Resource.pm | 2 +- lib/Foswiki/Plugins/AngularPlugin/Route.pm | 2 +- lib/Foswiki/Plugins/AngularPlugin/Sanitize.pm | 2 +- lib/Foswiki/Plugins/AngularPlugin/Scenario.pm | 2 +- lib/Foswiki/Plugins/AngularPlugin/Touch.pm | 2 +- .../AngularPlugin/angular.uncompressed.js | 914 +++++++++++------- .../ngAnimate/angular-animate.uncompressed.js | 28 +- .../ngAria/angular-aria.uncompressed.js | 106 +- 16 files changed, 658 insertions(+), 416 deletions(-) diff --git a/data/System/AngularPlugin.txt b/data/System/AngularPlugin.txt index c5ebea7..40e5af4 100644 --- a/data/System/AngularPlugin.txt +++ b/data/System/AngularPlugin.txt @@ -1,4 +1,4 @@ -%META:TOPICINFO{author="micha" comment="reprev" date="1414482295" format="1.1" reprev="2" version="3"}% +%META:TOPICINFO{author="ProjectContributor" comment="" date="1416833083" format="1.1" version="1"}% ---+!! %TOPIC% %SHORTDESCRIPTION% diff --git a/lib/Foswiki/Plugins/AngularPlugin/Animate.pm b/lib/Foswiki/Plugins/AngularPlugin/Animate.pm index 86c9ac2..804a0d4 100644 --- a/lib/Foswiki/Plugins/AngularPlugin/Animate.pm +++ b/lib/Foswiki/Plugins/AngularPlugin/Animate.pm @@ -13,7 +13,7 @@ sub new { my $this = bless( $class->SUPER::new( name => 'ngAnimate', - version => '1.3.3', + version => '1.3.4', author => 'Brat Tech LLC, Google and community', homepage => 'https://angularjs.org', javascript => ['angular-animate.js', ], diff --git a/lib/Foswiki/Plugins/AngularPlugin/Aria.pm b/lib/Foswiki/Plugins/AngularPlugin/Aria.pm index 8948dca..62ca54d 100644 --- a/lib/Foswiki/Plugins/AngularPlugin/Aria.pm +++ b/lib/Foswiki/Plugins/AngularPlugin/Aria.pm @@ -13,7 +13,7 @@ sub new { my $this = bless( $class->SUPER::new( name => 'ngAria', - version => '1.3.3', + version => '1.3.4', author => 'Brat Tech LLC, Google and community', homepage => 'https://angularjs.org', javascript => ['angular-aria.js', ], diff --git a/lib/Foswiki/Plugins/AngularPlugin/Cookies.pm b/lib/Foswiki/Plugins/AngularPlugin/Cookies.pm index b992cf5..3f1f8dc 100644 --- a/lib/Foswiki/Plugins/AngularPlugin/Cookies.pm +++ b/lib/Foswiki/Plugins/AngularPlugin/Cookies.pm @@ -13,7 +13,7 @@ sub new { my $this = bless( $class->SUPER::new( name => 'ngCookies', - version => '1.3.3', + version => '1.3.4', author => 'Brat Tech LLC, Google and community', homepage => 'https://angularjs.org', javascript => ['angular-cookies.js', ], diff --git a/lib/Foswiki/Plugins/AngularPlugin/Core.pm b/lib/Foswiki/Plugins/AngularPlugin/Core.pm index c5f34c0..cdce5a1 100644 --- a/lib/Foswiki/Plugins/AngularPlugin/Core.pm +++ b/lib/Foswiki/Plugins/AngularPlugin/Core.pm @@ -32,7 +32,7 @@ sub new { my $this = bless( $class->SUPER::new( name => 'ngCore', - version => '1.3.3', + version => '1.3.4', author => 'Brat Tech LLC, Google and community', homepage => 'https://angularjs.org', javascript => ['angular.js', ], diff --git a/lib/Foswiki/Plugins/AngularPlugin/Loader.pm b/lib/Foswiki/Plugins/AngularPlugin/Loader.pm index 7713c47..755c82b 100644 --- a/lib/Foswiki/Plugins/AngularPlugin/Loader.pm +++ b/lib/Foswiki/Plugins/AngularPlugin/Loader.pm @@ -13,7 +13,7 @@ sub new { my $this = bless( $class->SUPER::new( name => 'ngLoader', - version => '1.3.3', + version => '1.3.4', author => 'Brat Tech LLC, Google and community', homepage => 'https://angularjs.org', javascript => ['angular-loader.js', ], diff --git a/lib/Foswiki/Plugins/AngularPlugin/Messages.pm b/lib/Foswiki/Plugins/AngularPlugin/Messages.pm index 92ab064..f88ad77 100644 --- a/lib/Foswiki/Plugins/AngularPlugin/Messages.pm +++ b/lib/Foswiki/Plugins/AngularPlugin/Messages.pm @@ -13,7 +13,7 @@ sub new { my $this = bless( $class->SUPER::new( name => 'ngMessages', - version => '1.3.3', + version => '1.3.4', author => 'Brat Tech LLC, Google and community', homepage => 'https://angularjs.org', javascript => ['angular-messages.js', ], diff --git a/lib/Foswiki/Plugins/AngularPlugin/Mocks.pm b/lib/Foswiki/Plugins/AngularPlugin/Mocks.pm index cf82a39..64629eb 100644 --- a/lib/Foswiki/Plugins/AngularPlugin/Mocks.pm +++ b/lib/Foswiki/Plugins/AngularPlugin/Mocks.pm @@ -13,7 +13,7 @@ sub new { my $this = bless( $class->SUPER::new( name => 'ngMocks', - version => '1.3.3', + version => '1.3.4', author => 'Brat Tech LLC, Google and community', homepage => 'https://angularjs.org', javascript => ['angular-mocks.js', ], diff --git a/lib/Foswiki/Plugins/AngularPlugin/Resource.pm b/lib/Foswiki/Plugins/AngularPlugin/Resource.pm index af011b1..10443b6 100644 --- a/lib/Foswiki/Plugins/AngularPlugin/Resource.pm +++ b/lib/Foswiki/Plugins/AngularPlugin/Resource.pm @@ -13,7 +13,7 @@ sub new { my $this = bless( $class->SUPER::new( name => 'ngResource', - version => '1.3.3', + version => '1.3.4', author => 'Brat Tech LLC, Google and community', homepage => 'https://angularjs.org', javascript => ['angular-resource.js', ], diff --git a/lib/Foswiki/Plugins/AngularPlugin/Route.pm b/lib/Foswiki/Plugins/AngularPlugin/Route.pm index d1e1f27..0026bef 100644 --- a/lib/Foswiki/Plugins/AngularPlugin/Route.pm +++ b/lib/Foswiki/Plugins/AngularPlugin/Route.pm @@ -13,7 +13,7 @@ sub new { my $this = bless( $class->SUPER::new( name => 'ngRoute', - version => '1.3.3', + version => '1.3.4', author => 'Brat Tech LLC, Google and community', homepage => 'https://angularjs.org', javascript => ['angular-route.js', ], diff --git a/lib/Foswiki/Plugins/AngularPlugin/Sanitize.pm b/lib/Foswiki/Plugins/AngularPlugin/Sanitize.pm index 35dd7b0..ce92cdc 100644 --- a/lib/Foswiki/Plugins/AngularPlugin/Sanitize.pm +++ b/lib/Foswiki/Plugins/AngularPlugin/Sanitize.pm @@ -13,7 +13,7 @@ sub new { my $this = bless( $class->SUPER::new( name => 'ngSanitize', - version => '1.3.3', + version => '1.3.4', author => 'Brat Tech LLC, Google and community', homepage => 'https://angularjs.org', javascript => ['angular-sanitize.js', ], diff --git a/lib/Foswiki/Plugins/AngularPlugin/Scenario.pm b/lib/Foswiki/Plugins/AngularPlugin/Scenario.pm index dc347c1..f9fd0ea 100644 --- a/lib/Foswiki/Plugins/AngularPlugin/Scenario.pm +++ b/lib/Foswiki/Plugins/AngularPlugin/Scenario.pm @@ -13,7 +13,7 @@ sub new { my $this = bless( $class->SUPER::new( name => 'ngScenario', - version => '1.3.3', + version => '1.3.4', author => 'Brat Tech LLC, Google and community', homepage => 'https://angularjs.org', javascript => ['angular-scenario.js', ], diff --git a/lib/Foswiki/Plugins/AngularPlugin/Touch.pm b/lib/Foswiki/Plugins/AngularPlugin/Touch.pm index d789567..6c9c86f 100644 --- a/lib/Foswiki/Plugins/AngularPlugin/Touch.pm +++ b/lib/Foswiki/Plugins/AngularPlugin/Touch.pm @@ -13,7 +13,7 @@ sub new { my $this = bless( $class->SUPER::new( name => 'ngTouch', - version => '1.3.3', + version => '1.3.4', author => 'Brat Tech LLC, Google and community', homepage => 'https://angularjs.org', javascript => ['angular-touch.js', ], diff --git a/pub/System/AngularPlugin/angular.uncompressed.js b/pub/System/AngularPlugin/angular.uncompressed.js index e6b8b27..5a42772 100644 --- a/pub/System/AngularPlugin/angular.uncompressed.js +++ b/pub/System/AngularPlugin/angular.uncompressed.js @@ -1,5 +1,5 @@ /** - * @license AngularJS v1.3.3-local+sha.b7afd11 + * @license AngularJS v1.3.4-local+sha.bf6a79c * (c) 2010-2014 Google, Inc. http://angularjs.org * License: MIT */ @@ -42,40 +42,23 @@ function minErr(module, ErrorConstructor) { prefix = '[' + (module ? module + ':' : '') + code + '] ', template = arguments[1], templateArgs = arguments, - stringify = function(obj) { - if (typeof obj === 'function') { - return obj.toString().replace(/ \{[\s\S]*$/, ''); - } else if (typeof obj === 'undefined') { - return 'undefined'; - } else if (typeof obj !== 'string') { - return JSON.stringify(obj); - } - return obj; - }, + message, i; message = prefix + template.replace(/\{\d+\}/g, function(match) { var index = +match.slice(1, -1), arg; if (index + 2 < templateArgs.length) { - arg = templateArgs[index + 2]; - if (typeof arg === 'function') { - return arg.toString().replace(/ ?\{[\s\S]*$/, ''); - } else if (typeof arg === 'undefined') { - return 'undefined'; - } else if (typeof arg !== 'string') { - return toJson(arg); - } - return arg; + return toDebugString(templateArgs[index + 2]); } return match; }); - message = message + '\nhttp://errors.angularjs.org/1.3.3-local+sha.b7afd11/' + + message = message + '\nhttp://errors.angularjs.org/1.3.4-local+sha.bf6a79c/' + (module ? module + '/' : '') + code; for (i = 2; i < arguments.length; i++) { message = message + (i == 2 ? '?' : '&') + 'p' + (i - 2) + '=' + - encodeURIComponent(stringify(arguments[i])); + encodeURIComponent(toDebugString(arguments[i])); } return new ErrorConstructor(message); }; @@ -443,7 +426,7 @@ function int(str) { function inherit(parent, extra) { - return extend(new (extend(function() {}, {prototype:parent}))(), extra); + return extend(Object.create(parent), extra); } /** @@ -1002,7 +985,7 @@ function bind(self, fn) { return curryArgs.length ? function() { return arguments.length - ? fn.apply(self, curryArgs.concat(slice.call(arguments, 0))) + ? fn.apply(self, concat(curryArgs, arguments, 0)) : fn.apply(self, curryArgs); } : function() { @@ -1412,8 +1395,8 @@ function angularInit(element, bootstrap) { * @param {Object=} config an object for defining configuration options for the application. The * following keys are supported: * - * - `strictDi`: disable automatic function annotation for the application. This is meant to - * assist in finding bugs which break minified code. + * * `strictDi` - disable automatic function annotation for the application. This is meant to + * assist in finding bugs which break minified code. Defaults to `false`. * * @returns {auto.$injector} Returns the newly created injector for this app. */ @@ -1987,6 +1970,34 @@ function setupModuleLoader(window) { } +/* global: toDebugString: true */ + +function serializeObject(obj) { + var seen = []; + + return JSON.stringify(obj, function(key, val) { + val = toJsonReplacer(key, val); + if (isObject(val)) { + + if (seen.indexOf(val) >= 0) return '<>'; + + seen.push(val); + } + return val; + }); +} + +function toDebugString(obj) { + if (typeof obj === 'function') { + return obj.toString().replace(/ \{[\s\S]*$/, ''); + } else if (typeof obj === 'undefined') { + return 'undefined'; + } else if (typeof obj !== 'string') { + return serializeObject(obj); + } + return obj; +} + /* global angularModule: true, version: true, @@ -2089,10 +2100,10 @@ function setupModuleLoader(window) { * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat". */ var version = { - full: '1.3.3-local+sha.b7afd11', // all of these placeholder strings will be replaced by grunt's + full: '1.3.4-local+sha.bf6a79c', // all of these placeholder strings will be replaced by grunt's major: 1, // package task minor: 3, - dot: 3, + dot: 4, codeName: 'snapshot' }; @@ -2316,10 +2327,12 @@ function publishExternalAPI(angular) { * `'ngModel'`). * - `injector()` - retrieves the injector of the current element or its parent. * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current - * element or its parent. + * element or its parent. Requires {@link guide/production#disabling-debug-data Debug Data} to + * be enabled. * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the * current element. This getter should be used only on elements that contain a directive which starts a new isolate * scope. Calling `scope()` on this element always returns the original non-isolate scope. + * Requires {@link guide/production#disabling-debug-data Debug Data} to be enabled. * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top * parent element is reached. * @@ -3314,9 +3327,10 @@ HashMap.prototype = { * Creates an injector object that can be used for retrieving services as well as for * dependency injection (see {@link guide/di dependency injection}). * - * @param {Array.} 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. + * @param {boolean=} [strictDi=false] Whether the injector should be in strict mode, which + * disallows argument name annotation inference. * @returns {injector} Injector object. See {@link auto.$injector $injector}. * * @example @@ -3462,8 +3476,10 @@ function annotate(fn, strictDi, name) { * ## Inference * * In JavaScript calling `toString()` on a function returns the function definition. The definition - * can then be parsed and the function arguments can be extracted. *NOTE:* This does not work with - * minification, and obfuscation tools since these tools change the argument names. + * can then be parsed and the function arguments can be extracted. This method of discovering + * annotations is disallowed when the injector is in strict mode. + * *NOTE:* This does not work with minification, and obfuscation tools since these tools change the + * argument names. * * ## `$inject` Annotation * By adding an `$inject` property onto a function the injection parameters can be specified. @@ -3548,6 +3564,8 @@ function annotate(fn, strictDi, name) { * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']); * ``` * + * You can disallow this method by using strict injection mode. + * * This method does not work with code minification / obfuscation. For this reason the following * annotation strategies are supported. * @@ -3600,6 +3618,8 @@ function annotate(fn, strictDi, name) { * @param {Function|Array.} fn Function for which dependent service names need to * be retrieved as described above. * + * @param {boolean=} [strictDi=false] Disallow argument name annotation inference. + * * @returns {Array.} The names of the services which the function requires. */ @@ -4119,14 +4139,11 @@ function createInjector(modulesToLoad, strictDi) { } function instantiate(Type, locals, serviceName) { - var Constructor = function() {}, - instance, returnedValue; - // 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) {}]); - Constructor.prototype = (isArray(Type) ? Type[Type.length - 1] : Type).prototype; - instance = new Constructor(); - returnedValue = invoke(Type, instance, locals, serviceName); + // Object creation: http://jsperf.com/create-constructor/2 + var instance = Object.create((isArray(Type) ? Type[Type.length - 1] : Type).prototype); + var returnedValue = invoke(Type, instance, locals, serviceName); return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance; } @@ -4162,7 +4179,7 @@ function $AnchorScrollProvider() { * @name $anchorScrollProvider#disableAutoScrolling * * @description - * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically will detect changes to + * 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.
* Use this method to disable automatic scrolling. * @@ -4962,7 +4979,7 @@ function Browser(window, document, $log, $sniffer) { // IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode. // See https://github.com/angular/angular.js/commit/ffb2701 if (lastBrowserUrl === url && (!$sniffer.history || sameState)) { - return; + return self; } var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url); lastBrowserUrl = url; @@ -7874,10 +7891,10 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { var childBoundTranscludeFn = boundTranscludeFn; if (scope.$$destroyed) return; if (linkQueue) { - linkQueue.push(scope); - linkQueue.push(node); - linkQueue.push(rootElement); - linkQueue.push(childBoundTranscludeFn); + linkQueue.push(scope, + node, + rootElement, + childBoundTranscludeFn); } else { if (afterTemplateNodeLinkFn.transcludeOnThisElement) { childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn); @@ -8297,6 +8314,10 @@ function $ControllerProvider() { * * if $controllerProvider#allowGlobals, check `window[constructor]` on the global * `window` object (not recommended) * + * The string can use the `controller as property` syntax, where the controller instance is published + * as the specified property on the `scope`; the `scope` must be injected into `locals` param for this + * to work correctly. + * * @param {Object} locals Injection locals for Controller. * @return {Object} Instance of given controller. * @@ -8342,10 +8363,10 @@ function $ControllerProvider() { // // This feature is not intended for use by applications, and is thus not documented // publicly. - var Constructor = function() {}; - Constructor.prototype = (isArray(expression) ? + // Object creation: http://jsperf.com/create-constructor/2 + var controllerPrototype = (isArray(expression) ? expression[expression.length - 1] : expression).prototype; - instance = new Constructor(); + instance = Object.create(controllerPrototype); if (identifier) { addIdentifier(locals, identifier, instance, constructor || expression.name); @@ -8471,7 +8492,7 @@ function defaultHttpResponseTransform(data, headers) { // strip json vulnerability protection prefix data = data.replace(JSON_PROTECTION_PREFIX, ''); var contentType = headers('Content-Type'); - if ((contentType && contentType.indexOf(APPLICATION_JSON) === 0) || + if ((contentType && contentType.indexOf(APPLICATION_JSON) === 0 && data.trim()) || (JSON_START.test(data) && JSON_END.test(data))) { data = fromJson(data); } @@ -8486,7 +8507,7 @@ function defaultHttpResponseTransform(data, headers) { * @returns {Object} Parsed headers as key value object */ function parseHeaders(headers) { - var parsed = {}, key, val, i; + var parsed = createMap(), key, val, i; if (!headers) return parsed; @@ -8523,7 +8544,11 @@ function headersGetter(headers) { if (!headersObj) headersObj = parseHeaders(headers); if (name) { - return headersObj[lowercase(name)] || null; + var value = headersObj[lowercase(name)]; + if (value === void 0) { + value = null; + } + return value; } return headersObj; @@ -8572,6 +8597,11 @@ function $HttpProvider() { * * Object containing default values for all {@link ng.$http $http} requests. * + * - **`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 `default.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'`. * @@ -8585,6 +8615,7 @@ function $HttpProvider() { * - **`defaults.headers.post`** * - **`defaults.headers.put`** * - **`defaults.headers.patch`** + * **/ var defaults = this.defaults = { // transform incoming response data @@ -8799,6 +8830,21 @@ function $HttpProvider() { * 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. * + * 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 * @@ -9178,6 +9224,10 @@ function $HttpProvider() { }; var headers = mergeHeaders(requestConfig); + if (!angular.isObject(requestConfig)) { + throw minErr('$http')('badreq', 'Http request configuration must be an object. Received: {0}', requestConfig); + } + extend(config, requestConfig); config.headers = headers; config.method = uppercase(config.method); @@ -10672,6 +10722,13 @@ var locationPrototype = { * Return full url representation with all segments encoded according to rules specified in * [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt). * + * + * ```js + * // given url http://example.com/#/some/path?foo=bar&baz=xoxo + * var absUrl = $location.absUrl(); + * // => "http://example.com/#/some/path?foo=bar&baz=xoxo" + * ``` + * * @return {string} full url */ absUrl: locationGetter('$$absUrl'), @@ -10687,6 +10744,13 @@ var locationPrototype = { * * Change path, search and hash, when called with parameter and return `$location`. * + * + * ```js + * // given url http://example.com/#/some/path?foo=bar&baz=xoxo + * var url = $location.url(); + * // => "/some/path?foo=bar&baz=xoxo" + * ``` + * * @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`) * @return {string} url */ @@ -10695,8 +10759,8 @@ var locationPrototype = { return this.$$url; var match = PATH_MATCH.exec(url); - if (match[1]) this.path(decodeURIComponent(match[1])); - if (match[2] || match[1]) this.search(match[3] || ''); + if (match[1] || url === '') this.path(decodeURIComponent(match[1])); + if (match[2] || match[1] || url === '') this.search(match[3] || ''); this.hash(match[5] || ''); return this; @@ -10711,6 +10775,13 @@ var locationPrototype = { * * Return protocol of current url. * + * + * ```js + * // given url http://example.com/#/some/path?foo=bar&baz=xoxo + * var protocol = $location.protocol(); + * // => "http" + * ``` + * * @return {string} protocol of current url */ protocol: locationGetter('$$protocol'), @@ -10724,6 +10795,13 @@ var locationPrototype = { * * Return host of current url. * + * + * ```js + * // given url http://example.com/#/some/path?foo=bar&baz=xoxo + * var host = $location.host(); + * // => "example.com" + * ``` + * * @return {string} host of current url. */ host: locationGetter('$$host'), @@ -10737,6 +10815,13 @@ var locationPrototype = { * * Return port of current url. * + * + * ```js + * // given url http://example.com/#/some/path?foo=bar&baz=xoxo + * var port = $location.port(); + * // => 80 + * ``` + * * @return {Number} port */ port: locationGetter('$$port'), @@ -10755,6 +10840,13 @@ var locationPrototype = { * Note: Path should always begin with forward slash (/), this method will add the forward slash * if it is missing. * + * + * ```js + * // given url http://example.com/#/some/path?foo=bar&baz=xoxo + * var path = $location.path(); + * // => "/some/path" + * ``` + * * @param {(string|number)=} path New path * @return {string} path */ @@ -10780,10 +10872,9 @@ var locationPrototype = { * var searchObject = $location.search(); * // => {foo: 'bar', baz: 'xoxo'} * - * * // set foo to 'yipee' * $location.search('foo', 'yipee'); - * // => $location + * // $location.search() => {foo: 'yipee', baz: 'xoxo'} * ``` * * @param {string|Object.|Object.>} search New search params - string or @@ -10853,6 +10944,13 @@ var locationPrototype = { * * Change hash fragment when called with parameter and return `$location`. * + * + * ```js + * // given url http://example.com/some/path?foo=bar&baz=xoxo#hashValue + * var hash = $location.hash(); + * // => "hashValue" + * ``` + * * @param {(string|number)=} hash New hash fragment * @return {string} hash */ @@ -11174,11 +11272,19 @@ function $LocationProvider() { $rootScope.$evalAsync(function() { var oldUrl = $location.absUrl(); var oldState = $location.$$state; + var defaultPrevented; $location.$$parse(newUrl); $location.$$state = newState; - if ($rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl, - newState, oldState).defaultPrevented) { + + defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl, + newState, oldState).defaultPrevented; + + // if the location was changed by a `$locationChangeStart` handler then stop + // processing this location change + if ($location.absUrl() !== newUrl) return; + + if (defaultPrevented) { $location.$$parse(oldUrl); $location.$$state = oldState; setBrowserUrlWithFallback(oldUrl, false, oldState); @@ -11202,13 +11308,20 @@ function $LocationProvider() { initializing = false; $rootScope.$evalAsync(function() { - if ($rootScope.$broadcast('$locationChangeStart', $location.absUrl(), oldUrl, - $location.$$state, oldState).defaultPrevented) { + var newUrl = $location.absUrl(); + var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl, + $location.$$state, oldState).defaultPrevented; + + // if the location was changed by a `$locationChangeStart` handler then stop + // processing this location change + if ($location.absUrl() !== newUrl) return; + + if (defaultPrevented) { $location.$$parse(oldUrl); $location.$$state = oldState; } else { if (urlOrStateChanged) { - setBrowserUrlWithFallback($location.absUrl(), currentReplace, + setBrowserUrlWithFallback(newUrl, currentReplace, oldState === $location.$$state ? null : $location.$$state); } afterLocationChange(oldUrl, oldState); @@ -11545,44 +11658,31 @@ Lexer.prototype = { lex: function(text) { this.text = text; this.index = 0; - this.ch = undefined; this.tokens = []; while (this.index < this.text.length) { - this.ch = this.text.charAt(this.index); - if (this.is('"\'')) { - this.readString(this.ch); - } else if (this.isNumber(this.ch) || this.is('.') && this.isNumber(this.peek())) { + var ch = this.text.charAt(this.index); + if (ch === '"' || ch === "'") { + this.readString(ch); + } else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) { this.readNumber(); - } else if (this.isIdent(this.ch)) { + } else if (this.isIdent(ch)) { this.readIdent(); - } else if (this.is('(){}[].,;:?')) { - this.tokens.push({ - index: this.index, - text: this.ch - }); + } else if (this.is(ch, '(){}[].,;:?')) { + this.tokens.push({index: this.index, text: ch}); this.index++; - } else if (this.isWhitespace(this.ch)) { + } else if (this.isWhitespace(ch)) { this.index++; } else { - var ch2 = this.ch + this.peek(); + var ch2 = ch + this.peek(); var ch3 = ch2 + this.peek(2); - var fn = OPERATORS[this.ch]; - var fn2 = OPERATORS[ch2]; - var fn3 = OPERATORS[ch3]; - if (fn3) { - this.tokens.push({index: this.index, text: ch3, fn: fn3}); - this.index += 3; - } else if (fn2) { - this.tokens.push({index: this.index, text: ch2, fn: fn2}); - this.index += 2; - } else if (fn) { - this.tokens.push({ - index: this.index, - text: this.ch, - fn: fn - }); - this.index += 1; + var op1 = OPERATORS[ch]; + var op2 = OPERATORS[ch2]; + var op3 = OPERATORS[ch3]; + if (op1 || op2 || op3) { + var token = op3 ? ch3 : (op2 ? ch2 : ch); + this.tokens.push({index: this.index, text: token, operator: true}); + this.index += token.length; } else { this.throwError('Unexpected next character ', this.index, this.index + 1); } @@ -11591,8 +11691,8 @@ Lexer.prototype = { return this.tokens; }, - is: function(chars) { - return chars.indexOf(this.ch) !== -1; + is: function(ch, chars) { + return chars.indexOf(ch) !== -1; }, peek: function(i) { @@ -11601,7 +11701,7 @@ Lexer.prototype = { }, isNumber: function(ch) { - return ('0' <= ch && ch <= '9'); + return ('0' <= ch && ch <= '9') && typeof ch === "string"; }, isWhitespace: function(ch) { @@ -11654,79 +11754,28 @@ Lexer.prototype = { } this.index++; } - number = 1 * number; this.tokens.push({ index: start, text: number, constant: true, - fn: function() { return number; } + value: Number(number) }); }, readIdent: function() { - var expression = this.text; - - var ident = ''; var start = this.index; - - var lastDot, peekIndex, methodName, ch; - while (this.index < this.text.length) { - ch = this.text.charAt(this.index); - if (ch === '.' || this.isIdent(ch) || this.isNumber(ch)) { - if (ch === '.') lastDot = this.index; - ident += ch; - } else { + var ch = this.text.charAt(this.index); + if (!(this.isIdent(ch) || this.isNumber(ch))) { break; } this.index++; } - - //check if the identifier ends with . and if so move back one char - if (lastDot && ident[ident.length - 1] === '.') { - this.index--; - ident = ident.slice(0, -1); - lastDot = ident.lastIndexOf('.'); - if (lastDot === -1) { - lastDot = undefined; - } - } - - //check if this is not a method invocation and if it is back out to last dot - if (lastDot) { - peekIndex = this.index; - while (peekIndex < this.text.length) { - ch = this.text.charAt(peekIndex); - if (ch === '(') { - methodName = ident.substr(lastDot - start + 1); - ident = ident.substr(0, lastDot - start); - this.index = peekIndex; - break; - } - if (this.isWhitespace(ch)) { - peekIndex++; - } else { - break; - } - } - } - this.tokens.push({ index: start, - text: ident, - fn: CONSTANTS[ident] || getterFn(ident, this.options, expression) + text: this.text.slice(start, this.index), + identifier: true }); - - if (methodName) { - this.tokens.push({ - index: lastDot, - text: '.' - }); - this.tokens.push({ - index: lastDot + 1, - text: methodName - }); - } }, readString: function(quote) { @@ -11757,9 +11806,8 @@ Lexer.prototype = { this.tokens.push({ index: start, text: rawString, - string: string, constant: true, - fn: function() { return string; } + value: string }); return; } else { @@ -11820,16 +11868,12 @@ Parser.prototype = { primary = this.arrayDeclaration(); } else if (this.expect('{')) { primary = this.object(); + } else if (this.peek().identifier) { + primary = this.identifier(); + } else if (this.peek().constant) { + primary = this.constant(); } else { - var token = this.expect(); - primary = token.fn; - if (!primary) { - this.throwError('not a primary expression', token); - } - if (token.constant) { - primary.constant = true; - primary.literal = true; - } + this.throwError('not a primary expression', this.peek()); } var next, context; @@ -11863,8 +11907,11 @@ Parser.prototype = { }, peek: function(e1, e2, e3, e4) { - if (this.tokens.length > 0) { - var token = this.tokens[0]; + return this.peekAhead(0, e1, e2, e3, e4); + }, + peekAhead: function(i, e1, e2, e3, e4) { + if (this.tokens.length > i) { + var token = this.tokens[i]; var t = token.text; if (t === e1 || t === e2 || t === e3 || t === e4 || (!e1 && !e2 && !e3 && !e4)) { @@ -11884,12 +11931,19 @@ Parser.prototype = { }, consume: function(e1) { - if (!this.expect(e1)) { + if (this.tokens.length === 0) { + throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text); + } + + var token = this.expect(e1); + if (!token) { this.throwError('is unexpected, expecting [' + e1 + ']', this.peek()); } + return token; }, - unaryFn: function(fn, right) { + unaryFn: function(op, right) { + var fn = OPERATORS[op]; return extend(function $parseUnaryFn(self, locals) { return fn(self, locals, right); }, { @@ -11898,7 +11952,8 @@ Parser.prototype = { }); }, - binaryFn: function(left, fn, right, isBranching) { + binaryFn: function(left, op, right, isBranching) { + var fn = OPERATORS[op]; return extend(function $parseBinaryFn(self, locals) { return fn(self, locals, left, right); }, { @@ -11907,6 +11962,28 @@ Parser.prototype = { }); }, + identifier: function() { + var id = this.consume().text; + + //Continue reading each `.identifier` unless it is a method invocation + while (this.peek('.') && this.peekAhead(1).identifier && !this.peekAhead(2, '(')) { + id += this.consume().text + this.consume().text; + } + + return CONSTANTS[id] || getterFn(id, this.options, this.text); + }, + + constant: function() { + var value = this.consume().value; + + return extend(function $parseConstant() { + return value; + }, { + constant: true, + literal: true + }); + }, + statements: function() { var statements = []; while (true) { @@ -11938,8 +12015,7 @@ Parser.prototype = { }, filter: function(inputFn) { - var token = this.expect(); - var fn = this.$filter(token.text); + var fn = this.$filter(this.consume().text); var argsFn; var args; @@ -12002,7 +12078,7 @@ Parser.prototype = { var token; if ((token = this.expect('?'))) { middle = this.assignment(); - if ((token = this.expect(':'))) { + if (this.consume(':')) { var right = this.assignment(); return extend(function $parseTernary(self, locals) { @@ -12010,9 +12086,6 @@ Parser.prototype = { }, { constant: left.constant && middle.constant && right.constant }); - - } else { - this.throwError('expected :', token); } } @@ -12023,7 +12096,7 @@ Parser.prototype = { var left = this.logicalAND(); var token; while ((token = this.expect('||'))) { - left = this.binaryFn(left, token.fn, this.logicalAND(), true); + left = this.binaryFn(left, token.text, this.logicalAND(), true); } return left; }, @@ -12032,7 +12105,7 @@ Parser.prototype = { var left = this.equality(); var token; if ((token = this.expect('&&'))) { - left = this.binaryFn(left, token.fn, this.logicalAND(), true); + left = this.binaryFn(left, token.text, this.logicalAND(), true); } return left; }, @@ -12041,7 +12114,7 @@ Parser.prototype = { var left = this.relational(); var token; if ((token = this.expect('==','!=','===','!=='))) { - left = this.binaryFn(left, token.fn, this.equality()); + left = this.binaryFn(left, token.text, this.equality()); } return left; }, @@ -12050,7 +12123,7 @@ Parser.prototype = { var left = this.additive(); var token; if ((token = this.expect('<', '>', '<=', '>='))) { - left = this.binaryFn(left, token.fn, this.relational()); + left = this.binaryFn(left, token.text, this.relational()); } return left; }, @@ -12059,7 +12132,7 @@ Parser.prototype = { var left = this.multiplicative(); var token; while ((token = this.expect('+','-'))) { - left = this.binaryFn(left, token.fn, this.multiplicative()); + left = this.binaryFn(left, token.text, this.multiplicative()); } return left; }, @@ -12068,7 +12141,7 @@ Parser.prototype = { var left = this.unary(); var token; while ((token = this.expect('*','/','%'))) { - left = this.binaryFn(left, token.fn, this.unary()); + left = this.binaryFn(left, token.text, this.unary()); } return left; }, @@ -12078,9 +12151,9 @@ Parser.prototype = { if (this.expect('+')) { return this.primary(); } else if ((token = this.expect('-'))) { - return this.binaryFn(Parser.ZERO, token.fn, this.unary()); + return this.binaryFn(Parser.ZERO, token.text, this.unary()); } else if ((token = this.expect('!'))) { - return this.unaryFn(token.fn, this.unary()); + return this.unaryFn(token.text, this.unary()); } else { return this.primary(); } @@ -12088,7 +12161,7 @@ Parser.prototype = { fieldAccess: function(object) { var expression = this.text; - var field = this.expect().text; + var field = this.consume().text; var getter = getterFn(field, this.options, expression); return extend(function $parseFieldAccess(scope, locals, self) { @@ -12173,8 +12246,7 @@ Parser.prototype = { // Support trailing commas per ES5.1. break; } - var elementFn = this.expression(); - elementFns.push(elementFn); + elementFns.push(this.expression()); } while (this.expect(',')); } this.consume(']'); @@ -12200,11 +12272,16 @@ Parser.prototype = { // Support trailing commas per ES5.1. break; } - var token = this.expect(); - keys.push(token.string || token.text); + var token = this.consume(); + if (token.constant) { + keys.push(token.value); + } else if (token.identifier) { + keys.push(token.text); + } else { + this.throwError("invalid key", token); + } this.consume(':'); - var value = this.expression(); - valueFns.push(value); + valueFns.push(this.expression()); } while (this.expect(',')); } this.consume('}'); @@ -12685,7 +12762,11 @@ function $ParseProvider() { * @requires $rootScope * * @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). * * $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 promises to some degree. @@ -12821,7 +12902,7 @@ function $ParseProvider() { * * - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)` * - * - `finally(callback)` – allows you to observe either the fulfillment or rejection of a promise, + * - `finally(callback, notifyCallback)` – 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 * clean-up that needs to be done whether the promise was rejected or resolved. See the [full * specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for @@ -14049,11 +14130,11 @@ function $RootScopeProvider() { if (ttl < 5) { logIdx = 4 - ttl; if (!watchLog[logIdx]) watchLog[logIdx] = []; - logMsg = (isFunction(watch.exp)) - ? 'fn: ' + (watch.exp.name || watch.exp.toString()) - : watch.exp; - logMsg += '; newVal: ' + toJson(value) + '; oldVal: ' + toJson(last); - watchLog[logIdx].push(logMsg); + watchLog[logIdx].push({ + msg: isFunction(watch.exp) ? 'fn: ' + (watch.exp.name || watch.exp.toString()) : watch.exp, + newVal: value, + oldVal: last + }); } } else if (watch === lastDirtyWatch) { // If the most recently dirty watcher is now clean, short circuit since the remaining watchers @@ -14086,7 +14167,7 @@ function $RootScopeProvider() { throw $rootScopeMinErr('infdig', '{0} $digest() iterations reached. Aborting!\n' + 'Watchers fired in the last 5 iterations: {1}', - TTL, toJson(watchLog)); + TTL, watchLog); } } while (dirty || asyncQueue.length); @@ -16601,7 +16682,7 @@ function filterFilter() { * * @param {number} amount Input to filter. * @param {string=} symbol Currency symbol or identifier to be displayed. - * @param {number=} fractionSize Number of decimal places to round the amount to. + * @param {number=} fractionSize Number of decimal places to round the amount to, defaults to default max fraction size for current locale * @returns {string} Formatted number. * * @@ -16651,8 +16732,7 @@ function currencyFilter($locale) { } if (isUndefined(fractionSize)) { - // TODO: read the default value from the locale file - fractionSize = 2; + fractionSize = formats.PATTERNS[1].maxFrac; } // if null or undefined pass it through @@ -16804,9 +16884,9 @@ function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) { } } - parts.push(isNegative ? pattern.negPre : pattern.posPre); - parts.push(formatedText); - parts.push(isNegative ? pattern.negSuf : pattern.posSuf); + parts.push(isNegative ? pattern.negPre : pattern.posPre, + formatedText, + isNegative ? pattern.negSuf : pattern.posSuf); return parts.join(''); } @@ -17414,9 +17494,7 @@ function orderByFilter($parse) { return compare(get(a),get(b)); }, descending); }); - var arrayCopy = []; - for (var i = 0; i < array.length; i++) { arrayCopy.push(array[i]); } - return arrayCopy.sort(reverseComparator(comparator, reverseOrder)); + return slice.call(array).sort(reverseComparator(comparator, reverseOrder)); function comparator(o1, o2) { for (var i = 0; i < sortPredicate.length; i++) { @@ -18388,9 +18466,7 @@ var formDirectiveFactory = function(isNgForm) { controller.$setSubmitted(); }); - event.preventDefault - ? event.preventDefault() - : event.returnValue = false; // IE + event.preventDefault(); }; addEventListenerFn(formElement[0], 'submit', handleFormSubmission); @@ -18477,10 +18553,16 @@ var inputType = { * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than * minlength. * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than - * maxlength. - * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the - * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for - * patterns defined as scope expressions. + * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of + * any length. + * @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 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 is 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$')`. * @param {string=} ngChange Angular expression to be executed when input changes due to user * interaction with the input element. * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input. @@ -19020,10 +19102,16 @@ var inputType = { * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than * minlength. * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than - * maxlength. - * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the - * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for - * patterns defined as scope expressions. + * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of + * any length. + * @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 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 is 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$')`. * @param {string=} ngChange Angular expression to be executed when input changes due to user * interaction with the input element. * @@ -19102,10 +19190,16 @@ var inputType = { * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than * minlength. * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than - * maxlength. - * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the - * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for - * patterns defined as scope expressions. + * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of + * any length. + * @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 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 is 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$')`. * @param {string=} ngChange Angular expression to be executed when input changes due to user * interaction with the input element. * @@ -19185,10 +19279,16 @@ var inputType = { * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than * minlength. * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than - * maxlength. - * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the - * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for - * patterns defined as scope expressions. + * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of + * any length. + * @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 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 is 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$')`. * @param {string=} ngChange Angular expression to be executed when input changes due to user * interaction with the input element. * @@ -19451,7 +19551,7 @@ function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) { element.on('change', listener); ctrl.$render = function() { - element.val(ctrl.$isEmpty(ctrl.$modelValue) ? '' : ctrl.$viewValue); + element.val(ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue); }; } @@ -19561,10 +19661,10 @@ function createDateInputType(type, regexp, parseDate, format) { }); ctrl.$formatters.push(function(value) { - if (!ctrl.$isEmpty(value)) { - if (!isDate(value)) { - throw $ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value); - } + if (value && !isDate(value)) { + throw $ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value); + } + if (isValidDate(value)) { previousDate = value; if (previousDate && timezone === 'UTC') { var timezoneOffset = 60000 * previousDate.getTimezoneOffset(); @@ -19573,14 +19673,14 @@ function createDateInputType(type, regexp, parseDate, format) { return $filter('date')(value, format, timezone); } else { previousDate = null; + return ''; } - return ''; }); if (isDefined(attr.min) || attr.ngMin) { var minVal; ctrl.$validators.min = function(value) { - return ctrl.$isEmpty(value) || isUndefined(minVal) || parseDate(value) >= minVal; + return !isValidDate(value) || isUndefined(minVal) || parseDate(value) >= minVal; }; attr.$observe('min', function(val) { minVal = parseObservedDateValue(val); @@ -19591,18 +19691,18 @@ function createDateInputType(type, regexp, parseDate, format) { if (isDefined(attr.max) || attr.ngMax) { var maxVal; ctrl.$validators.max = function(value) { - return ctrl.$isEmpty(value) || isUndefined(maxVal) || parseDate(value) <= maxVal; + return !isValidDate(value) || isUndefined(maxVal) || parseDate(value) <= maxVal; }; attr.$observe('max', function(val) { maxVal = parseObservedDateValue(val); ctrl.$validate(); }); } - // Override the standard $isEmpty to detect invalid dates as well - ctrl.$isEmpty = function(value) { + + function isValidDate(value) { // Invalid Date: getTime() returns NaN - return !value || (value.getTime && value.getTime() !== value.getTime()); - }; + return value && !(value.getTime && value.getTime() !== value.getTime()); + } function parseObservedDateValue(val) { return isDefined(val) ? (isDate(val) ? val : parseDate(val)) : undefined; @@ -19686,7 +19786,8 @@ function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) { stringBasedInputType(ctrl); ctrl.$$parserName = 'url'; - ctrl.$validators.url = function(value) { + ctrl.$validators.url = function(modelValue, viewValue) { + var value = modelValue || viewValue; return ctrl.$isEmpty(value) || URL_REGEXP.test(value); }; } @@ -19698,7 +19799,8 @@ function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) { stringBasedInputType(ctrl); ctrl.$$parserName = 'email'; - ctrl.$validators.email = function(value) { + ctrl.$validators.email = function(modelValue, viewValue) { + var value = modelValue || viewValue; return ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value); }; } @@ -19752,9 +19854,11 @@ function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filt element[0].checked = ctrl.$viewValue; }; - // Override the standard `$isEmpty` because an empty checkbox is never equal to the trueValue + // Override the standard `$isEmpty` because the $viewValue of an empty checkbox is always set to `false` + // This is because of the parser below, which compares the `$modelValue` with `trueValue` to convert + // it to a boolean. ctrl.$isEmpty = function(value) { - return value !== trueValue; + return value === false; }; ctrl.$formatters.push(function(value) { @@ -19786,7 +19890,8 @@ function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filt * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than * minlength. * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than - * maxlength. + * 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 value does not match the * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for * patterns defined as scope expressions. @@ -19818,7 +19923,8 @@ function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filt * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than * minlength. * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than - * maxlength. + * 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 value does not match the * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for * patterns defined as scope expressions. @@ -20034,13 +20140,18 @@ is set to `true`. The parse error is stored in `ngModel.$error.parse`. * * @description * - * `NgModelController` provides API for the `ng-model` directive. The controller contains - * services for data-binding, validation, CSS updates, and value formatting and parsing. It - * purposefully does not contain any logic which deals with DOM rendering or listening to - * DOM events. Such DOM related logic should be provided by other directives which make use of - * `NgModelController` for data-binding. + * `NgModelController` provides API for the {@link ngModel `ngModel`} directive. + * The controller contains services for data-binding, validation, CSS updates, and value formatting + * and parsing. It purposefully does not contain any logic which deals with DOM rendering or + * listening to DOM events. + * Such DOM related logic should be provided by other directives which make use of + * `NgModelController` for data-binding to control elements. + * Angular provides this DOM logic for most {@link input `input`} elements. + * At the end of this page you can find a {@link ngModel.NgModelController#custom-control-example + * custom control example} that uses `ngModelController` to bind to `contenteditable` elements. * - * ## Custom Control Example + * @example + * ### Custom Control Example * This example shows how to use `NgModelController` with a custom control to achieve * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`) * collaborate together to achieve the desired result. @@ -20137,6 +20248,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$ function($scope, $exceptionHandler, $attr, $element, $parse, $animate, $timeout, $rootScope, $q, $interpolate) { this.$viewValue = Number.NaN; this.$modelValue = Number.NaN; + this.$$rawModelValue = undefined; // stores the parsed modelValue / model set from scope regardless of validity. this.$validators = {}; this.$asyncValidators = {}; this.$parsers = []; @@ -20155,32 +20267,33 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$ var parsedNgModel = $parse($attr.ngModel), + parsedNgModelAssign = parsedNgModel.assign, + ngModelGet = parsedNgModel, + ngModelSet = parsedNgModelAssign, pendingDebounce = null, ctrl = this; - var ngModelGet = function ngModelGet() { - var modelValue = parsedNgModel($scope); - if (ctrl.$options && ctrl.$options.getterSetter && isFunction(modelValue)) { - modelValue = modelValue(); - } - return modelValue; - }; - - var ngModelSet = function ngModelSet(newValue) { - var getterSetter; - if (ctrl.$options && ctrl.$options.getterSetter && - isFunction(getterSetter = parsedNgModel($scope))) { - - getterSetter(ctrl.$modelValue); - } else { - parsedNgModel.assign($scope, ctrl.$modelValue); - } - }; - this.$$setOptions = function(options) { ctrl.$options = options; - - if (!parsedNgModel.assign && (!options || !options.getterSetter)) { + if (options && options.getterSetter) { + var invokeModelGetter = $parse($attr.ngModel + '()'), + invokeModelSetter = $parse($attr.ngModel + '($$$p)'); + + ngModelGet = function($scope) { + var modelValue = parsedNgModel($scope); + if (isFunction(modelValue)) { + modelValue = invokeModelGetter($scope); + } + return modelValue; + }; + ngModelSet = function($scope, newValue) { + if (isFunction(parsedNgModel($scope))) { + invokeModelSetter($scope, {$$$p: ctrl.$modelValue}); + } else { + parsedNgModelAssign($scope, ctrl.$modelValue); + } + }; + } else if (!parsedNgModel.assign) { throw $ngModelMinErr('nonassign', "Expression '{0}' is non-assignable. Element: {1}", $attr.ngModel, startingTag($element)); } @@ -20213,17 +20326,18 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$ * @name ngModel.NgModelController#$isEmpty * * @description - * This is called when we need to determine if the value of the input is empty. + * This is called when we need to determine if the value of an input is empty. * * For instance, the required directive does this to work out if the input has data or not. + * * The default `$isEmpty` function checks whether the value is `undefined`, `''`, `null` or `NaN`. * * You can override this for input directives whose concept of being empty is different to the * default. The `checkboxInputType` directive does this because in its case a value of `false` * implies empty. * - * @param {*} value Model value to check. - * @returns {boolean} True if `value` is empty. + * @param {*} value The value of the input to check for emptiness. + * @returns {boolean} True if `value` is "empty". */ this.$isEmpty = function(value) { return isUndefined(value) || value === '' || value === null || value !== value; @@ -20274,9 +20388,9 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$ * @description * Sets the control to its pristine state. * - * This method can be called to remove the 'ng-dirty' class and set the control to its pristine - * state (ng-pristine class). A model is considered to be pristine when the model has not been changed - * from when first compiled within then form. + * This method can be called to remove the `ng-dirty` class and set the control to its pristine + * state (`ng-pristine` class). A model is considered to be pristine when the control + * has not been changed from when first compiled. */ this.$setPristine = function() { ctrl.$dirty = false; @@ -20285,6 +20399,25 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$ $animate.addClass($element, PRISTINE_CLASS); }; + /** + * @ngdoc method + * @name ngModel.NgModelController#$setDirty + * + * @description + * Sets the control to its dirty state. + * + * This method can be called to remove the `ng-pristine` class and set the control to its dirty + * state (`ng-dirty` class). A model is considered to be dirty when the control has been changed + * from when first compiled. + */ + this.$setDirty = function() { + ctrl.$dirty = true; + ctrl.$pristine = false; + $animate.removeClass($element, PRISTINE_CLASS); + $animate.addClass($element, DIRTY_CLASS); + parentForm.$setDirty(); + }; + /** * @ngdoc method * @name ngModel.NgModelController#$setUntouched @@ -20292,8 +20425,8 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$ * @description * Sets the control to its untouched state. * - * This method can be called to remove the 'ng-touched' class and set the control to its - * untouched state (ng-untouched class). Upon compilation, a model is set as untouched + * This method can be called to remove the `ng-touched` class and set the control to its + * untouched state (`ng-untouched` class). Upon compilation, a model is set as untouched * by default, however this function can be used to restore that state if the model has * already been touched by the user. */ @@ -20310,10 +20443,9 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$ * @description * Sets the control to its touched state. * - * This method can be called to remove the 'ng-untouched' class and set the control to its - * touched state (ng-touched class). A model is considered to be touched when the user has - * first interacted (focussed) on the model input element and then shifted focus away (blurred) - * from the input element. + * This method can be called to remove the `ng-untouched` class and set the control to its + * touched state (`ng-touched` class). A model is considered to be touched when the user has + * first focused the control element and then shifted focus away from the control (blur event). */ this.$setTouched = function() { ctrl.$touched = true; @@ -20391,14 +20523,51 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$ * @name ngModel.NgModelController#$validate * * @description - * Runs each of the registered validators (first synchronous validators and then asynchronous validators). + * Runs each of the registered validators (first synchronous validators and then + * asynchronous validators). + * If the validity changes to invalid, the model will be set to `undefined`, + * unless {@link ngModelOptions `ngModelOptions.allowInvalid`} is `true`. + * If the validity changes to valid, it will set the model to the last available valid + * modelValue, i.e. either the last parsed value or the last value set from the scope. */ this.$validate = function() { // ignore $validate before model is initialized if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) { return; } - this.$$parseAndValidate(); + + var viewValue = ctrl.$$lastCommittedViewValue; + // Note: we use the $$rawModelValue as $modelValue might have been + // set to undefined during a view -> model update that found validation + // errors. We can't parse the view here, since that could change + // the model although neither viewValue nor the model on the scope changed + var modelValue = ctrl.$$rawModelValue; + + // Check if the there's a parse error, so we don't unset it accidentially + var parserName = ctrl.$$parserName || 'parse'; + var parserValid = ctrl.$error[parserName] ? false : undefined; + + var prevValid = ctrl.$valid; + var prevModelValue = ctrl.$modelValue; + + var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid; + + ctrl.$$runValidators(parserValid, modelValue, viewValue, function(allValid) { + // If there was no change in validity, don't update the model + // This prevents changing an invalid modelValue to undefined + if (!allowInvalid && prevValid !== allValid) { + // Note: Don't check ctrl.$valid here, as we could have + // external validators (e.g. calculated on the server), + // that just call $setValidity and need the model value + // to calculate their validity. + ctrl.$modelValue = allValid ? modelValue : undefined; + + if (ctrl.$modelValue !== prevModelValue) { + ctrl.$$writeModelToScope(); + } + } + }); + }; this.$$runValidators = function(parseValid, modelValue, viewValue, doneCallback) { @@ -20517,11 +20686,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$ // change to dirty if (ctrl.$pristine) { - ctrl.$dirty = true; - ctrl.$pristine = false; - $animate.removeClass($element, PRISTINE_CLASS); - $animate.addClass($element, DIRTY_CLASS); - parentForm.$setDirty(); + this.$setDirty(); } this.$$parseAndValidate(); }; @@ -20542,10 +20707,11 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$ } if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) { // ctrl.$modelValue has not been touched yet... - ctrl.$modelValue = ngModelGet(); + ctrl.$modelValue = ngModelGet($scope); } var prevModelValue = ctrl.$modelValue; var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid; + ctrl.$$rawModelValue = modelValue; if (allowInvalid) { ctrl.$modelValue = modelValue; writeToModelIfNeeded(); @@ -20569,7 +20735,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$ }; this.$$writeModelToScope = function() { - ngModelSet(ctrl.$modelValue); + ngModelSet($scope, ctrl.$modelValue); forEach(ctrl.$viewChangeListeners, function(listener) { try { listener(); @@ -20665,12 +20831,12 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$ // ng-change executes in apply phase // 4. view should be changed back to 'a' $scope.$watch(function ngModelWatch() { - var modelValue = ngModelGet(); + var modelValue = ngModelGet($scope); // if scope model value and ngModel value are out of sync // TODO(perf): why not move this to the action fn? if (modelValue !== ctrl.$modelValue) { - ctrl.$modelValue = modelValue; + ctrl.$modelValue = ctrl.$$rawModelValue = modelValue; var formatters = ctrl.$formatters, idx = formatters.length; @@ -20855,7 +21021,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$ * */ -var ngModelDirective = function() { +var ngModelDirective = ['$rootScope', function($rootScope) { return { restrict: 'A', require: ['ngModel', '^?form', '^?ngModelOptions'], @@ -20899,15 +21065,17 @@ var ngModelDirective = function() { element.on('blur', function(ev) { if (modelCtrl.$touched) return; - scope.$apply(function() { - modelCtrl.$setTouched(); - }); + if ($rootScope.$$phase) { + scope.$evalAsync(modelCtrl.$setTouched); + } else { + scope.$apply(modelCtrl.$setTouched); + } }); } }; } }; -}; +}]; /** @@ -20996,8 +21164,8 @@ var requiredDirective = function() { if (!ctrl) return; attr.required = true; // force truthy in case we are on non input element - ctrl.$validators.required = function(value) { - return !attr.required || !ctrl.$isEmpty(value); + ctrl.$validators.required = function(modelValue, viewValue) { + return !attr.required || !ctrl.$isEmpty(viewValue); }; attr.$observe('required', function() { @@ -21018,7 +21186,7 @@ var patternDirective = function() { var regexp, patternExp = attr.ngPattern || attr.pattern; attr.$observe('pattern', function(regex) { if (isString(regex) && regex.length > 0) { - regex = new RegExp(regex); + regex = new RegExp('^' + regex + '$'); } if (regex && !regex.test) { @@ -21046,13 +21214,14 @@ var maxlengthDirective = function() { link: function(scope, elm, attr, ctrl) { if (!ctrl) return; - var maxlength = 0; + var maxlength = -1; attr.$observe('maxlength', function(value) { - maxlength = int(value) || 0; + var intVal = int(value); + maxlength = isNaN(intVal) ? -1 : intVal; ctrl.$validate(); }); ctrl.$validators.maxlength = function(modelValue, viewValue) { - return ctrl.$isEmpty(modelValue) || viewValue.length <= maxlength; + return (maxlength < 0) || ctrl.$isEmpty(modelValue) || (viewValue.length <= maxlength); }; } }; @@ -21071,7 +21240,7 @@ var minlengthDirective = function() { ctrl.$validate(); }); ctrl.$validators.minlength = function(modelValue, viewValue) { - return ctrl.$isEmpty(modelValue) || viewValue.length >= minlength; + return ctrl.$isEmpty(viewValue) || viewValue.length >= minlength; }; } }; @@ -21699,12 +21868,11 @@ var ngBindTemplateDirective = ['$interpolate', '$compile', function($interpolate * @name ngBindHtml * * @description - * Creates a binding that will innerHTML the result of evaluating the `expression` into the current - * 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` - * is available, for example, by including {@link ngSanitize} in your module's dependencies (not in - * core Angular). In order to use {@link ngSanitize} in your module's dependencies, you need to - * include "angular-sanitize.js" in your application. + * Evaluates the expression and inserts the resulting HTML into the element in a secure way. By default, + * the resulting HTML content will be sanitized using the {@link 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 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 @@ -23768,7 +23936,9 @@ var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 }); */ var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interpolate) { - var BRACE = /{}/g; + var BRACE = /{}/g, + IS_WHEN = /^when(Minus)?(.+)$/; + return { restrict: 'EA', link: function(scope, element, attr) { @@ -23779,34 +23949,44 @@ var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interp whensExpFns = {}, startSymbol = $interpolate.startSymbol(), endSymbol = $interpolate.endSymbol(), - isWhen = /^when(Minus)?(.+)$/; + braceReplacement = startSymbol + numberExp + '-' + offset + endSymbol, + watchRemover = angular.noop, + lastCount; forEach(attr, function(expression, attributeName) { - if (isWhen.test(attributeName)) { - whens[lowercase(attributeName.replace('when', '').replace('Minus', '-'))] = - element.attr(attr.$attr[attributeName]); + var tmpMatch = IS_WHEN.exec(attributeName); + if (tmpMatch) { + var whenKey = (tmpMatch[1] ? '-' : '') + lowercase(tmpMatch[2]); + whens[whenKey] = element.attr(attr.$attr[attributeName]); } }); forEach(whens, function(expression, key) { - whensExpFns[key] = - $interpolate(expression.replace(BRACE, startSymbol + numberExp + '-' + - offset + endSymbol)); + whensExpFns[key] = $interpolate(expression.replace(BRACE, braceReplacement)); + }); - scope.$watch(function ngPluralizeWatch() { - var value = parseFloat(scope.$eval(numberExp)); + scope.$watch(numberExp, function ngPluralizeWatchAction(newVal) { + var count = parseFloat(newVal); + var countIsNaN = isNaN(count); - if (!isNaN(value)) { - //if explicit number rule such as 1, 2, 3... is defined, just use it. Otherwise, - //check it against pluralization rules in $locale service - if (!(value in whens)) value = $locale.pluralCat(value - offset); - return whensExpFns[value](scope); - } else { - return ''; + if (!countIsNaN && !(count in whens)) { + // If an explicit number rule such as 1, 2, 3... is defined, just use it. + // Otherwise, check it against pluralization rules in $locale service. + count = $locale.pluralCat(count - offset); + } + + // If both `count` and `lastCount` are NaN, we don't need to re-register a watch. + // In JS `NaN !== NaN`, so we have to exlicitly check. + if ((count !== lastCount) && !(countIsNaN && isNaN(lastCount))) { + watchRemover(); + watchRemover = scope.$watch(whensExpFns[count], updateElementText); + lastCount = count; } - }, function ngPluralizeWatchAction(newVal) { - element.text(newVal); }); + + function updateElementText(newText) { + element.text(newText || ''); + } } }; }]; @@ -24177,7 +24357,7 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) { }); throw ngRepeatMinErr('dupes', "Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}, Duplicate value: {2}", - expression, trackById, toJson(value)); + expression, trackById, value); } else { // new never before seen block nextBlockOrder[index] = {id: trackById, scope: undefined, clone: undefined}; @@ -24288,17 +24468,17 @@ var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate'; * * ### Overriding `.ng-hide` * - * By default, the `.ng-hide` class will style the element with `display:none!important`. If you wish to change + * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide` * class in CSS: * * ```css * .ng-hide { * /* this is just another form of hiding an element */ - * display:block!important; - * position:absolute; - * top:-9999px; - * left:-9999px; + * display: block!important; + * position: absolute; + * top: -9999px; + * left: -9999px; * } * ``` * @@ -24318,13 +24498,13 @@ var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate'; * .my-element.ng-hide-add, .my-element.ng-hide-remove { * /* this is required as of 1.3x to properly * apply all styling in a show/hide animation */ - * transition:0s linear all; + * transition: 0s linear all; * } * * .my-element.ng-hide-add-active, * .my-element.ng-hide-remove-active { * /* the transition is defined in the active class */ - * transition:1s linear all; + * transition: 1s linear all; * } * * .my-element.ng-hide-add { ... } @@ -24366,29 +24546,29 @@ var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate'; .animate-show { - line-height:20px; - opacity:1; - padding:10px; - border:1px solid black; - background:white; + line-height: 20px; + opacity: 1; + padding: 10px; + border: 1px solid black; + background: white; } .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; + -webkit-transition: all linear 0.5s; + transition: all linear 0.5s; } .animate-show.ng-hide { - line-height:0; - opacity:0; - padding:0 10px; + line-height: 0; + opacity: 0; + padding: 0 10px; } .check-element { - padding:10px; - border:1px solid black; - background:white; + padding: 10px; + border: 1px solid black; + background: white; } @@ -24462,17 +24642,17 @@ var ngShowDirective = ['$animate', function($animate) { * * ### Overriding `.ng-hide` * - * By default, the `.ng-hide` class will style the element with `display:none!important`. If you wish to change + * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide` * class in CSS: * * ```css * .ng-hide { * /* this is just another form of hiding an element */ - * display:block!important; - * position:absolute; - * top:-9999px; - * left:-9999px; + * display: block!important; + * position: absolute; + * top: -9999px; + * left: -9999px; * } * ``` * @@ -24489,7 +24669,7 @@ var ngShowDirective = ['$animate', function($animate) { * //a working example can be found at the bottom of this page * // * .my-element.ng-hide-add, .my-element.ng-hide-remove { - * transition:0.5s linear all; + * transition: 0.5s linear all; * } * * .my-element.ng-hide-add { ... } @@ -24531,25 +24711,25 @@ var ngShowDirective = ['$animate', function($animate) { .animate-hide { - -webkit-transition:all linear 0.5s; - transition:all linear 0.5s; - line-height:20px; - opacity:1; - padding:10px; - border:1px solid black; - background:white; + -webkit-transition: all linear 0.5s; + transition: all linear 0.5s; + line-height: 20px; + opacity: 1; + padding: 10px; + border: 1px solid black; + background: white; } .animate-hide.ng-hide { - line-height:0; - opacity:0; - padding:0 10px; + line-height: 0; + opacity: 0; + padding: 0 10px; } .check-element { - padding:10px; - border:1px solid black; - background:white; + padding: 10px; + border: 1px solid black; + background: white; } @@ -25588,6 +25768,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) { updateLabelMap(labelMap, existingOption.label, false); updateLabelMap(labelMap, option.label, true); lastElement.text(existingOption.label = option.label); + lastElement.prop('label', existingOption.label); } if (existingOption.id !== option.id) { lastElement.val(existingOption.id = option.id); @@ -25617,6 +25798,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) { .val(option.id) .prop('selected', option.selected) .attr('selected', option.selected) + .prop('label', option.label) .text(option.label); } diff --git a/pub/System/AngularPlugin/modules/ngAnimate/angular-animate.uncompressed.js b/pub/System/AngularPlugin/modules/ngAnimate/angular-animate.uncompressed.js index 2c53aab..e63f968 100644 --- a/pub/System/AngularPlugin/modules/ngAnimate/angular-animate.uncompressed.js +++ b/pub/System/AngularPlugin/modules/ngAnimate/angular-animate.uncompressed.js @@ -1,5 +1,5 @@ /** - * @license AngularJS v1.3.3-local+sha.b7afd11 + * @license AngularJS v1.3.4-local+sha.bf6a79c * (c) 2010-2014 Google, Inc. http://angularjs.org * License: MIT */ @@ -245,7 +245,7 @@ * You then configure `$animate` to enforce this prefix: * * ```js - * $animateProvider.classNamePrefix(/animate-/); + * $animateProvider.classNameFilter(/animate-/); * ``` * * @@ -976,9 +976,9 @@ angular.module('ngAnimate', ['ng']) * | 3. $animate waits for the next digest to start the animation | class="my-animation ng-animate" | * | 4. the .ng-leave class is added to the element | class="my-animation ng-animate ng-leave" | * | 5. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-animate ng-leave" | - * | 6. $animate blocks all CSS transitions on the element to ensure the .ng-leave class styling is applied right away | class="my-animation ng-animate ng-leave” | + * | 6. $animate blocks all CSS transitions on the element to ensure the .ng-leave class styling is applied right away | class="my-animation ng-animate ng-leave" | * | 7. $animate waits for a single animation frame (this performs a reflow) | class="my-animation ng-animate ng-leave" | - * | 8. $animate removes the CSS transition block placed on the element | class="my-animation ng-animate ng-leave” | + * | 8. $animate removes the CSS transition block placed on the element | class="my-animation ng-animate ng-leave" | * | 9. the .ng-leave-active class is added (this triggers the CSS transition/animation) | class="my-animation ng-animate ng-leave ng-leave-active" | * | 10. $animate waits for the animation to complete (via events and timeout) | class="my-animation ng-animate ng-leave ng-leave-active" | * | 11. The animation ends and all generated CSS classes are removed from the element | class="my-animation" | @@ -1022,9 +1022,9 @@ angular.module('ngAnimate', ['ng']) * | 4. $animate runs the JavaScript-defined animations detected on the element | class="my-animation ng-animate" | * | 5. the .ng-move class is added to the element | class="my-animation ng-animate ng-move" | * | 6. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-animate ng-move" | - * | 7. $animate blocks all CSS transitions on the element to ensure the .ng-move class styling is applied right away | class="my-animation ng-animate ng-move” | + * | 7. $animate blocks all CSS transitions on the element to ensure the .ng-move class styling is applied right away | class="my-animation ng-animate ng-move" | * | 8. $animate waits for a single animation frame (this performs a reflow) | class="my-animation ng-animate ng-move" | - * | 9. $animate removes the CSS transition block placed on the element | class="my-animation ng-animate ng-move” | + * | 9. $animate removes the CSS transition block placed on the element | class="my-animation ng-animate ng-move" | * | 10. the .ng-move-active class is added (this triggers the CSS transition/animation) | class="my-animation ng-animate ng-move ng-move-active" | * | 11. $animate waits for the animation to complete (via events and timeout) | class="my-animation ng-animate ng-move ng-move-active" | * | 12. The animation ends and all generated CSS classes are removed from the element | class="my-animation" | @@ -1069,8 +1069,8 @@ angular.module('ngAnimate', ['ng']) * | 3. the .super-add class is added to the element | class="my-animation ng-animate super-add" | * | 4. $animate waits for a single animation frame (this performs a reflow) | class="my-animation ng-animate super-add" | * | 5. the .super and .super-add-active classes are added (this triggers the CSS transition/animation) | class="my-animation ng-animate super super-add super-add-active" | - * | 6. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-animate super-add" | - * | 7. $animate waits for the animation to complete (via events and timeout) | class="my-animation super super-add super-add-active" | + * | 6. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-animate super super-add super-add-active" | + * | 7. $animate waits for the animation to complete (via events and timeout) | class="my-animation ng-animate super super-add super-add-active" | * | 8. The animation ends and all generated CSS classes are removed from the element | class="my-animation super" | * | 9. The super class is kept on the element | class="my-animation super" | * | 10. The returned promise is resolved. | class="my-animation super" | @@ -1103,7 +1103,7 @@ angular.module('ngAnimate', ['ng']) * | 3. the .super-remove class is added to the element | class="my-animation super ng-animate super-remove" | * | 4. $animate waits for a single animation frame (this performs a reflow) | class="my-animation super ng-animate super-remove" | * | 5. the .super-remove-active classes are added and .super is removed (this triggers the CSS transition/animation) | class="my-animation ng-animate super-remove super-remove-active" | - * | 6. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation super ng-animate super-remove" | + * | 6. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-animate super-remove super-remove-active" | * | 7. $animate waits for the animation to complete (via events and timeout) | class="my-animation ng-animate super-remove super-remove-active" | * | 8. The animation ends and all generated CSS classes are removed from the element | class="my-animation" | * | 9. The returned promise is resolved. | class="my-animation" | @@ -1128,11 +1128,11 @@ angular.module('ngAnimate', ['ng']) * * | Animation Step | What the element class attribute looks like | * |--------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------| - * | 1. $animate.removeClass(element, ‘on’, ‘off’) is called | class="my-animation super off” | - * | 2. $animate runs the JavaScript-defined animations detected on the element | class="my-animation super ng-animate off” | - * | 3. the .on-add and .off-remove classes are added to the element | class="my-animation ng-animate on-add off-remove off” | - * | 4. $animate waits for a single animation frame (this performs a reflow) | class="my-animation ng-animate on-add off-remove off” | - * | 5. the .on, .on-add-active and .off-remove-active classes are added and .off is removed (this triggers the CSS transition/animation) | class="my-animation ng-animate on on-add on-add-active off-remove off-remove-active” | + * | 1. $animate.setClass(element, 'on', 'off') is called | class="my-animation off" | + * | 2. $animate runs the JavaScript-defined animations detected on the element | class="my-animation ng-animate off" | + * | 3. the .on-add and .off-remove classes are added to the element | class="my-animation ng-animate on-add off-remove off" | + * | 4. $animate waits for a single animation frame (this performs a reflow) | class="my-animation ng-animate on-add off-remove off" | + * | 5. the .on, .on-add-active and .off-remove-active classes are added and .off is removed (this triggers the CSS transition/animation) | class="my-animation ng-animate on on-add on-add-active off-remove off-remove-active" | * | 6. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-animate on on-add on-add-active off-remove off-remove-active" | * | 7. $animate waits for the animation to complete (via events and timeout) | class="my-animation ng-animate on on-add on-add-active off-remove off-remove-active" | * | 8. The animation ends and all generated CSS classes are removed from the element | class="my-animation on" | diff --git a/pub/System/AngularPlugin/modules/ngAria/angular-aria.uncompressed.js b/pub/System/AngularPlugin/modules/ngAria/angular-aria.uncompressed.js index e2c1e99..c5fff68 100644 --- a/pub/System/AngularPlugin/modules/ngAria/angular-aria.uncompressed.js +++ b/pub/System/AngularPlugin/modules/ngAria/angular-aria.uncompressed.js @@ -1,5 +1,5 @@ /** - * @license AngularJS v1.3.3-local+sha.b7afd11 + * @license AngularJS v1.3.4-local+sha.bf6a79c * (c) 2010-2014 Google, Inc. http://angularjs.org * License: MIT */ @@ -10,33 +10,49 @@ * @name ngAria * @description * - * The `ngAria` module provides support for adding ARIA - * attributes that convey state or semantic information about the application in order to allow assistive technologies - * to convey appropriate information to persons with disabilities. + * The `ngAria` module provides support for common + * [ARIA](http://www.w3.org/TR/wai-aria/) + * attributes that convey state or semantic information about the application for users + * of assistive technologies, such as screen readers. * *
* - * # Usage - * To enable the addition of the ARIA tags, just require the module into your application and the tags will - * hook into your ng-show/ng-hide, input, textarea, button, select and ng-required directives and adds the - * appropriate ARIA attributes. + * ## Usage * - * Currently, the following ARIA attributes are implemented: + * For ngAria to do its magic, simply include the module as a dependency. The directives supported + * by ngAria are: + * `ngModel`, `ngDisabled`, `ngShow`, `ngHide`, `ngClick`, `ngDblClick`, and `ngMessages`. * - * + aria-hidden - * + aria-checked - * + aria-disabled - * + aria-required - * + aria-invalid - * + aria-multiline - * + aria-valuenow - * + aria-valuemin - * + aria-valuemax - * + tabindex + * Below is a more detailed breakdown of the attributes handled by ngAria: * - * You can disable individual ARIA attributes by using the {@link ngAria.$ariaProvider#config config} method. + * | Directive | Supported Attributes | + * |---------------------------------------------|----------------------------------------------------------------------------------------| + * | {@link ng.directive:ngModel ngModel} | aria-checked, aria-valuemin, aria-valuemax, aria-valuenow, aria-invalid, aria-required | + * | {@link ng.directive:ngDisabled ngDisabled} | aria-disabled | + * | {@link ng.directive:ngShow ngShow} | aria-hidden | + * | {@link ng.directive:ngHide ngHide} | aria-hidden | + * | {@link ng.directive:ngClick ngClick} | tabindex | + * | {@link ng.directive:ngDblclick ngDblclick} | tabindex | + * | {@link module:ngMessages ngMessages} | aria-live | + * + * Find out more information about each directive by reading the + * {@link guide/accessibility ngAria Developer Guide}. + * + * ##Example + * Using ngDisabled with ngAria: + * ```html + * + * ``` + * Becomes: + * ```html + * + * ``` + * + * ##Disabling Attributes + * It's possible to disable individual attributes added by ngAria with the + * {@link ngAria.$ariaProvider#config config} method. For more details, see the + * {@link guide/accessibility Developer Guide}. */ - /* global -ngAriaModule */ var ngAriaModule = angular.module('ngAria', ['ng']). provider('$aria', $AriaProvider); @@ -47,10 +63,20 @@ var ngAriaModule = angular.module('ngAria', ['ng']). * * @description * - * Used for configuring ARIA attributes. + * Used for configuring the ARIA attributes injected and managed by ngAria. + * + * ```js + * angular.module('myApp', ['ngAria'], function config($ariaProvider) { + * $ariaProvider.config({ + * ariaValue: true, + * tabindex: false + * }); + * }); + *``` * * ## Dependencies * Requires the {@link ngAria} module to be installed. + * */ function $AriaProvider() { var config = { @@ -113,7 +139,41 @@ function $AriaProvider() { * * @description * - * Contains helper methods for applying ARIA attributes to HTML + * The $aria service contains helper methods for applying common + * [ARIA](http://www.w3.org/TR/wai-aria/) attributes to HTML directives. + * + * ngAria injects common accessibility attributes that tell assistive technologies when HTML + * elements are enabled, selected, hidden, and more. To see how this is performed with ngAria, + * let's review a code snippet from ngAria itself: + * + *```js + * ngAriaModule.directive('ngDisabled', ['$aria', function($aria) { + * return $aria.$$watchExpr('ngDisabled', 'aria-disabled'); + * }]) + *``` + * Shown above, the ngAria module creates a directive with the same signature as the + * traditional `ng-disabled` directive. But this ngAria version is dedicated to + * solely managing accessibility attributes. The internal `$aria` service is used to watch the + * boolean attribute `ngDisabled`. If it has not been explicitly set by the developer, + * `aria-disabled` is injected as an attribute with its value synchronized to the value in + * `ngDisabled`. + * + * Because ngAria hooks into the `ng-disabled` directive, developers do not have to do + * anything to enable this feature. The `aria-disabled` attribute is automatically managed + * simply as a silent side-effect of using `ng-disabled` with the ngAria module. + * + * The full list of directives that interface with ngAria: + * * **ngModel** + * * **ngShow** + * * **ngHide** + * * **ngClick** + * * **ngDblclick** + * * **ngMessages** + * * **ngDisabled** + * + * Read the {@link guide/accessibility ngAria Developer Guide} for a thorough explanation of each + * directive. + * * * ## Dependencies * Requires the {@link ngAria} module to be installed.