From 6a0931aeae0f452044bbc3fbbf23acf236ea0513 Mon Sep 17 00:00:00 2001 From: David Adam Date: Sat, 4 Oct 2014 18:03:56 +0800 Subject: [PATCH] web_config: update AngularJS to 1.0.8 --- share/tools/web_config/js/angular.js | 653 ++++++++++++++++++++------- 1 file changed, 482 insertions(+), 171 deletions(-) diff --git a/share/tools/web_config/js/angular.js b/share/tools/web_config/js/angular.js index a860c8594..719bc648e 100644 --- a/share/tools/web_config/js/angular.js +++ b/share/tools/web_config/js/angular.js @@ -1,5 +1,5 @@ /** - * @license AngularJS v1.0.7 + * @license AngularJS v1.0.8 * (c) 2010-2012 Google, Inc. http://angularjs.org * License: MIT */ @@ -282,7 +282,7 @@ noop.$inject = []; *
      function transformer(transformationFn, value) {
-       return (transformationFn || identity)(value);
+       return (transformationFn || angular.identity)(value);
      };
    
*/ @@ -409,6 +409,18 @@ function isArray(value) { function isFunction(value){return typeof value == 'function';} +/** + * Determines if a value is a regular expression object. + * + * @private + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is a `RegExp`. + */ +function isRegExp(value) { + return toString.apply(value) == '[object RegExp]'; +} + + /** * Checks if `obj` is a window object. * @@ -436,9 +448,20 @@ function isBoolean(value) { } -function trim(value) { - return isString(value) ? value.replace(/^\s*/, '').replace(/\s*$/, '') : value; -} +var trim = (function() { + // native trim is way faster: http://jsperf.com/angular-trim-test + // but IE doesn't have it... :-( + // TODO: we should move this into IE/ES5 polyfill + if (!String.prototype.trim) { + return function(value) { + return isString(value) ? value.replace(/^\s*/, '').replace(/\s*$/, '') : value; + }; + } + return function(value) { + return isString(value) ? value.trim() : value; + }; +})(); + /** * @ngdoc function @@ -581,6 +604,8 @@ function copy(source, destination){ destination = copy(source, []); } else if (isDate(source)) { destination = new Date(source.getTime()); + } else if (isRegExp(source)) { + destination = new RegExp(source.source); } else if (isObject(source)) { destination = copy(source, {}); } @@ -628,7 +653,7 @@ function shallowCopy(src, dst) { * @function * * @description - * Determines if two objects or two values are equivalent. Supports value types, arrays and + * Determines if two objects or two values are equivalent. Supports value types, regular expressions, arrays and * objects. * * Two objects or values are considered equivalent if at least one of the following is true: @@ -636,6 +661,9 @@ function shallowCopy(src, dst) { * * Both objects or values pass `===` comparison. * * Both objects or values are of the same type and all of their properties pass `===` comparison. * * Both values are NaN. (In JavasScript, NaN == NaN => false. But we consider two NaN as equal) + * * Both values represent the same regular expression (In JavasScript, + * /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual + * representation matches). * * During a property comparision, properties of `function` type and properties with names * that begin with `$` are ignored. @@ -654,6 +682,7 @@ function equals(o1, o2) { if (t1 == t2) { if (t1 == 'object') { if (isArray(o1)) { + if (!isArray(o2)) return false; if ((length = o1.length) == o2.length) { for(key=0; key @@ -825,10 +875,12 @@ function startingTag(element) { function parseKeyValue(/**string*/keyValue) { var obj = {}, key_value, key; forEach((keyValue || "").split('&'), function(keyValue){ - if (keyValue) { + if ( keyValue ) { key_value = keyValue.split('='); - key = decodeURIComponent(key_value[0]); - obj[key] = isDefined(key_value[1]) ? decodeURIComponent(key_value[1]) : true; + key = tryDecodeURIComponent(key_value[0]); + if ( isDefined(key) ) { + obj[key] = isDefined(key_value[1]) ? tryDecodeURIComponent(key_value[1]) : true; + } } }); return obj; @@ -894,10 +946,14 @@ function encodeUriQuery(val, pctEncodeSpaces) { * @description * * Use this directive to auto-bootstrap an application. Only - * one directive can be used per HTML document. The directive + * one ngApp directive can be used per HTML document. The directive * designates the root of the application and is typically placed * at the root of the page. * + * The first ngApp found in the document will be auto-bootstrapped. To use multiple applications in an + * HTML document you must manually bootstrap them using {@link angular.bootstrap}. + * Applications cannot be nested. + * * In the example below if the `ngApp` directive would not be placed * on the `html` element then the document would not be compiled * and the `{{ 1+2 }}` would not be resolved to `3`. @@ -963,12 +1019,15 @@ function angularInit(element, bootstrap) { * * See: {@link guide/bootstrap Bootstrap} * + * Note that ngScenario-based end-to-end tests cannot use this function to bootstrap manually. + * They must use {@link api/ng.directive:ngApp ngApp}. + * * @param {Element} element DOM element which is the root of angular application. * @param {Array=} modules an array of module declarations. See: {@link angular.module modules} * @returns {AUTO.$injector} Returns the newly created injector for this app. */ function bootstrap(element, modules) { - var resumeBootstrapInternal = function() { + var doBootstrap = function() { element = jqLite(element); modules = modules || []; modules.unshift(['$provide', function($provide) { @@ -990,7 +1049,7 @@ function bootstrap(element, modules) { var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/; if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) { - return resumeBootstrapInternal(); + return doBootstrap(); } window.name = window.name.replace(NG_DEFER_BOOTSTRAP, ''); @@ -998,7 +1057,7 @@ function bootstrap(element, modules) { forEach(extraModules, function(module) { modules.push(module); }); - resumeBootstrapInternal(); + doBootstrap(); }; } @@ -1022,9 +1081,10 @@ function bindJQuery() { injector: JQLitePrototype.injector, inheritedData: JQLitePrototype.inheritedData }); - JQLitePatchJQueryRemove('remove', true); - JQLitePatchJQueryRemove('empty'); - JQLitePatchJQueryRemove('html'); + // Method signature: JQLitePatchJQueryRemove(name, dispatchThis, filterElems, getterIfNoArguments) + JQLitePatchJQueryRemove('remove', true, true, false); + JQLitePatchJQueryRemove('empty', false, false, false); + JQLitePatchJQueryRemove('html', false, false, true); } else { jqLite = JQLite; } @@ -1051,6 +1111,33 @@ function assertArgFn(arg, name, acceptArrayAnnotation) { return arg; } +/** + * Return the value accessible from the object by path. Any undefined traversals are ignored + * @param {Object} obj starting object + * @param {string} path path to traverse + * @param {boolean=true} bindFnToScope + * @returns value as accessible by path + */ +//TODO(misko): this function needs to be removed +function getter(obj, path, bindFnToScope) { + if (!path) return obj; + var keys = path.split('.'); + var key; + var lastInstance = obj; + var len = keys.length; + + for (var i = 0; i < len; i++) { + key = keys[i]; + if (obj) { + obj = (lastInstance = obj)[key]; + } + } + if (!bindFnToScope && isFunction(obj)) { + return bind(lastInstance, obj); + } + return obj; +} + /** * @ngdoc interface * @name angular.Module @@ -1081,8 +1168,8 @@ function setupModuleLoader(window) { * * # Module * - * A module is a collocation of services, directives, filters, and configuration information. Module - * is used to configure the {@link AUTO.$injector $injector}. + * A module is a collection of services, directives, filters, and configuration information. + * `angular.module` is used to configure the {@link AUTO.$injector $injector}. * *
      * // Create a new module
@@ -1313,11 +1400,11 @@ function setupModuleLoader(window) {
  * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
  */
 var version = {
-  full: '1.0.7',    // all of these placeholder strings will be replaced by grunt's
+  full: '1.0.8',    // all of these placeholder strings will be replaced by grunt's
   major: 1,    // package task
   minor: 0,
-  dot: 7,
-  codeName: 'monochromatic-rainbow'
+  dot: 8,
+  codeName: 'bubble-burst'
 };
 
 
@@ -1386,7 +1473,6 @@ function publishExternalAPI(angular){
             ngPluralize: ngPluralizeDirective,
             ngRepeat: ngRepeatDirective,
             ngShow: ngShowDirective,
-            ngSubmit: ngSubmitDirective,
             ngStyle: ngStyleDirective,
             ngSwitch: ngSwitchDirective,
             ngSwitchWhen: ngSwitchWhenDirective,
@@ -1456,7 +1542,8 @@ function publishExternalAPI(angular){
  * Note: All element references in Angular are always wrapped with jQuery or jqLite; they are never
  * raw DOM references.
  *
- * ## Angular's jQuery lite provides the following methods:
+ * ## Angular's jqLite
+ * Angular's lite version of jQuery provides only the following jQuery methods:
  *
  * - [addClass()](http://api.jquery.com/addClass/)
  * - [after()](http://api.jquery.com/after/)
@@ -1489,8 +1576,14 @@ function publishExternalAPI(angular){
  * - [val()](http://api.jquery.com/val/)
  * - [wrap()](http://api.jquery.com/wrap/)
  *
- * ## In addtion to the above, Angular provides additional methods to both jQuery and jQuery lite:
+ * ## jQuery/jqLite Extras
+ * Angular also provides the following additional methods and events to both jQuery and jqLite:
  *
+ * ### Events
+ * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event
+ *    on all DOM nodes being removed.  This can be used to clean up and 3rd party bindings to the DOM
+ *    element before it is removed.
+ * ### Methods
  * - `controller(name)` - retrieves the controller of the current element or its parent. By default
  *   retrieves controller associated with the `ngController` directive. If `name` is provided as
  *   camelCase directive name, then the controller for this directive will be retrieved (e.g.
@@ -1537,37 +1630,38 @@ function camelCase(name) {
 /////////////////////////////////////////////
 // jQuery mutation patch
 //
-//  In conjunction with bindJQuery intercepts all jQuery's DOM destruction apis and fires a
+// In conjunction with bindJQuery intercepts all jQuery's DOM destruction apis and fires a
 // $destroy event on all DOM nodes being removed.
 //
 /////////////////////////////////////////////
 
-function JQLitePatchJQueryRemove(name, dispatchThis) {
+function JQLitePatchJQueryRemove(name, dispatchThis, filterElems, getterIfNoArguments) {
   var originalJqFn = jQuery.fn[name];
   originalJqFn = originalJqFn.$original || originalJqFn;
   removePatch.$original = originalJqFn;
   jQuery.fn[name] = removePatch;
 
-  function removePatch() {
-    var list = [this],
+  function removePatch(param) {
+    var list = filterElems && param ? [this.filter(param)] : [this],
         fireEvent = dispatchThis,
         set, setIndex, setLength,
-        element, childIndex, childLength, children,
-        fns, events;
+        element, childIndex, childLength, children;
 
-    while(list.length) {
-      set = list.shift();
-      for(setIndex = 0, setLength = set.length; setIndex < setLength; setIndex++) {
-        element = jqLite(set[setIndex]);
-        if (fireEvent) {
-          element.triggerHandler('$destroy');
-        } else {
-          fireEvent = !fireEvent;
-        }
-        for(childIndex = 0, childLength = (children = element.children()).length;
-            childIndex < childLength;
-            childIndex++) {
-          list.push(jQuery(children[childIndex]));
+    if (!getterIfNoArguments || param != null) {
+      while(list.length) {
+        set = list.shift();
+        for(setIndex = 0, setLength = set.length; setIndex < setLength; setIndex++) {
+          element = jqLite(set[setIndex]);
+          if (fireEvent) {
+            element.triggerHandler('$destroy');
+          } else {
+            fireEvent = !fireEvent;
+          }
+          for(childIndex = 0, childLength = (children = element.children()).length;
+              childIndex < childLength;
+              childIndex++) {
+            list.push(jQuery(children[childIndex]));
+          }
         }
       }
     }
@@ -1627,7 +1721,7 @@ function JQLiteUnbind(element, type, fn) {
       removeEventListenerFn(element, type, events[type]);
       delete events[type];
     } else {
-      arrayRemove(events[type], fn);
+      arrayRemove(events[type] || [], fn);
     }
   }
 }
@@ -1901,6 +1995,15 @@ forEach({
 
   val: function(element, value) {
     if (isUndefined(value)) {
+      if (nodeName_(element) === 'SELECT' && element.multiple) {
+        var result = [];
+        forEach(element.options, function (option) {
+          if (option.selected) {
+            result.push(option.value || option.text);
+          }
+        });
+        return result.length === 0 ? null : result;
+      }
       return element.value;
     }
     element.value = value;
@@ -2118,12 +2221,7 @@ forEach({
     if (element.nodeType === 1) {
       var index = element.firstChild;
       forEach(new JQLite(node), function(child){
-        if (index) {
-          element.insertBefore(child, index);
-        } else {
-          element.appendChild(child);
-          index = child;
-        }
+        element.insertBefore(child, index);
       });
     }
   },
@@ -3116,7 +3214,8 @@ function Browser(window, document, $log, $sniffer) {
   //////////////////////////////////////////////////////////////
 
   var lastBrowserUrl = location.href,
-      baseElement = document.find('base');
+      baseElement = document.find('base'),
+      replacedUrl = null;
 
   /**
    * @name ng.$browser#url
@@ -3151,14 +3250,21 @@ function Browser(window, document, $log, $sniffer) {
           baseElement.attr('href', baseElement.attr('href'));
         }
       } else {
-        if (replace) location.replace(url);
-        else location.href = url;
+        if (replace) {
+          location.replace(url);
+          replacedUrl = url;
+        } else {
+          location.href = url;
+          replacedUrl = null;
+        }
       }
       return self;
     // getter
     } else {
-      // the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
-      return location.href.replace(/%27/g,"'");
+      // - the replacedUrl is a workaround for an IE8-9 issue with location.replace method that doesn't update
+      //   location.href synchronously
+      // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
+      return replacedUrl || location.href.replace(/%27/g,"'");
     }
   };
 
@@ -3365,7 +3471,20 @@ function $BrowserProvider(){
  * @name ng.$cacheFactory
  *
  * @description
- * Factory that constructs cache objects.
+ * Factory that constructs cache objects and gives access to them.
+ * 
+ * 
+ * 
+ *  var cache = $cacheFactory('cacheId');
+ *  expect($cacheFactory.get('cacheId')).toBe(cache);
+ *  expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined();
+ *
+ *  cache.put("key", "value");
+ *  cache.put("another key", "another value");
+ * 
+ *  expect(cache.info()).toEqual({id: 'cacheId', size: 2}); // Since we've specified no options on creation
+ * 
+ * 
* * * @param {string} cacheId Name or id of the newly created cache. @@ -3497,6 +3616,16 @@ function $CacheFactoryProvider() { } + /** + * @ngdoc method + * @name ng.$cacheFactory#info + * @methodOf ng.$cacheFactory + * + * @description + * Get information about all the of the caches that have been created + * + * @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info` + */ cacheFactory.info = function() { var info = {}; forEach(caches, function(cache, cacheId) { @@ -3506,6 +3635,17 @@ function $CacheFactoryProvider() { }; + /** + * @ngdoc method + * @name ng.$cacheFactory#get + * @methodOf ng.$cacheFactory + * + * @description + * Get access to a cache object by the `cacheId` used when it was created. + * + * @param {string} cacheId Name or id of a cache to access. + * @returns {object} Cache object identified by the cacheId or undefined if no such cache. + */ cacheFactory.get = function(cacheId) { return caches[cacheId]; }; @@ -3520,8 +3660,44 @@ function $CacheFactoryProvider() { * @name ng.$templateCache * * @description - * Cache used for storing html templates. - * + * The first time a template is used, it is loaded in the template cache for quick retrieval. You can + * load templates directly into the cache in a `script` tag, or by consuming the `$templateCache` + * service directly. + * + * Adding via the `script` tag: + *
+ * 
+ * 
+ * 
+ * 
+ *   ...
+ * 
+ * 
+ * + * **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. + * + * Adding via the $templateCache service: + * + *
+ * var myApp = angular.module('myApp', []);
+ * myApp.run(function($templateCache) {
+ *   $templateCache.put('templateId.html', 'This is the content of the template');
+ * });
+ * 
+ * + * To retrieve the template later, simply use it in your HTML: + *
+ * 
+ *
+ * + * or get it via Javascript: + *
+ * $templateCache.get('templateId.html')
+ * 
+ * * See {@link ng.$cacheFactory $cacheFactory}. * */ @@ -3697,11 +3873,11 @@ function $CompileProvider($provide) { * @function * * @description - * Register a new directives with the compiler. + * Register a new directive with the compiler. * * @param {string} name Name of the directive in camel-case. (ie ngBind which will match as * ng-bind). - * @param {function} directiveFactory An injectable directive factroy function. See {@link guide/directive} for more + * @param {function|Array} directiveFactory An injectable directive factory function. See {@link guide/directive} for more * info. * @returns {ng.$compileProvider} Self for chaining. */ @@ -3824,7 +4000,7 @@ function $CompileProvider($provide) { // href property always returns normalized absolute url, so we can match against that normalizedVal = urlSanitizationNode.href; - if (!normalizedVal.match(urlSanitizationWhitelist)) { + if (normalizedVal !== '' && !normalizedVal.match(urlSanitizationWhitelist)) { this[key] = value = 'unsafe:' + normalizedVal; } } @@ -4048,7 +4224,7 @@ function $CompileProvider($provide) { for (var attr, name, nName, value, nAttrs = node.attributes, j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) { attr = nAttrs[j]; - if (attr.specified) { + if (!msie || msie >= 8 || attr.specified) { name = attr.name; nName = directiveNormalize(name.toLowerCase()); attrsMap[nName] = name; @@ -6427,33 +6603,6 @@ function setter(obj, path, setValue) { return setValue; } -/** - * Return the value accesible from the object by path. Any undefined traversals are ignored - * @param {Object} obj starting object - * @param {string} path path to traverse - * @param {boolean=true} bindFnToScope - * @returns value as accesbile by path - */ -//TODO(misko): this function needs to be removed -function getter(obj, path, bindFnToScope) { - if (!path) return obj; - var keys = path.split('.'); - var key; - var lastInstance = obj; - var len = keys.length; - - for (var i = 0; i < len; i++) { - key = keys[i]; - if (obj) { - obj = (lastInstance = obj)[key]; - } - } - if (!bindFnToScope && isFunction(obj)) { - return bind(lastInstance, obj); - } - return obj; -} - var getterFnCache = {}; /** @@ -6715,8 +6864,8 @@ function $ParseProvider() { * **Methods** * * - `then(successCallback, errorCallback)` – regardless of when the promise was or will be resolved - * or rejected calls one of the success or error callbacks asynchronously as soon as the result - * is available. The callbacks are called with a single argument the result or rejection reason. + * or rejected, `then` calls one of the success or error callbacks asynchronously as soon as the result + * is available. The callbacks are called with a single argument: the result or rejection reason. * * This method *returns a new promise* which is resolved or rejected via the return value of the * `successCallback` or `errorCallback`. @@ -6724,7 +6873,7 @@ function $ParseProvider() { * * # Chaining promises * - * Because calling `then` api of a promise returns a new derived promise, it is easily possible + * Because calling the `then` method of a promise returns a new derived promise, it is easily possible * to create a chain of promises: * *
@@ -6732,13 +6881,13 @@ function $ParseProvider() {
  *     return result + 1;
  *   });
  *
- *   // promiseB will be resolved immediately after promiseA is resolved and its value will be
- *   // the result of promiseA incremented by 1
+ *   // promiseB will be resolved immediately after promiseA is resolved and its value
+ *   // will be the result of promiseA incremented by 1
  * 
* * It is possible to create chains of any length and since a promise can be resolved with another * promise (which will defer its resolution further), it is possible to pause/defer resolution of - * the promises at any point in the chain. This makes it possible to implement powerful apis like + * the promises at any point in the chain. This makes it possible to implement powerful APIs like * $http's response interceptors. * * @@ -6845,8 +6994,8 @@ function qFactory(nextTick, exceptionHandler) { try { result.resolve((callback || defaultCallback)(value)); } catch(e) { - exceptionHandler(e); result.reject(e); + exceptionHandler(e); } }; @@ -6854,8 +7003,8 @@ function qFactory(nextTick, exceptionHandler) { try { result.resolve((errback || defaultErrback)(reason)); } catch(e) { - exceptionHandler(e); result.reject(e); + exceptionHandler(e); } }; @@ -7064,8 +7213,8 @@ function $RouteProvider(){ * route definition. * * `path` can contain named groups starting with a colon (`:name`). All characters up to the - * next slash are matched and stored in `$routeParams` under the given `name` when the route - * matches. + * next slash are matched and stored in `$routeParams` under the given `name` after the route + * is resolved. * * @param {Object} route Mapping information to be assigned to `$route.current` on route * match. @@ -7090,7 +7239,9 @@ function $RouteProvider(){ * - `factory` - `{string|function}`: If `string` then it is an alias for a service. * Otherwise if function, then it is {@link api/AUTO.$injector#invoke injected} * and the return value is treated as the dependency. If the result is a promise, it is resolved - * before its value is injected into the controller. + * before its value is injected into the controller. Be aware that `ngRoute.$routeParams` will + * still refer to the previous route within these resolve functions. Use `$route.current.params` + * to access the new route parameters, instead. * * - `redirectTo` – {(string|function())=} – value to update * {@link ng.$location $location} path with and trigger route redirection. @@ -7525,6 +7676,10 @@ function $RouteProvider(){ * The service guarantees that the identity of the `$routeParams` object will remain unchanged * (but its properties will likely change) even when a route change occurs. * + * Note that the `$routeParams` are only updated *after* a route change completes successfully. + * This means that you cannot rely on `$routeParams` being correct in route resolve functions. + * Instead you can use `$route.current.params` to access the new route's parameters. + * * @example *
  *  // Given:
@@ -7932,7 +8087,7 @@ function $RootScopeProvider(){
                   watch = watchers[length];
                   // Most common watches are on primitives, in which case we can short
                   // circuit it with === operator, only when === fails do we use .equals
-                  if ((value = watch.get(current)) !== (last = watch.last) &&
+                  if (watch && (value = watch.get(current)) !== (last = watch.last) &&
                       !(watch.eq
                           ? equals(value, last)
                           : (typeof value == 'number' && typeof last == 'number'
@@ -7985,6 +8140,9 @@ function $RootScopeProvider(){
        *
        * @description
        * Broadcasted when a scope and its children are being destroyed.
+       * 
+       * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
+       * clean up DOM bindings before an element is removed from the DOM.
        */
 
       /**
@@ -8006,6 +8164,9 @@ function $RootScopeProvider(){
        * Just before a scope is destroyed a `$destroy` event is broadcasted on this scope.
        * Application code can register a `$destroy` event handler that will give it chance to
        * perform any necessary cleanup.
+       * 
+       * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
+       * clean up DOM bindings before an element is removed from the DOM.
        */
       $destroy: function() {
         // we can't destroy the root scope or a scope that has been already destroyed
@@ -8410,8 +8571,10 @@ function $SnifferProvider() {
  * it is a global variable. In angular we always refer to it through the
  * `$window` service, so it may be overriden, removed or mocked for testing.
  *
- * All expressions are evaluated with respect to current scope so they don't
- * suffer from window globality.
+ * Expressions, like the one defined for the `ngClick` directive in the example
+ * below, are evaluated with respect to the current scope.  Therefore, there is
+ * no risk of inadvertently coding in a dependency on a global value in such an
+ * expression.
  *
  * @example
    
@@ -8731,6 +8894,7 @@ function $HttpProvider() {
      *     return function(promise) {
      *       return promise.then(function(response) {
      *         // do something on success
+     *         return response;
      *       }, function(response) {
      *         // do something on error
      *         if (canRecover(response)) {
@@ -8922,17 +9086,40 @@ function $HttpProvider() {
 
       var reqTransformFn = config.transformRequest || $config.transformRequest,
           respTransformFn = config.transformResponse || $config.transformResponse,
-          defHeaders = $config.headers,
-          reqHeaders = extend({'X-XSRF-TOKEN': $browser.cookies()['XSRF-TOKEN']},
-              defHeaders.common, defHeaders[lowercase(config.method)], config.headers),
-          reqData = transformData(config.data, headersGetter(reqHeaders), reqTransformFn),
+          reqHeaders = extend({}, config.headers),
+          defHeaders = extend(
+            {'X-XSRF-TOKEN': $browser.cookies()['XSRF-TOKEN']},
+            $config.headers.common,
+            $config.headers[lowercase(config.method)]
+          ),
+          reqData,
+          defHeaderName, lowercaseDefHeaderName, headerName,
           promise;
 
+      // using for-in instead of forEach to avoid unecessary iteration after header has been found
+      defaultHeadersIteration:
+      for(defHeaderName in defHeaders) {
+        lowercaseDefHeaderName = lowercase(defHeaderName);
+        for(headerName in config.headers) {
+          if (lowercase(headerName) === lowercaseDefHeaderName) {
+            continue defaultHeadersIteration;
+          }
+        }
+        reqHeaders[defHeaderName] = defHeaders[defHeaderName];
+      }
+
       // strip content-type if data is undefined
       if (isUndefined(config.data)) {
-        delete reqHeaders['Content-Type'];
+        for(var header in reqHeaders) {
+          if (lowercase(header) === 'content-type') {
+            delete reqHeaders[header];
+            break;
+          }
+        }
       }
 
+      reqData = transformData(config.data, headersGetter(reqHeaders), reqTransformFn);
+
       // send request
       promise = sendReq(config, reqData, reqHeaders);
 
@@ -9475,17 +9662,15 @@ function $TimeoutProvider() {
           deferred.reject(e);
           $exceptionHandler(e);
         }
+        finally {
+          delete deferreds[promise.$$timeoutId];
+        }
 
         if (!skipApply) $rootScope.$apply();
       }, delay);
 
-      cleanup = function() {
-        delete deferreds[promise.$$timeoutId];
-      };
-
       promise.$$timeoutId = timeoutId;
       deferreds[timeoutId] = deferred;
-      promise.then(cleanup, cleanup);
 
       return promise;
     }
@@ -9507,6 +9692,7 @@ function $TimeoutProvider() {
     timeout.cancel = function(promise) {
       if (promise && promise.$$timeoutId in deferreds) {
         deferreds[promise.$$timeoutId].reject('canceled');
+        delete deferreds[promise.$$timeoutId];
         return $browser.defer.cancel(promise.$$timeoutId);
       }
       return false;
@@ -9843,7 +10029,9 @@ function currencyFilter($locale) {
  * If the input is not a number an empty string is returned.
  *
  * @param {number|string} number Number to format.
- * @param {(number|string)=} [fractionSize=2] Number of decimal places to round the number to.
+ * @param {(number|string)=} fractionSize Number of decimal places to round the number to.
+ * If this is not provided then the fraction size is computed from the current locale's number
+ * formatting pattern. In the case of the default locale, it will be 3.
  * @returns {string} Number rounded to decimalPlaces and places a “,” after each third digit.
  *
  * @example
@@ -9950,6 +10138,11 @@ function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
     }
 
     if (fractionSize && fractionSize !== "0") formatedText += decimalSep + fraction.substr(0, fractionSize);
+  } else {
+
+    if (fractionSize > 0 && number > -1 && number < 1) {
+      formatedText = number.toFixed(fractionSize);
+    }
   }
 
   parts.push(isNegative ? pattern.negPre : pattern.posPre);
@@ -10073,7 +10266,7 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+
  *   * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US  locale (e.g. 9/3/10 12:05 pm)
  *   * `'fullDate'`: equivalent to `'EEEE, MMMM d,y'` for en_US  locale
  *     (e.g. Friday, September 3, 2010)
- *   * `'longDate'`: equivalent to `'MMMM d, y'` for en_US  locale (e.g. September 3, 2010
+ *   * `'longDate'`: equivalent to `'MMMM d, y'` for en_US  locale (e.g. September 3, 2010)
  *   * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US  locale (e.g. Sep 3, 2010)
  *   * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10)
  *   * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 pm)
@@ -10081,7 +10274,7 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+
  *
  *   `format` string can contain literal values. These need to be quoted with single quotes (e.g.
  *   `"h 'in the morning'"`). In order to output single quote, use two single quotes in a sequence
- *   (e.g. `"h o''clock"`).
+ *   (e.g. `"h 'o''clock'"`).
  *
  * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
  *    number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.SSSZ and its
@@ -10448,8 +10641,10 @@ function orderByFilter($parse){
       var t1 = typeof v1;
       var t2 = typeof v2;
       if (t1 == t2) {
-        if (t1 == "string") v1 = v1.toLowerCase();
-        if (t1 == "string") v2 = v2.toLowerCase();
+        if (t1 == "string") {
+           v1 = v1.toLowerCase();
+           v2 = v2.toLowerCase();
+        }
         if (v1 === v2) return 0;
         return v1 < v2 ? -1 : 1;
       } else {
@@ -10869,7 +11064,7 @@ function FormController(element, attrs) {
       errors = form.$error = {};
 
   // init state
-  form.$name = attrs.name;
+  form.$name = attrs.name || attrs.ngForm;
   form.$dirty = false;
   form.$pristine = true;
   form.$valid = true;
@@ -10889,12 +11084,32 @@ function FormController(element, attrs) {
       addClass((isValid ? VALID_CLASS : INVALID_CLASS) + validationErrorKey);
   }
 
+  /**
+   * @ngdoc function
+   * @name ng.directive:form.FormController#$addControl
+   * @methodOf ng.directive:form.FormController
+   *
+   * @description
+   * Register a control with the form.
+   *
+   * Input elements using ngModelController do this automatically when they are linked.
+   */
   form.$addControl = function(control) {
     if (control.$name && !form.hasOwnProperty(control.$name)) {
       form[control.$name] = control;
     }
   };
 
+  /**
+   * @ngdoc function
+   * @name ng.directive:form.FormController#$removeControl
+   * @methodOf ng.directive:form.FormController
+   *
+   * @description
+   * Deregister a control from the form.
+   *
+   * Input elements using ngModelController do this automatically when they are destroyed.
+   */
   form.$removeControl = function(control) {
     if (control.$name && form[control.$name] === control) {
       delete form[control.$name];
@@ -10904,6 +11119,16 @@ function FormController(element, attrs) {
     });
   };
 
+  /**
+   * @ngdoc function
+   * @name ng.directive:form.FormController#$setValidity
+   * @methodOf ng.directive:form.FormController
+   *
+   * @description
+   * Sets the validity of a form control.
+   *
+   * This method will also propagate to parent forms.
+   */
   form.$setValidity = function(validationToken, isValid, control) {
     var queue = errors[validationToken];
 
@@ -10942,6 +11167,17 @@ function FormController(element, attrs) {
     }
   };
 
+  /**
+   * @ngdoc function
+   * @name ng.directive:form.FormController#$setDirty
+   * @methodOf ng.directive:form.FormController
+   *
+   * @description
+   * Sets the form to a dirty state.
+   *
+   * This method can be called to add the 'ng-dirty' class and set the form to a dirty
+   * state (ng-dirty class). This method will also propagate to parent forms.
+   */
   form.$setDirty = function() {
     element.removeClass(PRISTINE_CLASS).addClass(DIRTY_CLASS);
     form.$dirty = true;
@@ -11118,7 +11354,7 @@ var formDirective = formDirectiveFactory();
 var ngFormDirective = formDirectiveFactory(true);
 
 var URL_REGEXP = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/;
-var EMAIL_REGEXP = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/;
+var EMAIL_REGEXP = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,6}$/;
 var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/;
 
 var inputType = {
@@ -11229,9 +11465,9 @@ var inputType = {
          
Number: - + Required! - + Not valid number! value = {{value}}
myForm.input.$valid = {{myForm.input.$valid}}
@@ -11352,6 +11588,8 @@ var inputType = { * @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. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. * * @example @@ -11910,12 +12148,25 @@ var VALID_CLASS = 'ng-valid', * * @property {string} $viewValue Actual string value in the view. * @property {*} $modelValue The value in the model, that the control is bound to. - * @property {Array.} $parsers Whenever the control reads value from the DOM, it executes - * all of these functions to sanitize / convert the value as well as validate. - * - * @property {Array.} $formatters Whenever the model value changes, it executes all of - * these functions to convert the value as well as validate. + * @property {Array.} $parsers Array of functions to execute, as a pipeline, whenever + the control reads value from the DOM. Each function is called, in turn, passing the value + through to the next. Used to sanitize / convert the value as well as validation. + + For validation, the parsers should update the validity state using + {@link ng.directive:ngModel.NgModelController#$setValidity $setValidity()}, + and return `undefined` for invalid values. * + * @property {Array.} $formatters Array of functions to execute, as a pipeline, whenever + * the model value changes. Each function is called, in turn, passing the value through to the + * next. Used to format / convert values for display in the control and validation. + *
+ *      function formatter(value) {
+ *        if (value) {
+ *          return value.toUpperCase();
+ *        }
+ *      }
+ *      ngModel.$formatters.push(formatter);
+ *      
* @property {Object} $error An bject hash with all errors as keys. * * @property {boolean} $pristine True if user has not interacted with the control yet. @@ -11930,6 +12181,10 @@ var VALID_CLASS = 'ng-valid', * specifically does not contain any logic which deals with DOM rendering or listening to * DOM events. The `NgModelController` is meant to be extended by other directives where, the * directive provides DOM manipulation and the `NgModelController` provides the data-binding. + * Note that you cannot use `NgModelController` in a directive with an isolated scope, + * as, in that case, the `ng-model` value gets put into the isolated scope and does not get + * propogated to the parent scope. + * * * This example shows how to use `NgModelController` with a custom control to achieve * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`) @@ -11970,7 +12225,13 @@ var VALID_CLASS = 'ng-valid', // Write data to the model function read() { - ngModel.$setViewValue(element.html()); + var html = element.html(); + // When we clear the content editable the browser leaves a
behind + // If strip-br attribute is provided then we strip this out + if( attrs.stripBr && html == '
' ) { + html = ''; + } + ngModel.$setViewValue(html); } } }; @@ -11980,6 +12241,7 @@ var VALID_CLASS = 'ng-valid',
Change me!
Required!
@@ -12102,8 +12364,8 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$ * For example {@link ng.directive:input input} or * {@link ng.directive:select select} directives call it. * - * It internally calls all `parsers` and if resulted value is valid, updates the model and - * calls all registered change listeners. + * It internally calls all `$parsers` (including validators) and updates the `$modelValue` and the actual model path. + * Lastly it calls all registered change listeners. * * @param {string} value Value from the view. */ @@ -12168,7 +12430,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$ * @element input * * @description - * Is directive that tells Angular to do two-way data binding. It works together with `input`, + * Is a directive that tells Angular to do two-way data binding. It works together with `input`, * `select`, `textarea`. You can easily write your own directives to use `ngModel` as well. * * `ngModel` is responsible for: @@ -12180,6 +12442,10 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$ * - setting related css class onto the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`), * - register the control with parent {@link ng.directive:form form}. * + * Note: `ngModel` will try to bind to the property given by evaluating the expression on the + * current scope. If the property doesn't already exist on this scope, it will be created + * implicitly and added to the scope. + * * For basic examples, how to use `ngModel`, see: * * - {@link ng.directive:input input} @@ -12320,8 +12586,9 @@ var requiredDirective = function() { List: - + Required! +
names = {{names}}
myForm.namesInput.$valid = {{myForm.namesInput.$valid}}
myForm.namesInput.$error = {{myForm.namesInput.$error}}
@@ -12333,12 +12600,14 @@ var requiredDirective = function() { it('should initialize to model', function() { expect(binding('names')).toEqual('["igor","misko","vojta"]'); expect(binding('myForm.namesInput.$valid')).toEqual('true'); + expect(element('span.error').css('display')).toBe('none'); }); it('should be invalid if empty', function() { input('names').enter(''); expect(binding('names')).toEqual('[]'); expect(binding('myForm.namesInput.$valid')).toEqual('false'); + expect(element('span.error').css('display')).not().toBe('none'); });
@@ -12388,7 +12657,7 @@ var ngValueDirective = function() { } else { return function(scope, elm, attr) { scope.$watch(attr.ngValue, function valueWatchAction(value) { - attr.$set('value', value, false); + attr.$set('value', value); }); }; } @@ -12408,10 +12677,9 @@ var ngValueDirective = function() { * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like * `{{ expression }}` which is similar but less verbose. * - * One scenario in which the use of `ngBind` is preferred over `{{ expression }}` binding is when - * it's desirable to put bindings into template that is momentarily displayed by the browser in its - * raw state before Angular compiles it. Since `ngBind` is an element attribute, it makes the - * bindings invisible to the user while the page is loading. + * It is preferrable to use `ngBind` instead of `{{ expression }}` when a template is momentarily + * displayed by the browser in its raw state before Angular compiles it. Since `ngBind` is an + * element attribute, it makes the bindings invisible to the user while the page is loading. * * An alternative solution to this problem would be using the * {@link ng.directive:ngCloak ngCloak} directive. @@ -12457,10 +12725,11 @@ var ngBindDirective = ngDirective(function(scope, element, attr) { * * @description * The `ngBindTemplate` directive specifies that the element - * text should be replaced with the template in ngBindTemplate. - * Unlike ngBind the ngBindTemplate can contain multiple `{{` `}}` - * expressions. (This is required since some HTML elements - * can not have SPAN elements such as TITLE, or OPTION to name a few.) + * text content should be replaced with the interpolation of the template + * in the `ngBindTemplate` attribute. + * Unlike `ngBind`, the `ngBindTemplate` can contain multiple `{{` `}}` + * expressions. This directive is needed since some HTML elements + * (such as TITLE and OPTION) cannot contain SPAN elements. * * @element ANY * @param {string} ngBindTemplate template of form @@ -12596,8 +12865,8 @@ function classDirective(name, selector) { * @name ng.directive:ngClass * * @description - * The `ngClass` allows you to set CSS class on HTML element dynamically by databinding an - * expression that represents all classes to be added. + * The `ngClass` allows you to set CSS classes on HTML an element, dynamically, by databinding + * an expression that represents all classes to be added. * * The directive won't add duplicate classes if a particular class was already set. * @@ -12607,7 +12876,9 @@ function classDirective(name, selector) { * @element ANY * @param {expression} ngClass {@link guide/expression Expression} to eval. The result * of the evaluation can be a string representing space delimited class - * names, an array, or a map of class names to boolean values. + * names, an array, or a map of class names to boolean values. In the case of a map, the + * names of the properties whose values are truthy will be added as css classes to the + * element. * * @example @@ -12753,7 +13024,7 @@ var ngClassEvenDirective = classDirective('Even', 1); * *
  * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
- *   display: none;
+ *   display: none !important;
  * }
  * 
* @@ -12821,11 +13092,9 @@ var ngCloakDirective = ngDirective({ * * @example * Here is a simple form for editing user contact information. Adding, removing, clearing, and - * greeting are methods declared on the controller (see source tab). These methods can - * easily be called from the angular markup. Notice that the scope becomes the `this` for the - * controller's instance. This allows for easy access to the view data from the controller. Also - * notice that any changes to the data are automatically reflected in the View without the need - * for a manual update. + * greeting are methods declared on the $scope by the controller (see source tab). These methods can + * easily be called from the angular markup. Notice that any changes to the data are automatically + * reflected in the View without the need for a manual update.