angular-gem 1.3.2 → 1.3.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (27) hide show
  1. checksums.yaml +8 -8
  2. data/lib/angular-gem/version.rb +1 -1
  3. data/vendor/assets/javascripts/1.3.4/angular-animate.js +2136 -0
  4. data/vendor/assets/javascripts/1.3.4/angular-aria.js +321 -0
  5. data/vendor/assets/javascripts/1.3.4/angular-cookies.js +206 -0
  6. data/vendor/assets/javascripts/1.3.4/angular-loader.js +405 -0
  7. data/vendor/assets/javascripts/1.3.4/angular-messages.js +400 -0
  8. data/vendor/assets/javascripts/1.3.4/angular-mocks.js +2380 -0
  9. data/vendor/assets/javascripts/1.3.4/angular-resource.js +667 -0
  10. data/vendor/assets/javascripts/1.3.4/angular-route.js +996 -0
  11. data/vendor/assets/javascripts/1.3.4/angular-sanitize.js +678 -0
  12. data/vendor/assets/javascripts/1.3.4/angular-scenario.js +37269 -0
  13. data/vendor/assets/javascripts/1.3.4/angular-touch.js +622 -0
  14. data/vendor/assets/javascripts/1.3.4/angular.js +25915 -0
  15. data/vendor/assets/javascripts/angular-animate.js +15 -15
  16. data/vendor/assets/javascripts/angular-aria.js +83 -23
  17. data/vendor/assets/javascripts/angular-cookies.js +1 -1
  18. data/vendor/assets/javascripts/angular-loader.js +6 -23
  19. data/vendor/assets/javascripts/angular-messages.js +1 -1
  20. data/vendor/assets/javascripts/angular-mocks.js +21 -17
  21. data/vendor/assets/javascripts/angular-resource.js +1 -1
  22. data/vendor/assets/javascripts/angular-route.js +21 -7
  23. data/vendor/assets/javascripts/angular-sanitize.js +26 -26
  24. data/vendor/assets/javascripts/angular-scenario.js +646 -453
  25. data/vendor/assets/javascripts/angular-touch.js +3 -3
  26. data/vendor/assets/javascripts/angular.js +641 -448
  27. metadata +14 -2
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license AngularJS v1.3.2
2
+ * @license AngularJS v1.3.4
3
3
  * (c) 2010-2014 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
@@ -301,7 +301,7 @@ ngTouch.directive('ngClick', ['$parse', '$timeout', '$rootElement',
301
301
  // Splices out the allowable region from the list after it has been used.
302
302
  function checkAllowableRegions(touchCoordinates, x, y) {
303
303
  for (var i = 0; i < touchCoordinates.length; i += 2) {
304
- if (hit(touchCoordinates[i], touchCoordinates[i+1], x, y)) {
304
+ if (hit(touchCoordinates[i], touchCoordinates[i + 1], x, y)) {
305
305
  touchCoordinates.splice(i, i + 2);
306
306
  return true; // allowable region
307
307
  }
@@ -366,7 +366,7 @@ ngTouch.directive('ngClick', ['$parse', '$timeout', '$rootElement',
366
366
  $timeout(function() {
367
367
  // Remove the allowable region.
368
368
  for (var i = 0; i < touchCoordinates.length; i += 2) {
369
- if (touchCoordinates[i] == x && touchCoordinates[i+1] == y) {
369
+ if (touchCoordinates[i] == x && touchCoordinates[i + 1] == y) {
370
370
  touchCoordinates.splice(i, i + 2);
371
371
  return;
372
372
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license AngularJS v1.3.2
2
+ * @license AngularJS v1.3.4
3
3
  * (c) 2010-2014 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
@@ -42,40 +42,23 @@ function minErr(module, ErrorConstructor) {
42
42
  prefix = '[' + (module ? module + ':' : '') + code + '] ',
43
43
  template = arguments[1],
44
44
  templateArgs = arguments,
45
- stringify = function(obj) {
46
- if (typeof obj === 'function') {
47
- return obj.toString().replace(/ \{[\s\S]*$/, '');
48
- } else if (typeof obj === 'undefined') {
49
- return 'undefined';
50
- } else if (typeof obj !== 'string') {
51
- return JSON.stringify(obj);
52
- }
53
- return obj;
54
- },
45
+
55
46
  message, i;
56
47
 
57
48
  message = prefix + template.replace(/\{\d+\}/g, function(match) {
58
49
  var index = +match.slice(1, -1), arg;
59
50
 
60
51
  if (index + 2 < templateArgs.length) {
61
- arg = templateArgs[index + 2];
62
- if (typeof arg === 'function') {
63
- return arg.toString().replace(/ ?\{[\s\S]*$/, '');
64
- } else if (typeof arg === 'undefined') {
65
- return 'undefined';
66
- } else if (typeof arg !== 'string') {
67
- return toJson(arg);
68
- }
69
- return arg;
52
+ return toDebugString(templateArgs[index + 2]);
70
53
  }
71
54
  return match;
72
55
  });
73
56
 
74
- message = message + '\nhttp://errors.angularjs.org/1.3.2/' +
57
+ message = message + '\nhttp://errors.angularjs.org/1.3.4/' +
75
58
  (module ? module + '/' : '') + code;
76
59
  for (i = 2; i < arguments.length; i++) {
77
- message = message + (i == 2 ? '?' : '&') + 'p' + (i-2) + '=' +
78
- encodeURIComponent(stringify(arguments[i]));
60
+ message = message + (i == 2 ? '?' : '&') + 'p' + (i - 2) + '=' +
61
+ encodeURIComponent(toDebugString(arguments[i]));
79
62
  }
80
63
  return new ErrorConstructor(message);
81
64
  };
@@ -443,7 +426,7 @@ function int(str) {
443
426
 
444
427
 
445
428
  function inherit(parent, extra) {
446
- return extend(new (extend(function() {}, {prototype:parent}))(), extra);
429
+ return extend(Object.create(parent), extra);
447
430
  }
448
431
 
449
432
  /**
@@ -706,7 +689,7 @@ function makeMap(str) {
706
689
 
707
690
 
708
691
  function nodeName_(element) {
709
- return lowercase(element.nodeName || element[0].nodeName);
692
+ return lowercase(element.nodeName || (element[0] && element[0].nodeName));
710
693
  }
711
694
 
712
695
  function includes(array, obj) {
@@ -715,7 +698,7 @@ function includes(array, obj) {
715
698
 
716
699
  function arrayRemove(array, value) {
717
700
  var index = array.indexOf(value);
718
- if (index >=0)
701
+ if (index >= 0)
719
702
  array.splice(index, 1);
720
703
  return value;
721
704
  }
@@ -916,7 +899,7 @@ function equals(o1, o2) {
916
899
  if (isArray(o1)) {
917
900
  if (!isArray(o2)) return false;
918
901
  if ((length = o1.length) == o2.length) {
919
- for (key=0; key<length; key++) {
902
+ for (key = 0; key < length; key++) {
920
903
  if (!equals(o1[key], o2[key])) return false;
921
904
  }
922
905
  return true;
@@ -1002,7 +985,7 @@ function bind(self, fn) {
1002
985
  return curryArgs.length
1003
986
  ? function() {
1004
987
  return arguments.length
1005
- ? fn.apply(self, curryArgs.concat(slice.call(arguments, 0)))
988
+ ? fn.apply(self, concat(curryArgs, arguments, 0))
1006
989
  : fn.apply(self, curryArgs);
1007
990
  }
1008
991
  : function() {
@@ -1202,7 +1185,7 @@ var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-'];
1202
1185
  function getNgAttribute(element, ngAttr) {
1203
1186
  var attr, i, ii = ngAttrPrefixes.length;
1204
1187
  element = jqLite(element);
1205
- for (i=0; i<ii; ++i) {
1188
+ for (i = 0; i < ii; ++i) {
1206
1189
  attr = ngAttrPrefixes[i] + ngAttr;
1207
1190
  if (isString(attr = element.attr(attr))) {
1208
1191
  return attr;
@@ -1412,8 +1395,8 @@ function angularInit(element, bootstrap) {
1412
1395
  * @param {Object=} config an object for defining configuration options for the application. The
1413
1396
  * following keys are supported:
1414
1397
  *
1415
- * - `strictDi`: disable automatic function annotation for the application. This is meant to
1416
- * assist in finding bugs which break minified code.
1398
+ * * `strictDi` - disable automatic function annotation for the application. This is meant to
1399
+ * assist in finding bugs which break minified code. Defaults to `false`.
1417
1400
  *
1418
1401
  * @returns {auto.$injector} Returns the newly created injector for this app.
1419
1402
  */
@@ -1987,6 +1970,34 @@ function setupModuleLoader(window) {
1987
1970
 
1988
1971
  }
1989
1972
 
1973
+ /* global: toDebugString: true */
1974
+
1975
+ function serializeObject(obj) {
1976
+ var seen = [];
1977
+
1978
+ return JSON.stringify(obj, function(key, val) {
1979
+ val = toJsonReplacer(key, val);
1980
+ if (isObject(val)) {
1981
+
1982
+ if (seen.indexOf(val) >= 0) return '<<already seen>>';
1983
+
1984
+ seen.push(val);
1985
+ }
1986
+ return val;
1987
+ });
1988
+ }
1989
+
1990
+ function toDebugString(obj) {
1991
+ if (typeof obj === 'function') {
1992
+ return obj.toString().replace(/ \{[\s\S]*$/, '');
1993
+ } else if (typeof obj === 'undefined') {
1994
+ return 'undefined';
1995
+ } else if (typeof obj !== 'string') {
1996
+ return serializeObject(obj);
1997
+ }
1998
+ return obj;
1999
+ }
2000
+
1990
2001
  /* global angularModule: true,
1991
2002
  version: true,
1992
2003
 
@@ -2089,11 +2100,11 @@ function setupModuleLoader(window) {
2089
2100
  * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
2090
2101
  */
2091
2102
  var version = {
2092
- full: '1.3.2', // all of these placeholder strings will be replaced by grunt's
2103
+ full: '1.3.4', // all of these placeholder strings will be replaced by grunt's
2093
2104
  major: 1, // package task
2094
2105
  minor: 3,
2095
- dot: 2,
2096
- codeName: 'cardiovasculatory-magnification'
2106
+ dot: 4,
2107
+ codeName: 'highfalutin-petroglyph'
2097
2108
  };
2098
2109
 
2099
2110
 
@@ -2316,10 +2327,12 @@ function publishExternalAPI(angular) {
2316
2327
  * `'ngModel'`).
2317
2328
  * - `injector()` - retrieves the injector of the current element or its parent.
2318
2329
  * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current
2319
- * element or its parent.
2330
+ * element or its parent. Requires {@link guide/production#disabling-debug-data Debug Data} to
2331
+ * be enabled.
2320
2332
  * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the
2321
2333
  * current element. This getter should be used only on elements that contain a directive which starts a new isolate
2322
2334
  * scope. Calling `scope()` on this element always returns the original non-isolate scope.
2335
+ * Requires {@link guide/production#disabling-debug-data Debug Data} to be enabled.
2323
2336
  * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
2324
2337
  * parent element is reached.
2325
2338
  *
@@ -2826,7 +2839,7 @@ forEach({
2826
2839
  }
2827
2840
  } else {
2828
2841
  return (element[name] ||
2829
- (element.attributes.getNamedItem(name)|| noop).specified)
2842
+ (element.attributes.getNamedItem(name) || noop).specified)
2830
2843
  ? lowercasedName
2831
2844
  : undefined;
2832
2845
  }
@@ -3314,9 +3327,10 @@ HashMap.prototype = {
3314
3327
  * Creates an injector object that can be used for retrieving services as well as for
3315
3328
  * dependency injection (see {@link guide/di dependency injection}).
3316
3329
  *
3317
-
3318
3330
  * @param {Array.<string|Function>} modules A list of module functions or their aliases. See
3319
- * {@link angular.module}. The `ng` module must be explicitly added.
3331
+ * {@link angular.module}. The `ng` module must be explicitly added.
3332
+ * @param {boolean=} [strictDi=false] Whether the injector should be in strict mode, which
3333
+ * disallows argument name annotation inference.
3320
3334
  * @returns {injector} Injector object. See {@link auto.$injector $injector}.
3321
3335
  *
3322
3336
  * @example
@@ -3462,8 +3476,10 @@ function annotate(fn, strictDi, name) {
3462
3476
  * ## Inference
3463
3477
  *
3464
3478
  * In JavaScript calling `toString()` on a function returns the function definition. The definition
3465
- * can then be parsed and the function arguments can be extracted. *NOTE:* This does not work with
3466
- * minification, and obfuscation tools since these tools change the argument names.
3479
+ * can then be parsed and the function arguments can be extracted. This method of discovering
3480
+ * annotations is disallowed when the injector is in strict mode.
3481
+ * *NOTE:* This does not work with minification, and obfuscation tools since these tools change the
3482
+ * argument names.
3467
3483
  *
3468
3484
  * ## `$inject` Annotation
3469
3485
  * By adding an `$inject` property onto a function the injection parameters can be specified.
@@ -3548,6 +3564,8 @@ function annotate(fn, strictDi, name) {
3548
3564
  * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
3549
3565
  * ```
3550
3566
  *
3567
+ * You can disallow this method by using strict injection mode.
3568
+ *
3551
3569
  * This method does not work with code minification / obfuscation. For this reason the following
3552
3570
  * annotation strategies are supported.
3553
3571
  *
@@ -3600,6 +3618,8 @@ function annotate(fn, strictDi, name) {
3600
3618
  * @param {Function|Array.<string|Function>} fn Function for which dependent service names need to
3601
3619
  * be retrieved as described above.
3602
3620
  *
3621
+ * @param {boolean=} [strictDi=false] Disallow argument name annotation inference.
3622
+ *
3603
3623
  * @returns {Array.<string>} The names of the services which the function requires.
3604
3624
  */
3605
3625
 
@@ -4119,14 +4139,11 @@ function createInjector(modulesToLoad, strictDi) {
4119
4139
  }
4120
4140
 
4121
4141
  function instantiate(Type, locals, serviceName) {
4122
- var Constructor = function() {},
4123
- instance, returnedValue;
4124
-
4125
4142
  // Check if Type is annotated and use just the given function at n-1 as parameter
4126
4143
  // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
4127
- Constructor.prototype = (isArray(Type) ? Type[Type.length - 1] : Type).prototype;
4128
- instance = new Constructor();
4129
- returnedValue = invoke(Type, instance, locals, serviceName);
4144
+ // Object creation: http://jsperf.com/create-constructor/2
4145
+ var instance = Object.create((isArray(Type) ? Type[Type.length - 1] : Type).prototype);
4146
+ var returnedValue = invoke(Type, instance, locals, serviceName);
4130
4147
 
4131
4148
  return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance;
4132
4149
  }
@@ -4162,7 +4179,7 @@ function $AnchorScrollProvider() {
4162
4179
  * @name $anchorScrollProvider#disableAutoScrolling
4163
4180
  *
4164
4181
  * @description
4165
- * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically will detect changes to
4182
+ * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to
4166
4183
  * {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.<br />
4167
4184
  * Use this method to disable automatic scrolling.
4168
4185
  *
@@ -4811,8 +4828,7 @@ function $$AsyncCallbackProvider() {
4811
4828
  /**
4812
4829
  * @param {object} window The global window object.
4813
4830
  * @param {object} document jQuery wrapped document.
4814
- * @param {function()} XHR XMLHttpRequest constructor.
4815
- * @param {object} $log console.log or an object with the same interface.
4831
+ * @param {object} $log window.console or an object with the same interface.
4816
4832
  * @param {object} $sniffer $sniffer service
4817
4833
  */
4818
4834
  function Browser(window, document, $log, $sniffer) {
@@ -4963,7 +4979,7 @@ function Browser(window, document, $log, $sniffer) {
4963
4979
  // IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode.
4964
4980
  // See https://github.com/angular/angular.js/commit/ffb2701
4965
4981
  if (lastBrowserUrl === url && (!$sniffer.history || sameState)) {
4966
- return;
4982
+ return self;
4967
4983
  }
4968
4984
  var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url);
4969
4985
  lastBrowserUrl = url;
@@ -5162,8 +5178,8 @@ function Browser(window, document, $log, $sniffer) {
5162
5178
  // - 20 cookies per unique domain
5163
5179
  // - 4096 bytes per cookie
5164
5180
  if (cookieLength > 4096) {
5165
- $log.warn("Cookie '"+ name +
5166
- "' possibly not set or overflowed because it was too large ("+
5181
+ $log.warn("Cookie '" + name +
5182
+ "' possibly not set or overflowed because it was too large (" +
5167
5183
  cookieLength + " > 4096 bytes)!");
5168
5184
  }
5169
5185
  }
@@ -5828,7 +5844,7 @@ function $TemplateCacheProvider() {
5828
5844
  *
5829
5845
  *
5830
5846
  * #### `bindToController`
5831
- * When an isolate scope is used for a component (see above), and `controllerAs` is used, `bindToController` will
5847
+ * When an isolate scope is used for a component (see above), and `controllerAs` is used, `bindToController: true` will
5832
5848
  * allow a component to have its properties bound to the controller, rather than to scope. When the controller
5833
5849
  * is instantiated, the initial values of the isolate scope bindings are already available.
5834
5850
  *
@@ -6677,16 +6693,16 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
6677
6693
 
6678
6694
  // for each tuples
6679
6695
  var nbrUrisWith2parts = Math.floor(rawUris.length / 2);
6680
- for (var i=0; i<nbrUrisWith2parts; i++) {
6681
- var innerIdx = i*2;
6696
+ for (var i = 0; i < nbrUrisWith2parts; i++) {
6697
+ var innerIdx = i * 2;
6682
6698
  // sanitize the uri
6683
6699
  result += $$sanitizeUri(trim(rawUris[innerIdx]), true);
6684
6700
  // add the descriptor
6685
- result += (" " + trim(rawUris[innerIdx+1]));
6701
+ result += (" " + trim(rawUris[innerIdx + 1]));
6686
6702
  }
6687
6703
 
6688
6704
  // split the last item into uri and descriptor
6689
- var lastTuple = trim(rawUris[i*2]).split(/\s/);
6705
+ var lastTuple = trim(rawUris[i * 2]).split(/\s/);
6690
6706
 
6691
6707
  // sanitize the last uri
6692
6708
  result += $$sanitizeUri(trim(lastTuple[0]), true);
@@ -6880,7 +6896,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
6880
6896
  if (!node) {
6881
6897
  return 'html';
6882
6898
  } else {
6883
- return nodeName_(node) !== 'foreignobject' && node.toString().match(/SVG/) ? 'svg': 'html';
6899
+ return nodeName_(node) !== 'foreignobject' && node.toString().match(/SVG/) ? 'svg' : 'html';
6884
6900
  }
6885
6901
  }
6886
6902
 
@@ -7693,7 +7709,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
7693
7709
  var match = null;
7694
7710
  if (hasDirectives.hasOwnProperty(name)) {
7695
7711
  for (var directive, directives = $injector.get(name + Suffix),
7696
- i = 0, ii = directives.length; i<ii; i++) {
7712
+ i = 0, ii = directives.length; i < ii; i++) {
7697
7713
  try {
7698
7714
  directive = directives[i];
7699
7715
  if ((maxPriority === undefined || maxPriority > directive.priority) &&
@@ -7722,7 +7738,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
7722
7738
  function directiveIsMultiElement(name) {
7723
7739
  if (hasDirectives.hasOwnProperty(name)) {
7724
7740
  for (var directive, directives = $injector.get(name + Suffix),
7725
- i = 0, ii = directives.length; i<ii; i++) {
7741
+ i = 0, ii = directives.length; i < ii; i++) {
7726
7742
  directive = directives[i];
7727
7743
  if (directive.multiElement) {
7728
7744
  return true;
@@ -7875,10 +7891,10 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
7875
7891
  var childBoundTranscludeFn = boundTranscludeFn;
7876
7892
  if (scope.$$destroyed) return;
7877
7893
  if (linkQueue) {
7878
- linkQueue.push(scope);
7879
- linkQueue.push(node);
7880
- linkQueue.push(rootElement);
7881
- linkQueue.push(childBoundTranscludeFn);
7894
+ linkQueue.push(scope,
7895
+ node,
7896
+ rootElement,
7897
+ childBoundTranscludeFn);
7882
7898
  } else {
7883
7899
  if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
7884
7900
  childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
@@ -7941,7 +7957,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
7941
7957
  case 'svg':
7942
7958
  case 'math':
7943
7959
  var wrapper = document.createElement('div');
7944
- wrapper.innerHTML = '<'+type+'>'+template+'</'+type+'>';
7960
+ wrapper.innerHTML = '<' + type + '>' + template + '</' + type + '>';
7945
7961
  return wrapper.childNodes[0].childNodes;
7946
7962
  default:
7947
7963
  return template;
@@ -8298,6 +8314,10 @@ function $ControllerProvider() {
8298
8314
  * * if $controllerProvider#allowGlobals, check `window[constructor]` on the global
8299
8315
  * `window` object (not recommended)
8300
8316
  *
8317
+ * The string can use the `controller as property` syntax, where the controller instance is published
8318
+ * as the specified property on the `scope`; the `scope` must be injected into `locals` param for this
8319
+ * to work correctly.
8320
+ *
8301
8321
  * @param {Object} locals Injection locals for Controller.
8302
8322
  * @return {Object} Instance of given controller.
8303
8323
  *
@@ -8343,10 +8363,10 @@ function $ControllerProvider() {
8343
8363
  //
8344
8364
  // This feature is not intended for use by applications, and is thus not documented
8345
8365
  // publicly.
8346
- var Constructor = function() {};
8347
- Constructor.prototype = (isArray(expression) ?
8366
+ // Object creation: http://jsperf.com/create-constructor/2
8367
+ var controllerPrototype = (isArray(expression) ?
8348
8368
  expression[expression.length - 1] : expression).prototype;
8349
- instance = new Constructor();
8369
+ instance = Object.create(controllerPrototype);
8350
8370
 
8351
8371
  if (identifier) {
8352
8372
  addIdentifier(locals, identifier, instance, constructor || expression.name);
@@ -8472,7 +8492,7 @@ function defaultHttpResponseTransform(data, headers) {
8472
8492
  // strip json vulnerability protection prefix
8473
8493
  data = data.replace(JSON_PROTECTION_PREFIX, '');
8474
8494
  var contentType = headers('Content-Type');
8475
- if ((contentType && contentType.indexOf(APPLICATION_JSON) === 0) ||
8495
+ if ((contentType && contentType.indexOf(APPLICATION_JSON) === 0 && data.trim()) ||
8476
8496
  (JSON_START.test(data) && JSON_END.test(data))) {
8477
8497
  data = fromJson(data);
8478
8498
  }
@@ -8487,7 +8507,7 @@ function defaultHttpResponseTransform(data, headers) {
8487
8507
  * @returns {Object} Parsed headers as key value object
8488
8508
  */
8489
8509
  function parseHeaders(headers) {
8490
- var parsed = {}, key, val, i;
8510
+ var parsed = createMap(), key, val, i;
8491
8511
 
8492
8512
  if (!headers) return parsed;
8493
8513
 
@@ -8524,7 +8544,11 @@ function headersGetter(headers) {
8524
8544
  if (!headersObj) headersObj = parseHeaders(headers);
8525
8545
 
8526
8546
  if (name) {
8527
- return headersObj[lowercase(name)] || null;
8547
+ var value = headersObj[lowercase(name)];
8548
+ if (value === void 0) {
8549
+ value = null;
8550
+ }
8551
+ return value;
8528
8552
  }
8529
8553
 
8530
8554
  return headersObj;
@@ -8573,6 +8597,11 @@ function $HttpProvider() {
8573
8597
  *
8574
8598
  * Object containing default values for all {@link ng.$http $http} requests.
8575
8599
  *
8600
+ * - **`defaults.cache`** - {Object} - an object built with {@link ng.$cacheFactory `$cacheFactory`}
8601
+ * that will provide the cache for all requests who set their `cache` property to `true`.
8602
+ * If you set the `default.cache = false` then only requests that specify their own custom
8603
+ * cache object will be cached. See {@link $http#caching $http Caching} for more information.
8604
+ *
8576
8605
  * - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token.
8577
8606
  * Defaults value is `'XSRF-TOKEN'`.
8578
8607
  *
@@ -8586,6 +8615,7 @@ function $HttpProvider() {
8586
8615
  * - **`defaults.headers.post`**
8587
8616
  * - **`defaults.headers.put`**
8588
8617
  * - **`defaults.headers.patch`**
8618
+ *
8589
8619
  **/
8590
8620
  var defaults = this.defaults = {
8591
8621
  // transform incoming response data
@@ -8800,6 +8830,21 @@ function $HttpProvider() {
8800
8830
  * In addition, you can supply a `headers` property in the config object passed when
8801
8831
  * calling `$http(config)`, which overrides the defaults without changing them globally.
8802
8832
  *
8833
+ * To explicitly remove a header automatically added via $httpProvider.defaults.headers on a per request basis,
8834
+ * Use the `headers` property, setting the desired header to `undefined`. For example:
8835
+ *
8836
+ * ```js
8837
+ * var req = {
8838
+ * method: 'POST',
8839
+ * url: 'http://example.com',
8840
+ * headers: {
8841
+ * 'Content-Type': undefined
8842
+ * },
8843
+ * data: { test: 'test' },
8844
+ * }
8845
+ *
8846
+ * $http(req).success(function(){...}).error(function(){...});
8847
+ * ```
8803
8848
  *
8804
8849
  * ## Transforming Requests and Responses
8805
8850
  *
@@ -9179,6 +9224,10 @@ function $HttpProvider() {
9179
9224
  };
9180
9225
  var headers = mergeHeaders(requestConfig);
9181
9226
 
9227
+ if (!angular.isObject(requestConfig)) {
9228
+ throw minErr('$http')('badreq', 'Http request configuration must be an object. Received: {0}', requestConfig);
9229
+ }
9230
+
9182
9231
  extend(config, requestConfig);
9183
9232
  config.headers = headers;
9184
9233
  config.method = uppercase(config.method);
@@ -10043,7 +10092,8 @@ function $InterpolateProvider() {
10043
10092
 
10044
10093
  function parseStringifyInterceptor(value) {
10045
10094
  try {
10046
- return stringify(getValue(value));
10095
+ value = getValue(value);
10096
+ return allOrNothing && !isDefined(value) ? value : stringify(value);
10047
10097
  } catch (err) {
10048
10098
  var newErr = $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text,
10049
10099
  err.toString());
@@ -10367,8 +10417,8 @@ function encodePath(path) {
10367
10417
  return segments.join('/');
10368
10418
  }
10369
10419
 
10370
- function parseAbsoluteUrl(absoluteUrl, locationObj, appBase) {
10371
- var parsedUrl = urlResolve(absoluteUrl, appBase);
10420
+ function parseAbsoluteUrl(absoluteUrl, locationObj) {
10421
+ var parsedUrl = urlResolve(absoluteUrl);
10372
10422
 
10373
10423
  locationObj.$$protocol = parsedUrl.protocol;
10374
10424
  locationObj.$$host = parsedUrl.hostname;
@@ -10376,12 +10426,12 @@ function parseAbsoluteUrl(absoluteUrl, locationObj, appBase) {
10376
10426
  }
10377
10427
 
10378
10428
 
10379
- function parseAppUrl(relativeUrl, locationObj, appBase) {
10429
+ function parseAppUrl(relativeUrl, locationObj) {
10380
10430
  var prefixed = (relativeUrl.charAt(0) !== '/');
10381
10431
  if (prefixed) {
10382
10432
  relativeUrl = '/' + relativeUrl;
10383
10433
  }
10384
- var match = urlResolve(relativeUrl, appBase);
10434
+ var match = urlResolve(relativeUrl);
10385
10435
  locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ?
10386
10436
  match.pathname.substring(1) : match.pathname);
10387
10437
  locationObj.$$search = parseKeyValue(match.search);
@@ -10436,7 +10486,7 @@ function LocationHtml5Url(appBase, basePrefix) {
10436
10486
  this.$$html5 = true;
10437
10487
  basePrefix = basePrefix || '';
10438
10488
  var appBaseNoFile = stripFile(appBase);
10439
- parseAbsoluteUrl(appBase, this, appBase);
10489
+ parseAbsoluteUrl(appBase, this);
10440
10490
 
10441
10491
 
10442
10492
  /**
@@ -10451,7 +10501,7 @@ function LocationHtml5Url(appBase, basePrefix) {
10451
10501
  appBaseNoFile);
10452
10502
  }
10453
10503
 
10454
- parseAppUrl(pathUrl, this, appBase);
10504
+ parseAppUrl(pathUrl, this);
10455
10505
 
10456
10506
  if (!this.$$path) {
10457
10507
  this.$$path = '/';
@@ -10514,7 +10564,7 @@ function LocationHtml5Url(appBase, basePrefix) {
10514
10564
  function LocationHashbangUrl(appBase, hashPrefix) {
10515
10565
  var appBaseNoFile = stripFile(appBase);
10516
10566
 
10517
- parseAbsoluteUrl(appBase, this, appBase);
10567
+ parseAbsoluteUrl(appBase, this);
10518
10568
 
10519
10569
 
10520
10570
  /**
@@ -10534,7 +10584,7 @@ function LocationHashbangUrl(appBase, hashPrefix) {
10534
10584
  throw $locationMinErr('ihshprfx', 'Invalid url "{0}", missing hash prefix "{1}".', url,
10535
10585
  hashPrefix);
10536
10586
  }
10537
- parseAppUrl(withoutHashUrl, this, appBase);
10587
+ parseAppUrl(withoutHashUrl, this);
10538
10588
 
10539
10589
  this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase);
10540
10590
 
@@ -10672,6 +10722,13 @@ var locationPrototype = {
10672
10722
  * Return full url representation with all segments encoded according to rules specified in
10673
10723
  * [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt).
10674
10724
  *
10725
+ *
10726
+ * ```js
10727
+ * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
10728
+ * var absUrl = $location.absUrl();
10729
+ * // => "http://example.com/#/some/path?foo=bar&baz=xoxo"
10730
+ * ```
10731
+ *
10675
10732
  * @return {string} full url
10676
10733
  */
10677
10734
  absUrl: locationGetter('$$absUrl'),
@@ -10687,6 +10744,13 @@ var locationPrototype = {
10687
10744
  *
10688
10745
  * Change path, search and hash, when called with parameter and return `$location`.
10689
10746
  *
10747
+ *
10748
+ * ```js
10749
+ * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
10750
+ * var url = $location.url();
10751
+ * // => "/some/path?foo=bar&baz=xoxo"
10752
+ * ```
10753
+ *
10690
10754
  * @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`)
10691
10755
  * @return {string} url
10692
10756
  */
@@ -10695,8 +10759,8 @@ var locationPrototype = {
10695
10759
  return this.$$url;
10696
10760
 
10697
10761
  var match = PATH_MATCH.exec(url);
10698
- if (match[1]) this.path(decodeURIComponent(match[1]));
10699
- if (match[2] || match[1]) this.search(match[3] || '');
10762
+ if (match[1] || url === '') this.path(decodeURIComponent(match[1]));
10763
+ if (match[2] || match[1] || url === '') this.search(match[3] || '');
10700
10764
  this.hash(match[5] || '');
10701
10765
 
10702
10766
  return this;
@@ -10711,6 +10775,13 @@ var locationPrototype = {
10711
10775
  *
10712
10776
  * Return protocol of current url.
10713
10777
  *
10778
+ *
10779
+ * ```js
10780
+ * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
10781
+ * var protocol = $location.protocol();
10782
+ * // => "http"
10783
+ * ```
10784
+ *
10714
10785
  * @return {string} protocol of current url
10715
10786
  */
10716
10787
  protocol: locationGetter('$$protocol'),
@@ -10724,6 +10795,13 @@ var locationPrototype = {
10724
10795
  *
10725
10796
  * Return host of current url.
10726
10797
  *
10798
+ *
10799
+ * ```js
10800
+ * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
10801
+ * var host = $location.host();
10802
+ * // => "example.com"
10803
+ * ```
10804
+ *
10727
10805
  * @return {string} host of current url.
10728
10806
  */
10729
10807
  host: locationGetter('$$host'),
@@ -10737,6 +10815,13 @@ var locationPrototype = {
10737
10815
  *
10738
10816
  * Return port of current url.
10739
10817
  *
10818
+ *
10819
+ * ```js
10820
+ * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
10821
+ * var port = $location.port();
10822
+ * // => 80
10823
+ * ```
10824
+ *
10740
10825
  * @return {Number} port
10741
10826
  */
10742
10827
  port: locationGetter('$$port'),
@@ -10755,6 +10840,13 @@ var locationPrototype = {
10755
10840
  * Note: Path should always begin with forward slash (/), this method will add the forward slash
10756
10841
  * if it is missing.
10757
10842
  *
10843
+ *
10844
+ * ```js
10845
+ * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
10846
+ * var path = $location.path();
10847
+ * // => "/some/path"
10848
+ * ```
10849
+ *
10758
10850
  * @param {(string|number)=} path New path
10759
10851
  * @return {string} path
10760
10852
  */
@@ -10780,10 +10872,9 @@ var locationPrototype = {
10780
10872
  * var searchObject = $location.search();
10781
10873
  * // => {foo: 'bar', baz: 'xoxo'}
10782
10874
  *
10783
- *
10784
10875
  * // set foo to 'yipee'
10785
10876
  * $location.search('foo', 'yipee');
10786
- * // => $location
10877
+ * // $location.search() => {foo: 'yipee', baz: 'xoxo'}
10787
10878
  * ```
10788
10879
  *
10789
10880
  * @param {string|Object.<string>|Object.<Array.<string>>} search New search params - string or
@@ -10853,6 +10944,13 @@ var locationPrototype = {
10853
10944
  *
10854
10945
  * Change hash fragment when called with parameter and return `$location`.
10855
10946
  *
10947
+ *
10948
+ * ```js
10949
+ * // given url http://example.com/some/path?foo=bar&baz=xoxo#hashValue
10950
+ * var hash = $location.hash();
10951
+ * // => "hashValue"
10952
+ * ```
10953
+ *
10856
10954
  * @param {(string|number)=} hash New hash fragment
10857
10955
  * @return {string} hash
10858
10956
  */
@@ -11174,11 +11272,19 @@ function $LocationProvider() {
11174
11272
  $rootScope.$evalAsync(function() {
11175
11273
  var oldUrl = $location.absUrl();
11176
11274
  var oldState = $location.$$state;
11275
+ var defaultPrevented;
11177
11276
 
11178
11277
  $location.$$parse(newUrl);
11179
11278
  $location.$$state = newState;
11180
- if ($rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
11181
- newState, oldState).defaultPrevented) {
11279
+
11280
+ defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
11281
+ newState, oldState).defaultPrevented;
11282
+
11283
+ // if the location was changed by a `$locationChangeStart` handler then stop
11284
+ // processing this location change
11285
+ if ($location.absUrl() !== newUrl) return;
11286
+
11287
+ if (defaultPrevented) {
11182
11288
  $location.$$parse(oldUrl);
11183
11289
  $location.$$state = oldState;
11184
11290
  setBrowserUrlWithFallback(oldUrl, false, oldState);
@@ -11202,13 +11308,20 @@ function $LocationProvider() {
11202
11308
  initializing = false;
11203
11309
 
11204
11310
  $rootScope.$evalAsync(function() {
11205
- if ($rootScope.$broadcast('$locationChangeStart', $location.absUrl(), oldUrl,
11206
- $location.$$state, oldState).defaultPrevented) {
11311
+ var newUrl = $location.absUrl();
11312
+ var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
11313
+ $location.$$state, oldState).defaultPrevented;
11314
+
11315
+ // if the location was changed by a `$locationChangeStart` handler then stop
11316
+ // processing this location change
11317
+ if ($location.absUrl() !== newUrl) return;
11318
+
11319
+ if (defaultPrevented) {
11207
11320
  $location.$$parse(oldUrl);
11208
11321
  $location.$$state = oldState;
11209
11322
  } else {
11210
11323
  if (urlOrStateChanged) {
11211
- setBrowserUrlWithFallback($location.absUrl(), currentReplace,
11324
+ setBrowserUrlWithFallback(newUrl, currentReplace,
11212
11325
  oldState === $location.$$state ? null : $location.$$state);
11213
11326
  }
11214
11327
  afterLocationChange(oldUrl, oldState);
@@ -11398,7 +11511,7 @@ var $parseMinErr = minErr('$parse');
11398
11511
  // Sandboxing Angular Expressions
11399
11512
  // ------------------------------
11400
11513
  // Angular expressions are generally considered safe because these expressions only have direct
11401
- // access to $scope and locals. However, one can obtain the ability to execute arbitrary JS code by
11514
+ // access to `$scope` and locals. However, one can obtain the ability to execute arbitrary JS code by
11402
11515
  // obtaining a reference to native JS functions such as the Function constructor.
11403
11516
  //
11404
11517
  // As an example, consider the following Angular expression:
@@ -11407,7 +11520,7 @@ var $parseMinErr = minErr('$parse');
11407
11520
  //
11408
11521
  // This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits
11409
11522
  // against the expression language, but not to prevent exploits that were enabled by exposing
11410
- // sensitive JavaScript or browser apis on Scope. Exposing such objects on a Scope is never a good
11523
+ // sensitive JavaScript or browser APIs on Scope. Exposing such objects on a Scope is never a good
11411
11524
  // practice and therefore we are not even trying to protect against interaction with an object
11412
11525
  // explicitly exposed in this way.
11413
11526
  //
@@ -11415,6 +11528,8 @@ var $parseMinErr = minErr('$parse');
11415
11528
  // window or some DOM object that has a reference to window is published onto a Scope.
11416
11529
  // Similarly we prevent invocations of function known to be dangerous, as well as assignments to
11417
11530
  // native objects.
11531
+ //
11532
+ // See https://docs.angularjs.org/guide/security
11418
11533
 
11419
11534
 
11420
11535
  function ensureSafeMemberName(name, fullExpression) {
@@ -11423,7 +11538,7 @@ function ensureSafeMemberName(name, fullExpression) {
11423
11538
  || name === "__proto__") {
11424
11539
  throw $parseMinErr('isecfld',
11425
11540
  'Attempting to access a disallowed field in Angular expressions! '
11426
- +'Expression: {0}', fullExpression);
11541
+ + 'Expression: {0}', fullExpression);
11427
11542
  }
11428
11543
  return name;
11429
11544
  }
@@ -11500,24 +11615,24 @@ var OPERATORS = extend(createMap(), {
11500
11615
  }
11501
11616
  return a;
11502
11617
  }
11503
- return isDefined(b)?b:undefined;},
11618
+ return isDefined(b) ? b : undefined;},
11504
11619
  '-':function(self, locals, a, b) {
11505
11620
  a=a(self, locals); b=b(self, locals);
11506
- return (isDefined(a)?a:0)-(isDefined(b)?b:0);
11621
+ return (isDefined(a) ? a : 0) - (isDefined(b) ? b : 0);
11507
11622
  },
11508
- '*':function(self, locals, a, b) {return a(self, locals)*b(self, locals);},
11509
- '/':function(self, locals, a, b) {return a(self, locals)/b(self, locals);},
11510
- '%':function(self, locals, a, b) {return a(self, locals)%b(self, locals);},
11511
- '===':function(self, locals, a, b) {return a(self, locals)===b(self, locals);},
11512
- '!==':function(self, locals, a, b) {return a(self, locals)!==b(self, locals);},
11513
- '==':function(self, locals, a, b) {return a(self, locals)==b(self, locals);},
11514
- '!=':function(self, locals, a, b) {return a(self, locals)!=b(self, locals);},
11515
- '<':function(self, locals, a, b) {return a(self, locals)<b(self, locals);},
11516
- '>':function(self, locals, a, b) {return a(self, locals)>b(self, locals);},
11517
- '<=':function(self, locals, a, b) {return a(self, locals)<=b(self, locals);},
11518
- '>=':function(self, locals, a, b) {return a(self, locals)>=b(self, locals);},
11519
- '&&':function(self, locals, a, b) {return a(self, locals)&&b(self, locals);},
11520
- '||':function(self, locals, a, b) {return a(self, locals)||b(self, locals);},
11623
+ '*':function(self, locals, a, b) {return a(self, locals) * b(self, locals);},
11624
+ '/':function(self, locals, a, b) {return a(self, locals) / b(self, locals);},
11625
+ '%':function(self, locals, a, b) {return a(self, locals) % b(self, locals);},
11626
+ '===':function(self, locals, a, b) {return a(self, locals) === b(self, locals);},
11627
+ '!==':function(self, locals, a, b) {return a(self, locals) !== b(self, locals);},
11628
+ '==':function(self, locals, a, b) {return a(self, locals) == b(self, locals);},
11629
+ '!=':function(self, locals, a, b) {return a(self, locals) != b(self, locals);},
11630
+ '<':function(self, locals, a, b) {return a(self, locals) < b(self, locals);},
11631
+ '>':function(self, locals, a, b) {return a(self, locals) > b(self, locals);},
11632
+ '<=':function(self, locals, a, b) {return a(self, locals) <= b(self, locals);},
11633
+ '>=':function(self, locals, a, b) {return a(self, locals) >= b(self, locals);},
11634
+ '&&':function(self, locals, a, b) {return a(self, locals) && b(self, locals);},
11635
+ '||':function(self, locals, a, b) {return a(self, locals) || b(self, locals);},
11521
11636
  '!':function(self, locals, a) {return !a(self, locals);},
11522
11637
 
11523
11638
  //Tokenized as operators but parsed as assignment/filters
@@ -11543,44 +11658,31 @@ Lexer.prototype = {
11543
11658
  lex: function(text) {
11544
11659
  this.text = text;
11545
11660
  this.index = 0;
11546
- this.ch = undefined;
11547
11661
  this.tokens = [];
11548
11662
 
11549
11663
  while (this.index < this.text.length) {
11550
- this.ch = this.text.charAt(this.index);
11551
- if (this.is('"\'')) {
11552
- this.readString(this.ch);
11553
- } else if (this.isNumber(this.ch) || this.is('.') && this.isNumber(this.peek())) {
11664
+ var ch = this.text.charAt(this.index);
11665
+ if (ch === '"' || ch === "'") {
11666
+ this.readString(ch);
11667
+ } else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) {
11554
11668
  this.readNumber();
11555
- } else if (this.isIdent(this.ch)) {
11669
+ } else if (this.isIdent(ch)) {
11556
11670
  this.readIdent();
11557
- } else if (this.is('(){}[].,;:?')) {
11558
- this.tokens.push({
11559
- index: this.index,
11560
- text: this.ch
11561
- });
11671
+ } else if (this.is(ch, '(){}[].,;:?')) {
11672
+ this.tokens.push({index: this.index, text: ch});
11562
11673
  this.index++;
11563
- } else if (this.isWhitespace(this.ch)) {
11674
+ } else if (this.isWhitespace(ch)) {
11564
11675
  this.index++;
11565
11676
  } else {
11566
- var ch2 = this.ch + this.peek();
11677
+ var ch2 = ch + this.peek();
11567
11678
  var ch3 = ch2 + this.peek(2);
11568
- var fn = OPERATORS[this.ch];
11569
- var fn2 = OPERATORS[ch2];
11570
- var fn3 = OPERATORS[ch3];
11571
- if (fn3) {
11572
- this.tokens.push({index: this.index, text: ch3, fn: fn3});
11573
- this.index += 3;
11574
- } else if (fn2) {
11575
- this.tokens.push({index: this.index, text: ch2, fn: fn2});
11576
- this.index += 2;
11577
- } else if (fn) {
11578
- this.tokens.push({
11579
- index: this.index,
11580
- text: this.ch,
11581
- fn: fn
11582
- });
11583
- this.index += 1;
11679
+ var op1 = OPERATORS[ch];
11680
+ var op2 = OPERATORS[ch2];
11681
+ var op3 = OPERATORS[ch3];
11682
+ if (op1 || op2 || op3) {
11683
+ var token = op3 ? ch3 : (op2 ? ch2 : ch);
11684
+ this.tokens.push({index: this.index, text: token, operator: true});
11685
+ this.index += token.length;
11584
11686
  } else {
11585
11687
  this.throwError('Unexpected next character ', this.index, this.index + 1);
11586
11688
  }
@@ -11589,8 +11691,8 @@ Lexer.prototype = {
11589
11691
  return this.tokens;
11590
11692
  },
11591
11693
 
11592
- is: function(chars) {
11593
- return chars.indexOf(this.ch) !== -1;
11694
+ is: function(ch, chars) {
11695
+ return chars.indexOf(ch) !== -1;
11594
11696
  },
11595
11697
 
11596
11698
  peek: function(i) {
@@ -11599,7 +11701,7 @@ Lexer.prototype = {
11599
11701
  },
11600
11702
 
11601
11703
  isNumber: function(ch) {
11602
- return ('0' <= ch && ch <= '9');
11704
+ return ('0' <= ch && ch <= '9') && typeof ch === "string";
11603
11705
  },
11604
11706
 
11605
11707
  isWhitespace: function(ch) {
@@ -11652,79 +11754,28 @@ Lexer.prototype = {
11652
11754
  }
11653
11755
  this.index++;
11654
11756
  }
11655
- number = 1 * number;
11656
11757
  this.tokens.push({
11657
11758
  index: start,
11658
11759
  text: number,
11659
11760
  constant: true,
11660
- fn: function() { return number; }
11761
+ value: Number(number)
11661
11762
  });
11662
11763
  },
11663
11764
 
11664
11765
  readIdent: function() {
11665
- var expression = this.text;
11666
-
11667
- var ident = '';
11668
11766
  var start = this.index;
11669
-
11670
- var lastDot, peekIndex, methodName, ch;
11671
-
11672
11767
  while (this.index < this.text.length) {
11673
- ch = this.text.charAt(this.index);
11674
- if (ch === '.' || this.isIdent(ch) || this.isNumber(ch)) {
11675
- if (ch === '.') lastDot = this.index;
11676
- ident += ch;
11677
- } else {
11768
+ var ch = this.text.charAt(this.index);
11769
+ if (!(this.isIdent(ch) || this.isNumber(ch))) {
11678
11770
  break;
11679
11771
  }
11680
11772
  this.index++;
11681
11773
  }
11682
-
11683
- //check if the identifier ends with . and if so move back one char
11684
- if (lastDot && ident[ident.length - 1] === '.') {
11685
- this.index--;
11686
- ident = ident.slice(0, -1);
11687
- lastDot = ident.lastIndexOf('.');
11688
- if (lastDot === -1) {
11689
- lastDot = undefined;
11690
- }
11691
- }
11692
-
11693
- //check if this is not a method invocation and if it is back out to last dot
11694
- if (lastDot) {
11695
- peekIndex = this.index;
11696
- while (peekIndex < this.text.length) {
11697
- ch = this.text.charAt(peekIndex);
11698
- if (ch === '(') {
11699
- methodName = ident.substr(lastDot - start + 1);
11700
- ident = ident.substr(0, lastDot - start);
11701
- this.index = peekIndex;
11702
- break;
11703
- }
11704
- if (this.isWhitespace(ch)) {
11705
- peekIndex++;
11706
- } else {
11707
- break;
11708
- }
11709
- }
11710
- }
11711
-
11712
11774
  this.tokens.push({
11713
11775
  index: start,
11714
- text: ident,
11715
- fn: CONSTANTS[ident] || getterFn(ident, this.options, expression)
11776
+ text: this.text.slice(start, this.index),
11777
+ identifier: true
11716
11778
  });
11717
-
11718
- if (methodName) {
11719
- this.tokens.push({
11720
- index: lastDot,
11721
- text: '.'
11722
- });
11723
- this.tokens.push({
11724
- index: lastDot + 1,
11725
- text: methodName
11726
- });
11727
- }
11728
11779
  },
11729
11780
 
11730
11781
  readString: function(quote) {
@@ -11755,9 +11806,8 @@ Lexer.prototype = {
11755
11806
  this.tokens.push({
11756
11807
  index: start,
11757
11808
  text: rawString,
11758
- string: string,
11759
11809
  constant: true,
11760
- fn: function() { return string; }
11810
+ value: string
11761
11811
  });
11762
11812
  return;
11763
11813
  } else {
@@ -11818,16 +11868,12 @@ Parser.prototype = {
11818
11868
  primary = this.arrayDeclaration();
11819
11869
  } else if (this.expect('{')) {
11820
11870
  primary = this.object();
11871
+ } else if (this.peek().identifier) {
11872
+ primary = this.identifier();
11873
+ } else if (this.peek().constant) {
11874
+ primary = this.constant();
11821
11875
  } else {
11822
- var token = this.expect();
11823
- primary = token.fn;
11824
- if (!primary) {
11825
- this.throwError('not a primary expression', token);
11826
- }
11827
- if (token.constant) {
11828
- primary.constant = true;
11829
- primary.literal = true;
11830
- }
11876
+ this.throwError('not a primary expression', this.peek());
11831
11877
  }
11832
11878
 
11833
11879
  var next, context;
@@ -11861,8 +11907,11 @@ Parser.prototype = {
11861
11907
  },
11862
11908
 
11863
11909
  peek: function(e1, e2, e3, e4) {
11864
- if (this.tokens.length > 0) {
11865
- var token = this.tokens[0];
11910
+ return this.peekAhead(0, e1, e2, e3, e4);
11911
+ },
11912
+ peekAhead: function(i, e1, e2, e3, e4) {
11913
+ if (this.tokens.length > i) {
11914
+ var token = this.tokens[i];
11866
11915
  var t = token.text;
11867
11916
  if (t === e1 || t === e2 || t === e3 || t === e4 ||
11868
11917
  (!e1 && !e2 && !e3 && !e4)) {
@@ -11882,12 +11931,19 @@ Parser.prototype = {
11882
11931
  },
11883
11932
 
11884
11933
  consume: function(e1) {
11885
- if (!this.expect(e1)) {
11934
+ if (this.tokens.length === 0) {
11935
+ throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
11936
+ }
11937
+
11938
+ var token = this.expect(e1);
11939
+ if (!token) {
11886
11940
  this.throwError('is unexpected, expecting [' + e1 + ']', this.peek());
11887
11941
  }
11942
+ return token;
11888
11943
  },
11889
11944
 
11890
- unaryFn: function(fn, right) {
11945
+ unaryFn: function(op, right) {
11946
+ var fn = OPERATORS[op];
11891
11947
  return extend(function $parseUnaryFn(self, locals) {
11892
11948
  return fn(self, locals, right);
11893
11949
  }, {
@@ -11896,7 +11952,8 @@ Parser.prototype = {
11896
11952
  });
11897
11953
  },
11898
11954
 
11899
- binaryFn: function(left, fn, right, isBranching) {
11955
+ binaryFn: function(left, op, right, isBranching) {
11956
+ var fn = OPERATORS[op];
11900
11957
  return extend(function $parseBinaryFn(self, locals) {
11901
11958
  return fn(self, locals, left, right);
11902
11959
  }, {
@@ -11905,6 +11962,28 @@ Parser.prototype = {
11905
11962
  });
11906
11963
  },
11907
11964
 
11965
+ identifier: function() {
11966
+ var id = this.consume().text;
11967
+
11968
+ //Continue reading each `.identifier` unless it is a method invocation
11969
+ while (this.peek('.') && this.peekAhead(1).identifier && !this.peekAhead(2, '(')) {
11970
+ id += this.consume().text + this.consume().text;
11971
+ }
11972
+
11973
+ return CONSTANTS[id] || getterFn(id, this.options, this.text);
11974
+ },
11975
+
11976
+ constant: function() {
11977
+ var value = this.consume().value;
11978
+
11979
+ return extend(function $parseConstant() {
11980
+ return value;
11981
+ }, {
11982
+ constant: true,
11983
+ literal: true
11984
+ });
11985
+ },
11986
+
11908
11987
  statements: function() {
11909
11988
  var statements = [];
11910
11989
  while (true) {
@@ -11936,8 +12015,7 @@ Parser.prototype = {
11936
12015
  },
11937
12016
 
11938
12017
  filter: function(inputFn) {
11939
- var token = this.expect();
11940
- var fn = this.$filter(token.text);
12018
+ var fn = this.$filter(this.consume().text);
11941
12019
  var argsFn;
11942
12020
  var args;
11943
12021
 
@@ -12000,7 +12078,7 @@ Parser.prototype = {
12000
12078
  var token;
12001
12079
  if ((token = this.expect('?'))) {
12002
12080
  middle = this.assignment();
12003
- if ((token = this.expect(':'))) {
12081
+ if (this.consume(':')) {
12004
12082
  var right = this.assignment();
12005
12083
 
12006
12084
  return extend(function $parseTernary(self, locals) {
@@ -12008,9 +12086,6 @@ Parser.prototype = {
12008
12086
  }, {
12009
12087
  constant: left.constant && middle.constant && right.constant
12010
12088
  });
12011
-
12012
- } else {
12013
- this.throwError('expected :', token);
12014
12089
  }
12015
12090
  }
12016
12091
 
@@ -12021,7 +12096,7 @@ Parser.prototype = {
12021
12096
  var left = this.logicalAND();
12022
12097
  var token;
12023
12098
  while ((token = this.expect('||'))) {
12024
- left = this.binaryFn(left, token.fn, this.logicalAND(), true);
12099
+ left = this.binaryFn(left, token.text, this.logicalAND(), true);
12025
12100
  }
12026
12101
  return left;
12027
12102
  },
@@ -12030,7 +12105,7 @@ Parser.prototype = {
12030
12105
  var left = this.equality();
12031
12106
  var token;
12032
12107
  if ((token = this.expect('&&'))) {
12033
- left = this.binaryFn(left, token.fn, this.logicalAND(), true);
12108
+ left = this.binaryFn(left, token.text, this.logicalAND(), true);
12034
12109
  }
12035
12110
  return left;
12036
12111
  },
@@ -12039,7 +12114,7 @@ Parser.prototype = {
12039
12114
  var left = this.relational();
12040
12115
  var token;
12041
12116
  if ((token = this.expect('==','!=','===','!=='))) {
12042
- left = this.binaryFn(left, token.fn, this.equality());
12117
+ left = this.binaryFn(left, token.text, this.equality());
12043
12118
  }
12044
12119
  return left;
12045
12120
  },
@@ -12048,7 +12123,7 @@ Parser.prototype = {
12048
12123
  var left = this.additive();
12049
12124
  var token;
12050
12125
  if ((token = this.expect('<', '>', '<=', '>='))) {
12051
- left = this.binaryFn(left, token.fn, this.relational());
12126
+ left = this.binaryFn(left, token.text, this.relational());
12052
12127
  }
12053
12128
  return left;
12054
12129
  },
@@ -12057,7 +12132,7 @@ Parser.prototype = {
12057
12132
  var left = this.multiplicative();
12058
12133
  var token;
12059
12134
  while ((token = this.expect('+','-'))) {
12060
- left = this.binaryFn(left, token.fn, this.multiplicative());
12135
+ left = this.binaryFn(left, token.text, this.multiplicative());
12061
12136
  }
12062
12137
  return left;
12063
12138
  },
@@ -12066,7 +12141,7 @@ Parser.prototype = {
12066
12141
  var left = this.unary();
12067
12142
  var token;
12068
12143
  while ((token = this.expect('*','/','%'))) {
12069
- left = this.binaryFn(left, token.fn, this.unary());
12144
+ left = this.binaryFn(left, token.text, this.unary());
12070
12145
  }
12071
12146
  return left;
12072
12147
  },
@@ -12076,9 +12151,9 @@ Parser.prototype = {
12076
12151
  if (this.expect('+')) {
12077
12152
  return this.primary();
12078
12153
  } else if ((token = this.expect('-'))) {
12079
- return this.binaryFn(Parser.ZERO, token.fn, this.unary());
12154
+ return this.binaryFn(Parser.ZERO, token.text, this.unary());
12080
12155
  } else if ((token = this.expect('!'))) {
12081
- return this.unaryFn(token.fn, this.unary());
12156
+ return this.unaryFn(token.text, this.unary());
12082
12157
  } else {
12083
12158
  return this.primary();
12084
12159
  }
@@ -12086,7 +12161,7 @@ Parser.prototype = {
12086
12161
 
12087
12162
  fieldAccess: function(object) {
12088
12163
  var expression = this.text;
12089
- var field = this.expect().text;
12164
+ var field = this.consume().text;
12090
12165
  var getter = getterFn(field, this.options, expression);
12091
12166
 
12092
12167
  return extend(function $parseFieldAccess(scope, locals, self) {
@@ -12171,8 +12246,7 @@ Parser.prototype = {
12171
12246
  // Support trailing commas per ES5.1.
12172
12247
  break;
12173
12248
  }
12174
- var elementFn = this.expression();
12175
- elementFns.push(elementFn);
12249
+ elementFns.push(this.expression());
12176
12250
  } while (this.expect(','));
12177
12251
  }
12178
12252
  this.consume(']');
@@ -12198,11 +12272,16 @@ Parser.prototype = {
12198
12272
  // Support trailing commas per ES5.1.
12199
12273
  break;
12200
12274
  }
12201
- var token = this.expect();
12202
- keys.push(token.string || token.text);
12275
+ var token = this.consume();
12276
+ if (token.constant) {
12277
+ keys.push(token.value);
12278
+ } else if (token.identifier) {
12279
+ keys.push(token.text);
12280
+ } else {
12281
+ this.throwError("invalid key", token);
12282
+ }
12203
12283
  this.consume(':');
12204
- var value = this.expression();
12205
- valueFns.push(value);
12284
+ valueFns.push(this.expression());
12206
12285
  } while (this.expect(','));
12207
12286
  }
12208
12287
  this.consume('}');
@@ -12644,13 +12723,21 @@ function $ParseProvider() {
12644
12723
 
12645
12724
  function addInterceptor(parsedExpression, interceptorFn) {
12646
12725
  if (!interceptorFn) return parsedExpression;
12726
+ var watchDelegate = parsedExpression.$$watchDelegate;
12647
12727
 
12648
- var fn = function interceptedExpression(scope, locals) {
12728
+ var regularWatch =
12729
+ watchDelegate !== oneTimeLiteralWatchDelegate &&
12730
+ watchDelegate !== oneTimeWatchDelegate;
12731
+
12732
+ var fn = regularWatch ? function regularInterceptedExpression(scope, locals) {
12733
+ var value = parsedExpression(scope, locals);
12734
+ return interceptorFn(value, scope, locals);
12735
+ } : function oneTimeInterceptedExpression(scope, locals) {
12649
12736
  var value = parsedExpression(scope, locals);
12650
12737
  var result = interceptorFn(value, scope, locals);
12651
12738
  // we only return the interceptor's result if the
12652
12739
  // initial value is defined (for bind-once)
12653
- return isDefined(value) || interceptorFn.$stateful ? result : value;
12740
+ return isDefined(value) ? result : value;
12654
12741
  };
12655
12742
 
12656
12743
  // Propagate $$watchDelegates other then inputsWatchDelegate
@@ -12675,7 +12762,11 @@ function $ParseProvider() {
12675
12762
  * @requires $rootScope
12676
12763
  *
12677
12764
  * @description
12678
- * A promise/deferred implementation inspired by [Kris Kowal's Q](https://github.com/kriskowal/q).
12765
+ * A service that helps you run functions asynchronously, and use their return values (or exceptions)
12766
+ * when they are done processing.
12767
+ *
12768
+ * This is an implementation of promises/deferred objects inspired by
12769
+ * [Kris Kowal's Q](https://github.com/kriskowal/q).
12679
12770
  *
12680
12771
  * $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred
12681
12772
  * implementations, and the other which resembles ES6 promises to some degree.
@@ -12811,16 +12902,12 @@ function $ParseProvider() {
12811
12902
  *
12812
12903
  * - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)`
12813
12904
  *
12814
- * - `finally(callback)` – allows you to observe either the fulfillment or rejection of a promise,
12905
+ * - `finally(callback, notifyCallback)` – allows you to observe either the fulfillment or rejection of a promise,
12815
12906
  * but to do so without modifying the final value. This is useful to release resources or do some
12816
12907
  * clean-up that needs to be done whether the promise was rejected or resolved. See the [full
12817
12908
  * specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for
12818
12909
  * more information.
12819
12910
  *
12820
- * Because `finally` is a reserved word in JavaScript and reserved keywords are not supported as
12821
- * property names by ES3, you'll need to invoke the method like `promise['finally'](callback)` to
12822
- * make your code IE8 and Android 2.x compatible.
12823
- *
12824
12911
  * # Chaining promises
12825
12912
  *
12826
12913
  * Because calling the `then` method of a promise returns a new derived promise, it is easily
@@ -14043,11 +14130,11 @@ function $RootScopeProvider() {
14043
14130
  if (ttl < 5) {
14044
14131
  logIdx = 4 - ttl;
14045
14132
  if (!watchLog[logIdx]) watchLog[logIdx] = [];
14046
- logMsg = (isFunction(watch.exp))
14047
- ? 'fn: ' + (watch.exp.name || watch.exp.toString())
14048
- : watch.exp;
14049
- logMsg += '; newVal: ' + toJson(value) + '; oldVal: ' + toJson(last);
14050
- watchLog[logIdx].push(logMsg);
14133
+ watchLog[logIdx].push({
14134
+ msg: isFunction(watch.exp) ? 'fn: ' + (watch.exp.name || watch.exp.toString()) : watch.exp,
14135
+ newVal: value,
14136
+ oldVal: last
14137
+ });
14051
14138
  }
14052
14139
  } else if (watch === lastDirtyWatch) {
14053
14140
  // If the most recently dirty watcher is now clean, short circuit since the remaining watchers
@@ -14080,7 +14167,7 @@ function $RootScopeProvider() {
14080
14167
  throw $rootScopeMinErr('infdig',
14081
14168
  '{0} $digest() iterations reached. Aborting!\n' +
14082
14169
  'Watchers fired in the last 5 iterations: {1}',
14083
- TTL, toJson(watchLog));
14170
+ TTL, watchLog);
14084
14171
  }
14085
14172
 
14086
14173
  } while (dirty || asyncQueue.length);
@@ -14431,7 +14518,7 @@ function $RootScopeProvider() {
14431
14518
  do {
14432
14519
  namedListeners = scope.$$listeners[name] || empty;
14433
14520
  event.currentScope = scope;
14434
- for (i=0, length=namedListeners.length; i<length; i++) {
14521
+ for (i = 0, length = namedListeners.length; i < length; i++) {
14435
14522
 
14436
14523
  // if listeners were deregistered, defragment the array
14437
14524
  if (!namedListeners[i]) {
@@ -14505,7 +14592,7 @@ function $RootScopeProvider() {
14505
14592
  while ((current = next)) {
14506
14593
  event.currentScope = current;
14507
14594
  listeners = current.$$listeners[name] || [];
14508
- for (i=0, length = listeners.length; i<length; i++) {
14595
+ for (i = 0, length = listeners.length; i < length; i++) {
14509
14596
  // if listeners were deregistered, defragment the array
14510
14597
  if (!listeners[i]) {
14511
14598
  listeners.splice(i, 1);
@@ -14661,7 +14748,7 @@ function $$SanitizeUriProvider() {
14661
14748
  var normalizedVal;
14662
14749
  normalizedVal = urlResolve(uri).href;
14663
14750
  if (normalizedVal !== '' && !normalizedVal.match(regex)) {
14664
- return 'unsafe:'+normalizedVal;
14751
+ return 'unsafe:' + normalizedVal;
14665
14752
  }
14666
14753
  return uri;
14667
14754
  };
@@ -15760,7 +15847,7 @@ function $SnifferProvider() {
15760
15847
  transitions = !!(('transition' in bodyStyle) || (vendorPrefix + 'Transition' in bodyStyle));
15761
15848
  animations = !!(('animation' in bodyStyle) || (vendorPrefix + 'Animation' in bodyStyle));
15762
15849
 
15763
- if (android && (!transitions||!animations)) {
15850
+ if (android && (!transitions || !animations)) {
15764
15851
  transitions = isString(document.body.style.webkitTransition);
15765
15852
  animations = isString(document.body.style.webkitAnimation);
15766
15853
  }
@@ -15831,7 +15918,7 @@ function $TemplateRequestProvider() {
15831
15918
  if (isArray(transformResponse)) {
15832
15919
  var original = transformResponse;
15833
15920
  transformResponse = [];
15834
- for (var i=0; i<original.length; ++i) {
15921
+ for (var i = 0; i < original.length; ++i) {
15835
15922
  var transformer = original[i];
15836
15923
  if (transformer !== defaultHttpResponseTransform) {
15837
15924
  transformResponse.push(transformer);
@@ -16075,7 +16162,7 @@ function $TimeoutProvider() {
16075
16162
  // exactly the behavior needed here. There is little value is mocking these out for this
16076
16163
  // service.
16077
16164
  var urlParsingNode = document.createElement("a");
16078
- var originUrl = urlResolve(window.location.href, true);
16165
+ var originUrl = urlResolve(window.location.href);
16079
16166
 
16080
16167
 
16081
16168
  /**
@@ -16130,7 +16217,7 @@ var originUrl = urlResolve(window.location.href, true);
16130
16217
  * | pathname | The pathname, beginning with "/"
16131
16218
  *
16132
16219
  */
16133
- function urlResolve(url, base) {
16220
+ function urlResolve(url) {
16134
16221
  var href = url;
16135
16222
 
16136
16223
  if (msie) {
@@ -16510,8 +16597,8 @@ function filterFilter() {
16510
16597
  }
16511
16598
  return false;
16512
16599
  }
16513
- text = (''+text).toLowerCase();
16514
- return (''+obj).toLowerCase().indexOf(text) > -1;
16600
+ text = ('' + text).toLowerCase();
16601
+ return ('' + obj).toLowerCase().indexOf(text) > -1;
16515
16602
  };
16516
16603
  }
16517
16604
  }
@@ -16595,7 +16682,7 @@ function filterFilter() {
16595
16682
  *
16596
16683
  * @param {number} amount Input to filter.
16597
16684
  * @param {string=} symbol Currency symbol or identifier to be displayed.
16598
- * @param {number=} fractionSize Number of decimal places to round the amount to.
16685
+ * @param {number=} fractionSize Number of decimal places to round the amount to, defaults to default max fraction size for current locale
16599
16686
  * @returns {string} Formatted number.
16600
16687
  *
16601
16688
  *
@@ -16645,8 +16732,7 @@ function currencyFilter($locale) {
16645
16732
  }
16646
16733
 
16647
16734
  if (isUndefined(fractionSize)) {
16648
- // TODO: read the default value from the locale file
16649
- fractionSize = 2;
16735
+ fractionSize = formats.PATTERNS[1].maxFrac;
16650
16736
  }
16651
16737
 
16652
16738
  // if null or undefined pass it through
@@ -16771,7 +16857,7 @@ function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
16771
16857
  if (whole.length >= (lgroup + group)) {
16772
16858
  pos = whole.length - lgroup;
16773
16859
  for (i = 0; i < pos; i++) {
16774
- if ((pos - i)%group === 0 && i !== 0) {
16860
+ if ((pos - i) % group === 0 && i !== 0) {
16775
16861
  formatedText += groupSep;
16776
16862
  }
16777
16863
  formatedText += whole.charAt(i);
@@ -16779,7 +16865,7 @@ function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
16779
16865
  }
16780
16866
 
16781
16867
  for (i = pos; i < whole.length; i++) {
16782
- if ((whole.length - i)%lgroup === 0 && i !== 0) {
16868
+ if ((whole.length - i) % lgroup === 0 && i !== 0) {
16783
16869
  formatedText += groupSep;
16784
16870
  }
16785
16871
  formatedText += whole.charAt(i);
@@ -16798,9 +16884,9 @@ function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
16798
16884
  }
16799
16885
  }
16800
16886
 
16801
- parts.push(isNegative ? pattern.negPre : pattern.posPre);
16802
- parts.push(formatedText);
16803
- parts.push(isNegative ? pattern.negSuf : pattern.posSuf);
16887
+ parts.push(isNegative ? pattern.negPre : pattern.posPre,
16888
+ formatedText,
16889
+ isNegative ? pattern.negSuf : pattern.posSuf);
16804
16890
  return parts.join('');
16805
16891
  }
16806
16892
 
@@ -17019,10 +17105,10 @@ function dateFilter($locale) {
17019
17105
  tzMin = int(match[9] + match[11]);
17020
17106
  }
17021
17107
  dateSetter.call(date, int(match[1]), int(match[2]) - 1, int(match[3]));
17022
- var h = int(match[4]||0) - tzHour;
17023
- var m = int(match[5]||0) - tzMin;
17024
- var s = int(match[6]||0);
17025
- var ms = Math.round(parseFloat('0.' + (match[7]||0)) * 1000);
17108
+ var h = int(match[4] || 0) - tzHour;
17109
+ var m = int(match[5] || 0) - tzMin;
17110
+ var s = int(match[6] || 0);
17111
+ var ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000);
17026
17112
  timeSetter.call(date, h, m, s, ms);
17027
17113
  return date;
17028
17114
  }
@@ -17254,7 +17340,7 @@ function limitToFilter() {
17254
17340
  n = input.length;
17255
17341
  }
17256
17342
 
17257
- for (; i<n; i++) {
17343
+ for (; i < n; i++) {
17258
17344
  out.push(input[i]);
17259
17345
  }
17260
17346
 
@@ -17381,7 +17467,7 @@ orderByFilter.$inject = ['$parse'];
17381
17467
  function orderByFilter($parse) {
17382
17468
  return function(array, sortPredicate, reverseOrder) {
17383
17469
  if (!(isArrayLike(array))) return array;
17384
- sortPredicate = isArray(sortPredicate) ? sortPredicate: [sortPredicate];
17470
+ sortPredicate = isArray(sortPredicate) ? sortPredicate : [sortPredicate];
17385
17471
  if (sortPredicate.length === 0) { sortPredicate = ['+']; }
17386
17472
  sortPredicate = sortPredicate.map(function(predicate) {
17387
17473
  var descending = false, get = predicate || identity;
@@ -17408,9 +17494,7 @@ function orderByFilter($parse) {
17408
17494
  return compare(get(a),get(b));
17409
17495
  }, descending);
17410
17496
  });
17411
- var arrayCopy = [];
17412
- for (var i = 0; i < array.length; i++) { arrayCopy.push(array[i]); }
17413
- return arrayCopy.sort(reverseComparator(comparator, reverseOrder));
17497
+ return slice.call(array).sort(reverseComparator(comparator, reverseOrder));
17414
17498
 
17415
17499
  function comparator(o1, o2) {
17416
17500
  for (var i = 0; i < sortPredicate.length; i++) {
@@ -18382,9 +18466,7 @@ var formDirectiveFactory = function(isNgForm) {
18382
18466
  controller.$setSubmitted();
18383
18467
  });
18384
18468
 
18385
- event.preventDefault
18386
- ? event.preventDefault()
18387
- : event.returnValue = false; // IE
18469
+ event.preventDefault();
18388
18470
  };
18389
18471
 
18390
18472
  addEventListenerFn(formElement[0], 'submit', handleFormSubmission);
@@ -18471,10 +18553,16 @@ var inputType = {
18471
18553
  * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
18472
18554
  * minlength.
18473
18555
  * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
18474
- * maxlength.
18475
- * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
18476
- * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
18477
- * patterns defined as scope expressions.
18556
+ * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
18557
+ * any length.
18558
+ * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
18559
+ * that contains the regular expression body that will be converted to a regular expression
18560
+ * as in the ngPattern directive.
18561
+ * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
18562
+ * a RegExp found by evaluating the Angular expression given in the attribute value.
18563
+ * If the expression evaluates to a RegExp object then this is used directly.
18564
+ * If the expression is a string then it will be converted to a RegExp after wrapping it in `^` and `$`
18565
+ * characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`.
18478
18566
  * @param {string=} ngChange Angular expression to be executed when input changes due to user
18479
18567
  * interaction with the input element.
18480
18568
  * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
@@ -19014,10 +19102,16 @@ var inputType = {
19014
19102
  * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
19015
19103
  * minlength.
19016
19104
  * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
19017
- * maxlength.
19018
- * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
19019
- * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
19020
- * patterns defined as scope expressions.
19105
+ * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
19106
+ * any length.
19107
+ * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
19108
+ * that contains the regular expression body that will be converted to a regular expression
19109
+ * as in the ngPattern directive.
19110
+ * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
19111
+ * a RegExp found by evaluating the Angular expression given in the attribute value.
19112
+ * If the expression evaluates to a RegExp object then this is used directly.
19113
+ * If the expression is a string then it will be converted to a RegExp after wrapping it in `^` and `$`
19114
+ * characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`.
19021
19115
  * @param {string=} ngChange Angular expression to be executed when input changes due to user
19022
19116
  * interaction with the input element.
19023
19117
  *
@@ -19096,10 +19190,16 @@ var inputType = {
19096
19190
  * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
19097
19191
  * minlength.
19098
19192
  * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
19099
- * maxlength.
19100
- * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
19101
- * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
19102
- * patterns defined as scope expressions.
19193
+ * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
19194
+ * any length.
19195
+ * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
19196
+ * that contains the regular expression body that will be converted to a regular expression
19197
+ * as in the ngPattern directive.
19198
+ * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
19199
+ * a RegExp found by evaluating the Angular expression given in the attribute value.
19200
+ * If the expression evaluates to a RegExp object then this is used directly.
19201
+ * If the expression is a string then it will be converted to a RegExp after wrapping it in `^` and `$`
19202
+ * characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`.
19103
19203
  * @param {string=} ngChange Angular expression to be executed when input changes due to user
19104
19204
  * interaction with the input element.
19105
19205
  *
@@ -19179,10 +19279,16 @@ var inputType = {
19179
19279
  * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
19180
19280
  * minlength.
19181
19281
  * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
19182
- * maxlength.
19183
- * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
19184
- * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
19185
- * patterns defined as scope expressions.
19282
+ * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
19283
+ * any length.
19284
+ * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
19285
+ * that contains the regular expression body that will be converted to a regular expression
19286
+ * as in the ngPattern directive.
19287
+ * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
19288
+ * a RegExp found by evaluating the Angular expression given in the attribute value.
19289
+ * If the expression evaluates to a RegExp object then this is used directly.
19290
+ * If the expression is a string then it will be converted to a RegExp after wrapping it in `^` and `$`
19291
+ * characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`.
19186
19292
  * @param {string=} ngChange Angular expression to be executed when input changes due to user
19187
19293
  * interaction with the input element.
19188
19294
  *
@@ -19445,7 +19551,7 @@ function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
19445
19551
  element.on('change', listener);
19446
19552
 
19447
19553
  ctrl.$render = function() {
19448
- element.val(ctrl.$isEmpty(ctrl.$modelValue) ? '' : ctrl.$viewValue);
19554
+ element.val(ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue);
19449
19555
  };
19450
19556
  }
19451
19557
 
@@ -19493,8 +19599,8 @@ function createDateParser(regexp, mapping) {
19493
19599
  // When a date is JSON'ified to wraps itself inside of an extra
19494
19600
  // set of double quotes. This makes the date parsing code unable
19495
19601
  // to match the date string and parse it as a date.
19496
- if (iso.charAt(0) == '"' && iso.charAt(iso.length-1) == '"') {
19497
- iso = iso.substring(1, iso.length-1);
19602
+ if (iso.charAt(0) == '"' && iso.charAt(iso.length - 1) == '"') {
19603
+ iso = iso.substring(1, iso.length - 1);
19498
19604
  }
19499
19605
  if (ISO_DATE_REGEXP.test(iso)) {
19500
19606
  return new Date(iso);
@@ -19555,10 +19661,10 @@ function createDateInputType(type, regexp, parseDate, format) {
19555
19661
  });
19556
19662
 
19557
19663
  ctrl.$formatters.push(function(value) {
19558
- if (!ctrl.$isEmpty(value)) {
19559
- if (!isDate(value)) {
19560
- throw $ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value);
19561
- }
19664
+ if (value && !isDate(value)) {
19665
+ throw $ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value);
19666
+ }
19667
+ if (isValidDate(value)) {
19562
19668
  previousDate = value;
19563
19669
  if (previousDate && timezone === 'UTC') {
19564
19670
  var timezoneOffset = 60000 * previousDate.getTimezoneOffset();
@@ -19567,14 +19673,14 @@ function createDateInputType(type, regexp, parseDate, format) {
19567
19673
  return $filter('date')(value, format, timezone);
19568
19674
  } else {
19569
19675
  previousDate = null;
19676
+ return '';
19570
19677
  }
19571
- return '';
19572
19678
  });
19573
19679
 
19574
19680
  if (isDefined(attr.min) || attr.ngMin) {
19575
19681
  var minVal;
19576
19682
  ctrl.$validators.min = function(value) {
19577
- return ctrl.$isEmpty(value) || isUndefined(minVal) || parseDate(value) >= minVal;
19683
+ return !isValidDate(value) || isUndefined(minVal) || parseDate(value) >= minVal;
19578
19684
  };
19579
19685
  attr.$observe('min', function(val) {
19580
19686
  minVal = parseObservedDateValue(val);
@@ -19585,18 +19691,18 @@ function createDateInputType(type, regexp, parseDate, format) {
19585
19691
  if (isDefined(attr.max) || attr.ngMax) {
19586
19692
  var maxVal;
19587
19693
  ctrl.$validators.max = function(value) {
19588
- return ctrl.$isEmpty(value) || isUndefined(maxVal) || parseDate(value) <= maxVal;
19694
+ return !isValidDate(value) || isUndefined(maxVal) || parseDate(value) <= maxVal;
19589
19695
  };
19590
19696
  attr.$observe('max', function(val) {
19591
19697
  maxVal = parseObservedDateValue(val);
19592
19698
  ctrl.$validate();
19593
19699
  });
19594
19700
  }
19595
- // Override the standard $isEmpty to detect invalid dates as well
19596
- ctrl.$isEmpty = function(value) {
19701
+
19702
+ function isValidDate(value) {
19597
19703
  // Invalid Date: getTime() returns NaN
19598
- return !value || (value.getTime && value.getTime() !== value.getTime());
19599
- };
19704
+ return value && !(value.getTime && value.getTime() !== value.getTime());
19705
+ }
19600
19706
 
19601
19707
  function parseObservedDateValue(val) {
19602
19708
  return isDefined(val) ? (isDate(val) ? val : parseDate(val)) : undefined;
@@ -19680,7 +19786,8 @@ function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
19680
19786
  stringBasedInputType(ctrl);
19681
19787
 
19682
19788
  ctrl.$$parserName = 'url';
19683
- ctrl.$validators.url = function(value) {
19789
+ ctrl.$validators.url = function(modelValue, viewValue) {
19790
+ var value = modelValue || viewValue;
19684
19791
  return ctrl.$isEmpty(value) || URL_REGEXP.test(value);
19685
19792
  };
19686
19793
  }
@@ -19692,7 +19799,8 @@ function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
19692
19799
  stringBasedInputType(ctrl);
19693
19800
 
19694
19801
  ctrl.$$parserName = 'email';
19695
- ctrl.$validators.email = function(value) {
19802
+ ctrl.$validators.email = function(modelValue, viewValue) {
19803
+ var value = modelValue || viewValue;
19696
19804
  return ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value);
19697
19805
  };
19698
19806
  }
@@ -19746,9 +19854,11 @@ function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filt
19746
19854
  element[0].checked = ctrl.$viewValue;
19747
19855
  };
19748
19856
 
19749
- // Override the standard `$isEmpty` because an empty checkbox is never equal to the trueValue
19857
+ // Override the standard `$isEmpty` because the $viewValue of an empty checkbox is always set to `false`
19858
+ // This is because of the parser below, which compares the `$modelValue` with `trueValue` to convert
19859
+ // it to a boolean.
19750
19860
  ctrl.$isEmpty = function(value) {
19751
- return value !== trueValue;
19861
+ return value === false;
19752
19862
  };
19753
19863
 
19754
19864
  ctrl.$formatters.push(function(value) {
@@ -19780,7 +19890,8 @@ function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filt
19780
19890
  * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
19781
19891
  * minlength.
19782
19892
  * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
19783
- * maxlength.
19893
+ * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
19894
+ * length.
19784
19895
  * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
19785
19896
  * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
19786
19897
  * patterns defined as scope expressions.
@@ -19812,7 +19923,8 @@ function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filt
19812
19923
  * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
19813
19924
  * minlength.
19814
19925
  * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
19815
- * maxlength.
19926
+ * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
19927
+ * length.
19816
19928
  * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
19817
19929
  * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
19818
19930
  * patterns defined as scope expressions.
@@ -19941,12 +20053,17 @@ var VALID_CLASS = 'ng-valid',
19941
20053
  * @property {string} $viewValue Actual string value in the view.
19942
20054
  * @property {*} $modelValue The value in the model that the control is bound to.
19943
20055
  * @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever
19944
- the control reads value from the DOM. The functions are called in array order, each passing the value
19945
- through to the next. The last return value is forwarded to the $validators collection.
19946
- Used to sanitize / convert the value.
19947
- Returning undefined from a parser means a parse error occurred. No $validators will
19948
- run and the 'ngModel' will not be updated until the parse error is resolved. The parse error is stored
19949
- in 'ngModel.$error.parse'.
20056
+ the control reads value from the DOM. The functions are called in array order, each passing
20057
+ its return value through to the next. The last return value is forwarded to the
20058
+ {@link ngModel.NgModelController#$validators `$validators`} collection.
20059
+
20060
+ Parsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue
20061
+ `$viewValue`}.
20062
+
20063
+ Returning `undefined` from a parser means a parse error occurred. In that case,
20064
+ no {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel`
20065
+ will be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`}
20066
+ is set to `true`. The parse error is stored in `ngModel.$error.parse`.
19950
20067
 
19951
20068
  *
19952
20069
  * @property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever
@@ -20023,13 +20140,18 @@ var VALID_CLASS = 'ng-valid',
20023
20140
  *
20024
20141
  * @description
20025
20142
  *
20026
- * `NgModelController` provides API for the `ng-model` directive. The controller contains
20027
- * services for data-binding, validation, CSS updates, and value formatting and parsing. It
20028
- * purposefully does not contain any logic which deals with DOM rendering or listening to
20029
- * DOM events. Such DOM related logic should be provided by other directives which make use of
20030
- * `NgModelController` for data-binding.
20143
+ * `NgModelController` provides API for the {@link ngModel `ngModel`} directive.
20144
+ * The controller contains services for data-binding, validation, CSS updates, and value formatting
20145
+ * and parsing. It purposefully does not contain any logic which deals with DOM rendering or
20146
+ * listening to DOM events.
20147
+ * Such DOM related logic should be provided by other directives which make use of
20148
+ * `NgModelController` for data-binding to control elements.
20149
+ * Angular provides this DOM logic for most {@link input `input`} elements.
20150
+ * At the end of this page you can find a {@link ngModel.NgModelController#custom-control-example
20151
+ * custom control example} that uses `ngModelController` to bind to `contenteditable` elements.
20031
20152
  *
20032
- * ## Custom Control Example
20153
+ * @example
20154
+ * ### Custom Control Example
20033
20155
  * This example shows how to use `NgModelController` with a custom control to achieve
20034
20156
  * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`)
20035
20157
  * collaborate together to achieve the desired result.
@@ -20071,7 +20193,7 @@ var VALID_CLASS = 'ng-valid',
20071
20193
 
20072
20194
  // Listen for change events to enable binding
20073
20195
  element.on('blur keyup change', function() {
20074
- scope.$apply(read);
20196
+ scope.$evalAsync(read);
20075
20197
  });
20076
20198
  read(); // initialize
20077
20199
 
@@ -20126,6 +20248,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
20126
20248
  function($scope, $exceptionHandler, $attr, $element, $parse, $animate, $timeout, $rootScope, $q, $interpolate) {
20127
20249
  this.$viewValue = Number.NaN;
20128
20250
  this.$modelValue = Number.NaN;
20251
+ this.$$rawModelValue = undefined; // stores the parsed modelValue / model set from scope regardless of validity.
20129
20252
  this.$validators = {};
20130
20253
  this.$asyncValidators = {};
20131
20254
  this.$parsers = [];
@@ -20144,32 +20267,33 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
20144
20267
 
20145
20268
 
20146
20269
  var parsedNgModel = $parse($attr.ngModel),
20270
+ parsedNgModelAssign = parsedNgModel.assign,
20271
+ ngModelGet = parsedNgModel,
20272
+ ngModelSet = parsedNgModelAssign,
20147
20273
  pendingDebounce = null,
20148
20274
  ctrl = this;
20149
20275
 
20150
- var ngModelGet = function ngModelGet() {
20151
- var modelValue = parsedNgModel($scope);
20152
- if (ctrl.$options && ctrl.$options.getterSetter && isFunction(modelValue)) {
20153
- modelValue = modelValue();
20154
- }
20155
- return modelValue;
20156
- };
20157
-
20158
- var ngModelSet = function ngModelSet(newValue) {
20159
- var getterSetter;
20160
- if (ctrl.$options && ctrl.$options.getterSetter &&
20161
- isFunction(getterSetter = parsedNgModel($scope))) {
20162
-
20163
- getterSetter(ctrl.$modelValue);
20164
- } else {
20165
- parsedNgModel.assign($scope, ctrl.$modelValue);
20166
- }
20167
- };
20168
-
20169
20276
  this.$$setOptions = function(options) {
20170
20277
  ctrl.$options = options;
20171
-
20172
- if (!parsedNgModel.assign && (!options || !options.getterSetter)) {
20278
+ if (options && options.getterSetter) {
20279
+ var invokeModelGetter = $parse($attr.ngModel + '()'),
20280
+ invokeModelSetter = $parse($attr.ngModel + '($$$p)');
20281
+
20282
+ ngModelGet = function($scope) {
20283
+ var modelValue = parsedNgModel($scope);
20284
+ if (isFunction(modelValue)) {
20285
+ modelValue = invokeModelGetter($scope);
20286
+ }
20287
+ return modelValue;
20288
+ };
20289
+ ngModelSet = function($scope, newValue) {
20290
+ if (isFunction(parsedNgModel($scope))) {
20291
+ invokeModelSetter($scope, {$$$p: ctrl.$modelValue});
20292
+ } else {
20293
+ parsedNgModelAssign($scope, ctrl.$modelValue);
20294
+ }
20295
+ };
20296
+ } else if (!parsedNgModel.assign) {
20173
20297
  throw $ngModelMinErr('nonassign', "Expression '{0}' is non-assignable. Element: {1}",
20174
20298
  $attr.ngModel, startingTag($element));
20175
20299
  }
@@ -20202,17 +20326,18 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
20202
20326
  * @name ngModel.NgModelController#$isEmpty
20203
20327
  *
20204
20328
  * @description
20205
- * This is called when we need to determine if the value of the input is empty.
20329
+ * This is called when we need to determine if the value of an input is empty.
20206
20330
  *
20207
20331
  * For instance, the required directive does this to work out if the input has data or not.
20332
+ *
20208
20333
  * The default `$isEmpty` function checks whether the value is `undefined`, `''`, `null` or `NaN`.
20209
20334
  *
20210
20335
  * You can override this for input directives whose concept of being empty is different to the
20211
20336
  * default. The `checkboxInputType` directive does this because in its case a value of `false`
20212
20337
  * implies empty.
20213
20338
  *
20214
- * @param {*} value Model value to check.
20215
- * @returns {boolean} True if `value` is empty.
20339
+ * @param {*} value The value of the input to check for emptiness.
20340
+ * @returns {boolean} True if `value` is "empty".
20216
20341
  */
20217
20342
  this.$isEmpty = function(value) {
20218
20343
  return isUndefined(value) || value === '' || value === null || value !== value;
@@ -20263,9 +20388,9 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
20263
20388
  * @description
20264
20389
  * Sets the control to its pristine state.
20265
20390
  *
20266
- * This method can be called to remove the 'ng-dirty' class and set the control to its pristine
20267
- * state (ng-pristine class). A model is considered to be pristine when the model has not been changed
20268
- * from when first compiled within then form.
20391
+ * This method can be called to remove the `ng-dirty` class and set the control to its pristine
20392
+ * state (`ng-pristine` class). A model is considered to be pristine when the control
20393
+ * has not been changed from when first compiled.
20269
20394
  */
20270
20395
  this.$setPristine = function() {
20271
20396
  ctrl.$dirty = false;
@@ -20274,6 +20399,25 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
20274
20399
  $animate.addClass($element, PRISTINE_CLASS);
20275
20400
  };
20276
20401
 
20402
+ /**
20403
+ * @ngdoc method
20404
+ * @name ngModel.NgModelController#$setDirty
20405
+ *
20406
+ * @description
20407
+ * Sets the control to its dirty state.
20408
+ *
20409
+ * This method can be called to remove the `ng-pristine` class and set the control to its dirty
20410
+ * state (`ng-dirty` class). A model is considered to be dirty when the control has been changed
20411
+ * from when first compiled.
20412
+ */
20413
+ this.$setDirty = function() {
20414
+ ctrl.$dirty = true;
20415
+ ctrl.$pristine = false;
20416
+ $animate.removeClass($element, PRISTINE_CLASS);
20417
+ $animate.addClass($element, DIRTY_CLASS);
20418
+ parentForm.$setDirty();
20419
+ };
20420
+
20277
20421
  /**
20278
20422
  * @ngdoc method
20279
20423
  * @name ngModel.NgModelController#$setUntouched
@@ -20281,8 +20425,8 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
20281
20425
  * @description
20282
20426
  * Sets the control to its untouched state.
20283
20427
  *
20284
- * This method can be called to remove the 'ng-touched' class and set the control to its
20285
- * untouched state (ng-untouched class). Upon compilation, a model is set as untouched
20428
+ * This method can be called to remove the `ng-touched` class and set the control to its
20429
+ * untouched state (`ng-untouched` class). Upon compilation, a model is set as untouched
20286
20430
  * by default, however this function can be used to restore that state if the model has
20287
20431
  * already been touched by the user.
20288
20432
  */
@@ -20299,10 +20443,9 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
20299
20443
  * @description
20300
20444
  * Sets the control to its touched state.
20301
20445
  *
20302
- * This method can be called to remove the 'ng-untouched' class and set the control to its
20303
- * touched state (ng-touched class). A model is considered to be touched when the user has
20304
- * first interacted (focussed) on the model input element and then shifted focus away (blurred)
20305
- * from the input element.
20446
+ * This method can be called to remove the `ng-untouched` class and set the control to its
20447
+ * touched state (`ng-touched` class). A model is considered to be touched when the user has
20448
+ * first focused the control element and then shifted focus away from the control (blur event).
20306
20449
  */
20307
20450
  this.$setTouched = function() {
20308
20451
  ctrl.$touched = true;
@@ -20380,14 +20523,51 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
20380
20523
  * @name ngModel.NgModelController#$validate
20381
20524
  *
20382
20525
  * @description
20383
- * Runs each of the registered validators (first synchronous validators and then asynchronous validators).
20526
+ * Runs each of the registered validators (first synchronous validators and then
20527
+ * asynchronous validators).
20528
+ * If the validity changes to invalid, the model will be set to `undefined`,
20529
+ * unless {@link ngModelOptions `ngModelOptions.allowInvalid`} is `true`.
20530
+ * If the validity changes to valid, it will set the model to the last available valid
20531
+ * modelValue, i.e. either the last parsed value or the last value set from the scope.
20384
20532
  */
20385
20533
  this.$validate = function() {
20386
20534
  // ignore $validate before model is initialized
20387
20535
  if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
20388
20536
  return;
20389
20537
  }
20390
- this.$$parseAndValidate();
20538
+
20539
+ var viewValue = ctrl.$$lastCommittedViewValue;
20540
+ // Note: we use the $$rawModelValue as $modelValue might have been
20541
+ // set to undefined during a view -> model update that found validation
20542
+ // errors. We can't parse the view here, since that could change
20543
+ // the model although neither viewValue nor the model on the scope changed
20544
+ var modelValue = ctrl.$$rawModelValue;
20545
+
20546
+ // Check if the there's a parse error, so we don't unset it accidentially
20547
+ var parserName = ctrl.$$parserName || 'parse';
20548
+ var parserValid = ctrl.$error[parserName] ? false : undefined;
20549
+
20550
+ var prevValid = ctrl.$valid;
20551
+ var prevModelValue = ctrl.$modelValue;
20552
+
20553
+ var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
20554
+
20555
+ ctrl.$$runValidators(parserValid, modelValue, viewValue, function(allValid) {
20556
+ // If there was no change in validity, don't update the model
20557
+ // This prevents changing an invalid modelValue to undefined
20558
+ if (!allowInvalid && prevValid !== allValid) {
20559
+ // Note: Don't check ctrl.$valid here, as we could have
20560
+ // external validators (e.g. calculated on the server),
20561
+ // that just call $setValidity and need the model value
20562
+ // to calculate their validity.
20563
+ ctrl.$modelValue = allValid ? modelValue : undefined;
20564
+
20565
+ if (ctrl.$modelValue !== prevModelValue) {
20566
+ ctrl.$$writeModelToScope();
20567
+ }
20568
+ }
20569
+ });
20570
+
20391
20571
  };
20392
20572
 
20393
20573
  this.$$runValidators = function(parseValid, modelValue, viewValue, doneCallback) {
@@ -20506,11 +20686,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
20506
20686
 
20507
20687
  // change to dirty
20508
20688
  if (ctrl.$pristine) {
20509
- ctrl.$dirty = true;
20510
- ctrl.$pristine = false;
20511
- $animate.removeClass($element, PRISTINE_CLASS);
20512
- $animate.addClass($element, DIRTY_CLASS);
20513
- parentForm.$setDirty();
20689
+ this.$setDirty();
20514
20690
  }
20515
20691
  this.$$parseAndValidate();
20516
20692
  };
@@ -20531,10 +20707,11 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
20531
20707
  }
20532
20708
  if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
20533
20709
  // ctrl.$modelValue has not been touched yet...
20534
- ctrl.$modelValue = ngModelGet();
20710
+ ctrl.$modelValue = ngModelGet($scope);
20535
20711
  }
20536
20712
  var prevModelValue = ctrl.$modelValue;
20537
20713
  var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
20714
+ ctrl.$$rawModelValue = modelValue;
20538
20715
  if (allowInvalid) {
20539
20716
  ctrl.$modelValue = modelValue;
20540
20717
  writeToModelIfNeeded();
@@ -20558,7 +20735,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
20558
20735
  };
20559
20736
 
20560
20737
  this.$$writeModelToScope = function() {
20561
- ngModelSet(ctrl.$modelValue);
20738
+ ngModelSet($scope, ctrl.$modelValue);
20562
20739
  forEach(ctrl.$viewChangeListeners, function(listener) {
20563
20740
  try {
20564
20741
  listener();
@@ -20654,12 +20831,12 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
20654
20831
  // ng-change executes in apply phase
20655
20832
  // 4. view should be changed back to 'a'
20656
20833
  $scope.$watch(function ngModelWatch() {
20657
- var modelValue = ngModelGet();
20834
+ var modelValue = ngModelGet($scope);
20658
20835
 
20659
20836
  // if scope model value and ngModel value are out of sync
20660
20837
  // TODO(perf): why not move this to the action fn?
20661
20838
  if (modelValue !== ctrl.$modelValue) {
20662
- ctrl.$modelValue = modelValue;
20839
+ ctrl.$modelValue = ctrl.$$rawModelValue = modelValue;
20663
20840
 
20664
20841
  var formatters = ctrl.$formatters,
20665
20842
  idx = formatters.length;
@@ -20844,7 +21021,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
20844
21021
  </file>
20845
21022
  * </example>
20846
21023
  */
20847
- var ngModelDirective = function() {
21024
+ var ngModelDirective = ['$rootScope', function($rootScope) {
20848
21025
  return {
20849
21026
  restrict: 'A',
20850
21027
  require: ['ngModel', '^?form', '^?ngModelOptions'],
@@ -20888,15 +21065,17 @@ var ngModelDirective = function() {
20888
21065
  element.on('blur', function(ev) {
20889
21066
  if (modelCtrl.$touched) return;
20890
21067
 
20891
- scope.$apply(function() {
20892
- modelCtrl.$setTouched();
20893
- });
21068
+ if ($rootScope.$$phase) {
21069
+ scope.$evalAsync(modelCtrl.$setTouched);
21070
+ } else {
21071
+ scope.$apply(modelCtrl.$setTouched);
21072
+ }
20894
21073
  });
20895
21074
  }
20896
21075
  };
20897
21076
  }
20898
21077
  };
20899
- };
21078
+ }];
20900
21079
 
20901
21080
 
20902
21081
  /**
@@ -20985,8 +21164,8 @@ var requiredDirective = function() {
20985
21164
  if (!ctrl) return;
20986
21165
  attr.required = true; // force truthy in case we are on non input element
20987
21166
 
20988
- ctrl.$validators.required = function(value) {
20989
- return !attr.required || !ctrl.$isEmpty(value);
21167
+ ctrl.$validators.required = function(modelValue, viewValue) {
21168
+ return !attr.required || !ctrl.$isEmpty(viewValue);
20990
21169
  };
20991
21170
 
20992
21171
  attr.$observe('required', function() {
@@ -21007,7 +21186,7 @@ var patternDirective = function() {
21007
21186
  var regexp, patternExp = attr.ngPattern || attr.pattern;
21008
21187
  attr.$observe('pattern', function(regex) {
21009
21188
  if (isString(regex) && regex.length > 0) {
21010
- regex = new RegExp(regex);
21189
+ regex = new RegExp('^' + regex + '$');
21011
21190
  }
21012
21191
 
21013
21192
  if (regex && !regex.test) {
@@ -21035,13 +21214,14 @@ var maxlengthDirective = function() {
21035
21214
  link: function(scope, elm, attr, ctrl) {
21036
21215
  if (!ctrl) return;
21037
21216
 
21038
- var maxlength = 0;
21217
+ var maxlength = -1;
21039
21218
  attr.$observe('maxlength', function(value) {
21040
- maxlength = int(value) || 0;
21219
+ var intVal = int(value);
21220
+ maxlength = isNaN(intVal) ? -1 : intVal;
21041
21221
  ctrl.$validate();
21042
21222
  });
21043
21223
  ctrl.$validators.maxlength = function(modelValue, viewValue) {
21044
- return ctrl.$isEmpty(modelValue) || viewValue.length <= maxlength;
21224
+ return (maxlength < 0) || ctrl.$isEmpty(modelValue) || (viewValue.length <= maxlength);
21045
21225
  };
21046
21226
  }
21047
21227
  };
@@ -21060,7 +21240,7 @@ var minlengthDirective = function() {
21060
21240
  ctrl.$validate();
21061
21241
  });
21062
21242
  ctrl.$validators.minlength = function(modelValue, viewValue) {
21063
- return ctrl.$isEmpty(modelValue) || viewValue.length >= minlength;
21243
+ return ctrl.$isEmpty(viewValue) || viewValue.length >= minlength;
21064
21244
  };
21065
21245
  }
21066
21246
  };
@@ -21298,7 +21478,7 @@ var ngValueDirective = function() {
21298
21478
  * `ngModelOptions` has an effect on the element it's declared on and its descendants.
21299
21479
  *
21300
21480
  * @param {Object} ngModelOptions options to apply to the current model. Valid keys are:
21301
- * - `updateOn`: string specifying which event should be the input bound to. You can set several
21481
+ * - `updateOn`: string specifying which event should the input be bound to. You can set several
21302
21482
  * events using an space delimited list. There is a special event called `default` that
21303
21483
  * matches the default events belonging of the control.
21304
21484
  * - `debounce`: integer value which contains the debounce model update value in milliseconds. A
@@ -21688,12 +21868,11 @@ var ngBindTemplateDirective = ['$interpolate', '$compile', function($interpolate
21688
21868
  * @name ngBindHtml
21689
21869
  *
21690
21870
  * @description
21691
- * Creates a binding that will innerHTML the result of evaluating the `expression` into the current
21692
- * element in a secure way. By default, the innerHTML-ed content will be sanitized using the {@link
21693
- * ngSanitize.$sanitize $sanitize} service. To utilize this functionality, ensure that `$sanitize`
21694
- * is available, for example, by including {@link ngSanitize} in your module's dependencies (not in
21695
- * core Angular). In order to use {@link ngSanitize} in your module's dependencies, you need to
21696
- * include "angular-sanitize.js" in your application.
21871
+ * Evaluates the expression and inserts the resulting HTML into the element in a secure way. By default,
21872
+ * the resulting HTML content will be sanitized using the {@link ngSanitize.$sanitize $sanitize} service.
21873
+ * To utilize this functionality, ensure that `$sanitize` is available, for example, by including {@link
21874
+ * ngSanitize} in your module's dependencies (not in core Angular). In order to use {@link ngSanitize}
21875
+ * in your module's dependencies, you need to include "angular-sanitize.js" in your application.
21697
21876
  *
21698
21877
  * You may also bypass sanitization for values you know are safe. To do so, bind to
21699
21878
  * an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}. See the example
@@ -23757,7 +23936,9 @@ var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 });
23757
23936
  </example>
23758
23937
  */
23759
23938
  var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interpolate) {
23760
- var BRACE = /{}/g;
23939
+ var BRACE = /{}/g,
23940
+ IS_WHEN = /^when(Minus)?(.+)$/;
23941
+
23761
23942
  return {
23762
23943
  restrict: 'EA',
23763
23944
  link: function(scope, element, attr) {
@@ -23768,34 +23949,44 @@ var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interp
23768
23949
  whensExpFns = {},
23769
23950
  startSymbol = $interpolate.startSymbol(),
23770
23951
  endSymbol = $interpolate.endSymbol(),
23771
- isWhen = /^when(Minus)?(.+)$/;
23952
+ braceReplacement = startSymbol + numberExp + '-' + offset + endSymbol,
23953
+ watchRemover = angular.noop,
23954
+ lastCount;
23772
23955
 
23773
23956
  forEach(attr, function(expression, attributeName) {
23774
- if (isWhen.test(attributeName)) {
23775
- whens[lowercase(attributeName.replace('when', '').replace('Minus', '-'))] =
23776
- element.attr(attr.$attr[attributeName]);
23957
+ var tmpMatch = IS_WHEN.exec(attributeName);
23958
+ if (tmpMatch) {
23959
+ var whenKey = (tmpMatch[1] ? '-' : '') + lowercase(tmpMatch[2]);
23960
+ whens[whenKey] = element.attr(attr.$attr[attributeName]);
23777
23961
  }
23778
23962
  });
23779
23963
  forEach(whens, function(expression, key) {
23780
- whensExpFns[key] =
23781
- $interpolate(expression.replace(BRACE, startSymbol + numberExp + '-' +
23782
- offset + endSymbol));
23964
+ whensExpFns[key] = $interpolate(expression.replace(BRACE, braceReplacement));
23965
+
23783
23966
  });
23784
23967
 
23785
- scope.$watch(function ngPluralizeWatch() {
23786
- var value = parseFloat(scope.$eval(numberExp));
23968
+ scope.$watch(numberExp, function ngPluralizeWatchAction(newVal) {
23969
+ var count = parseFloat(newVal);
23970
+ var countIsNaN = isNaN(count);
23787
23971
 
23788
- if (!isNaN(value)) {
23789
- //if explicit number rule such as 1, 2, 3... is defined, just use it. Otherwise,
23790
- //check it against pluralization rules in $locale service
23791
- if (!(value in whens)) value = $locale.pluralCat(value - offset);
23792
- return whensExpFns[value](scope);
23793
- } else {
23794
- return '';
23972
+ if (!countIsNaN && !(count in whens)) {
23973
+ // If an explicit number rule such as 1, 2, 3... is defined, just use it.
23974
+ // Otherwise, check it against pluralization rules in $locale service.
23975
+ count = $locale.pluralCat(count - offset);
23976
+ }
23977
+
23978
+ // If both `count` and `lastCount` are NaN, we don't need to re-register a watch.
23979
+ // In JS `NaN !== NaN`, so we have to exlicitly check.
23980
+ if ((count !== lastCount) && !(countIsNaN && isNaN(lastCount))) {
23981
+ watchRemover();
23982
+ watchRemover = scope.$watch(whensExpFns[count], updateElementText);
23983
+ lastCount = count;
23795
23984
  }
23796
- }, function ngPluralizeWatchAction(newVal) {
23797
- element.text(newVal);
23798
23985
  });
23986
+
23987
+ function updateElementText(newText) {
23988
+ element.text(newText || '');
23989
+ }
23799
23990
  }
23800
23991
  };
23801
23992
  }];
@@ -24166,7 +24357,7 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
24166
24357
  });
24167
24358
  throw ngRepeatMinErr('dupes',
24168
24359
  "Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}, Duplicate value: {2}",
24169
- expression, trackById, toJson(value));
24360
+ expression, trackById, value);
24170
24361
  } else {
24171
24362
  // new never before seen block
24172
24363
  nextBlockOrder[index] = {id: trackById, scope: undefined, clone: undefined};
@@ -24277,17 +24468,17 @@ var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
24277
24468
  *
24278
24469
  * ### Overriding `.ng-hide`
24279
24470
  *
24280
- * By default, the `.ng-hide` class will style the element with `display:none!important`. If you wish to change
24471
+ * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
24281
24472
  * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
24282
24473
  * class in CSS:
24283
24474
  *
24284
24475
  * ```css
24285
24476
  * .ng-hide {
24286
24477
  * /&#42; this is just another form of hiding an element &#42;/
24287
- * display:block!important;
24288
- * position:absolute;
24289
- * top:-9999px;
24290
- * left:-9999px;
24478
+ * display: block!important;
24479
+ * position: absolute;
24480
+ * top: -9999px;
24481
+ * left: -9999px;
24291
24482
  * }
24292
24483
  * ```
24293
24484
  *
@@ -24307,13 +24498,13 @@ var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
24307
24498
  * .my-element.ng-hide-add, .my-element.ng-hide-remove {
24308
24499
  * /&#42; this is required as of 1.3x to properly
24309
24500
  * apply all styling in a show/hide animation &#42;/
24310
- * transition:0s linear all;
24501
+ * transition: 0s linear all;
24311
24502
  * }
24312
24503
  *
24313
24504
  * .my-element.ng-hide-add-active,
24314
24505
  * .my-element.ng-hide-remove-active {
24315
24506
  * /&#42; the transition is defined in the active class &#42;/
24316
- * transition:1s linear all;
24507
+ * transition: 1s linear all;
24317
24508
  * }
24318
24509
  *
24319
24510
  * .my-element.ng-hide-add { ... }
@@ -24355,29 +24546,29 @@ var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
24355
24546
  </file>
24356
24547
  <file name="animations.css">
24357
24548
  .animate-show {
24358
- line-height:20px;
24359
- opacity:1;
24360
- padding:10px;
24361
- border:1px solid black;
24362
- background:white;
24549
+ line-height: 20px;
24550
+ opacity: 1;
24551
+ padding: 10px;
24552
+ border: 1px solid black;
24553
+ background: white;
24363
24554
  }
24364
24555
 
24365
24556
  .animate-show.ng-hide-add.ng-hide-add-active,
24366
24557
  .animate-show.ng-hide-remove.ng-hide-remove-active {
24367
- -webkit-transition:all linear 0.5s;
24368
- transition:all linear 0.5s;
24558
+ -webkit-transition: all linear 0.5s;
24559
+ transition: all linear 0.5s;
24369
24560
  }
24370
24561
 
24371
24562
  .animate-show.ng-hide {
24372
- line-height:0;
24373
- opacity:0;
24374
- padding:0 10px;
24563
+ line-height: 0;
24564
+ opacity: 0;
24565
+ padding: 0 10px;
24375
24566
  }
24376
24567
 
24377
24568
  .check-element {
24378
- padding:10px;
24379
- border:1px solid black;
24380
- background:white;
24569
+ padding: 10px;
24570
+ border: 1px solid black;
24571
+ background: white;
24381
24572
  }
24382
24573
  </file>
24383
24574
  <file name="protractor.js" type="protractor">
@@ -24451,17 +24642,17 @@ var ngShowDirective = ['$animate', function($animate) {
24451
24642
  *
24452
24643
  * ### Overriding `.ng-hide`
24453
24644
  *
24454
- * By default, the `.ng-hide` class will style the element with `display:none!important`. If you wish to change
24645
+ * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
24455
24646
  * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
24456
24647
  * class in CSS:
24457
24648
  *
24458
24649
  * ```css
24459
24650
  * .ng-hide {
24460
24651
  * /&#42; this is just another form of hiding an element &#42;/
24461
- * display:block!important;
24462
- * position:absolute;
24463
- * top:-9999px;
24464
- * left:-9999px;
24652
+ * display: block!important;
24653
+ * position: absolute;
24654
+ * top: -9999px;
24655
+ * left: -9999px;
24465
24656
  * }
24466
24657
  * ```
24467
24658
  *
@@ -24478,7 +24669,7 @@ var ngShowDirective = ['$animate', function($animate) {
24478
24669
  * //a working example can be found at the bottom of this page
24479
24670
  * //
24480
24671
  * .my-element.ng-hide-add, .my-element.ng-hide-remove {
24481
- * transition:0.5s linear all;
24672
+ * transition: 0.5s linear all;
24482
24673
  * }
24483
24674
  *
24484
24675
  * .my-element.ng-hide-add { ... }
@@ -24520,25 +24711,25 @@ var ngShowDirective = ['$animate', function($animate) {
24520
24711
  </file>
24521
24712
  <file name="animations.css">
24522
24713
  .animate-hide {
24523
- -webkit-transition:all linear 0.5s;
24524
- transition:all linear 0.5s;
24525
- line-height:20px;
24526
- opacity:1;
24527
- padding:10px;
24528
- border:1px solid black;
24529
- background:white;
24714
+ -webkit-transition: all linear 0.5s;
24715
+ transition: all linear 0.5s;
24716
+ line-height: 20px;
24717
+ opacity: 1;
24718
+ padding: 10px;
24719
+ border: 1px solid black;
24720
+ background: white;
24530
24721
  }
24531
24722
 
24532
24723
  .animate-hide.ng-hide {
24533
- line-height:0;
24534
- opacity:0;
24535
- padding:0 10px;
24724
+ line-height: 0;
24725
+ opacity: 0;
24726
+ padding: 0 10px;
24536
24727
  }
24537
24728
 
24538
24729
  .check-element {
24539
- padding:10px;
24540
- border:1px solid black;
24541
- background:white;
24730
+ padding: 10px;
24731
+ border: 1px solid black;
24732
+ background: white;
24542
24733
  }
24543
24734
  </file>
24544
24735
  <file name="protractor.js" type="protractor">
@@ -24972,7 +25163,7 @@ var ngOptionsMinErr = minErr('ngOptions');
24972
25163
  * In many cases, `ngRepeat` can be used on `<option>` elements instead of `ngOptions` to achieve a
24973
25164
  * similar result. However, the `ngOptions` provides some benefits such as reducing memory and
24974
25165
  * increasing speed by not creating a new scope for each repeated instance, as well as providing
24975
- * more flexibility in how the `select`'s model is assigned via `select as`. `ngOptions should be
25166
+ * more flexibility in how the `select`'s model is assigned via `select as`. `ngOptions` should be
24976
25167
  * used when the `select` model needs to be bound to a non-string value. This is because an option
24977
25168
  * element can only be bound to string values at present.
24978
25169
  *
@@ -25570,13 +25761,14 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
25570
25761
  lastElement = null; // start at the beginning
25571
25762
  for (index = 0, length = optionGroup.length; index < length; index++) {
25572
25763
  option = optionGroup[index];
25573
- if ((existingOption = existingOptions[index+1])) {
25764
+ if ((existingOption = existingOptions[index + 1])) {
25574
25765
  // reuse elements
25575
25766
  lastElement = existingOption.element;
25576
25767
  if (existingOption.label !== option.label) {
25577
25768
  updateLabelMap(labelMap, existingOption.label, false);
25578
25769
  updateLabelMap(labelMap, option.label, true);
25579
25770
  lastElement.text(existingOption.label = option.label);
25771
+ lastElement.prop('label', existingOption.label);
25580
25772
  }
25581
25773
  if (existingOption.id !== option.id) {
25582
25774
  lastElement.val(existingOption.id = option.id);
@@ -25606,6 +25798,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
25606
25798
  .val(option.id)
25607
25799
  .prop('selected', option.selected)
25608
25800
  .attr('selected', option.selected)
25801
+ .prop('label', option.label)
25609
25802
  .text(option.label);
25610
25803
  }
25611
25804