angularjs-on-rails 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license AngularJS v1.2.18
2
+ * @license AngularJS v1.2.21
3
3
  * (c) 2010-2014 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license AngularJS v1.2.18
2
+ * @license AngularJS v1.2.21
3
3
  * (c) 2010-2014 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
@@ -68,7 +68,7 @@ function minErr(module) {
68
68
  return match;
69
69
  });
70
70
 
71
- message = message + '\nhttp://errors.angularjs.org/1.2.18/' +
71
+ message = message + '\nhttp://errors.angularjs.org/1.2.21/' +
72
72
  (module ? module + '/' : '') + code;
73
73
  for (i = 2; i < arguments.length; i++) {
74
74
  message = message + (i == 2 ? '?' : '&') + 'p' + (i-2) + '=' +
@@ -80,88 +80,88 @@ function minErr(module) {
80
80
  }
81
81
 
82
82
  /* We need to tell jshint what variables are being exported */
83
- /* global
84
- -angular,
85
- -msie,
86
- -jqLite,
87
- -jQuery,
88
- -slice,
89
- -push,
90
- -toString,
91
- -ngMinErr,
92
- -angularModule,
93
- -nodeName_,
94
- -uid,
95
-
96
- -lowercase,
97
- -uppercase,
98
- -manualLowercase,
99
- -manualUppercase,
100
- -nodeName_,
101
- -isArrayLike,
102
- -forEach,
103
- -sortedKeys,
104
- -forEachSorted,
105
- -reverseParams,
106
- -nextUid,
107
- -setHashKey,
108
- -extend,
109
- -int,
110
- -inherit,
111
- -noop,
112
- -identity,
113
- -valueFn,
114
- -isUndefined,
115
- -isDefined,
116
- -isObject,
117
- -isString,
118
- -isNumber,
119
- -isDate,
120
- -isArray,
121
- -isFunction,
122
- -isRegExp,
123
- -isWindow,
124
- -isScope,
125
- -isFile,
126
- -isBlob,
127
- -isBoolean,
128
- -trim,
129
- -isElement,
130
- -makeMap,
131
- -map,
132
- -size,
133
- -includes,
134
- -indexOf,
135
- -arrayRemove,
136
- -isLeafNode,
137
- -copy,
138
- -shallowCopy,
139
- -equals,
140
- -csp,
141
- -concat,
142
- -sliceArgs,
143
- -bind,
144
- -toJsonReplacer,
145
- -toJson,
146
- -fromJson,
147
- -toBoolean,
148
- -startingTag,
149
- -tryDecodeURIComponent,
150
- -parseKeyValue,
151
- -toKeyValue,
152
- -encodeUriSegment,
153
- -encodeUriQuery,
154
- -angularInit,
155
- -bootstrap,
156
- -snake_case,
157
- -bindJQuery,
158
- -assertArg,
159
- -assertArgFn,
160
- -assertNotHasOwnProperty,
161
- -getter,
162
- -getBlockElements,
163
- -hasOwnProperty,
164
-
83
+ /* global angular: true,
84
+ msie: true,
85
+ jqLite: true,
86
+ jQuery: true,
87
+ slice: true,
88
+ push: true,
89
+ toString: true,
90
+ ngMinErr: true,
91
+ angularModule: true,
92
+ nodeName_: true,
93
+ uid: true,
94
+ VALIDITY_STATE_PROPERTY: true,
95
+
96
+ lowercase: true,
97
+ uppercase: true,
98
+ manualLowercase: true,
99
+ manualUppercase: true,
100
+ nodeName_: true,
101
+ isArrayLike: true,
102
+ forEach: true,
103
+ sortedKeys: true,
104
+ forEachSorted: true,
105
+ reverseParams: true,
106
+ nextUid: true,
107
+ setHashKey: true,
108
+ extend: true,
109
+ int: true,
110
+ inherit: true,
111
+ noop: true,
112
+ identity: true,
113
+ valueFn: true,
114
+ isUndefined: true,
115
+ isDefined: true,
116
+ isObject: true,
117
+ isString: true,
118
+ isNumber: true,
119
+ isDate: true,
120
+ isArray: true,
121
+ isFunction: true,
122
+ isRegExp: true,
123
+ isWindow: true,
124
+ isScope: true,
125
+ isFile: true,
126
+ isBlob: true,
127
+ isBoolean: true,
128
+ isPromiseLike: true,
129
+ trim: true,
130
+ isElement: true,
131
+ makeMap: true,
132
+ map: true,
133
+ size: true,
134
+ includes: true,
135
+ indexOf: true,
136
+ arrayRemove: true,
137
+ isLeafNode: true,
138
+ copy: true,
139
+ shallowCopy: true,
140
+ equals: true,
141
+ csp: true,
142
+ concat: true,
143
+ sliceArgs: true,
144
+ bind: true,
145
+ toJsonReplacer: true,
146
+ toJson: true,
147
+ fromJson: true,
148
+ toBoolean: true,
149
+ startingTag: true,
150
+ tryDecodeURIComponent: true,
151
+ parseKeyValue: true,
152
+ toKeyValue: true,
153
+ encodeUriSegment: true,
154
+ encodeUriQuery: true,
155
+ angularInit: true,
156
+ bootstrap: true,
157
+ snake_case: true,
158
+ bindJQuery: true,
159
+ assertArg: true,
160
+ assertArgFn: true,
161
+ assertNotHasOwnProperty: true,
162
+ getter: true,
163
+ getBlockElements: true,
164
+ hasOwnProperty: true,
165
165
  */
166
166
 
167
167
  ////////////////////////////////////
@@ -181,6 +181,10 @@ function minErr(module) {
181
181
  * <div doc-module-components="ng"></div>
182
182
  */
183
183
 
184
+ // The name of a form control's ValidityState property.
185
+ // This is used so that it's possible for internal tests to create mock ValidityStates.
186
+ var VALIDITY_STATE_PROPERTY = 'validity';
187
+
184
188
  /**
185
189
  * @ngdoc function
186
190
  * @name angular.lowercase
@@ -316,11 +320,12 @@ function forEach(obj, iterator, context) {
316
320
  iterator.call(context, obj[key], key);
317
321
  }
318
322
  }
319
- } else if (obj.forEach && obj.forEach !== forEach) {
320
- obj.forEach(iterator, context);
321
- } else if (isArrayLike(obj)) {
322
- for (key = 0; key < obj.length; key++)
323
+ } else if (isArray(obj) || isArrayLike(obj)) {
324
+ for (key = 0; key < obj.length; key++) {
323
325
  iterator.call(context, obj[key], key);
326
+ }
327
+ } else if (obj.forEach && obj.forEach !== forEach) {
328
+ obj.forEach(iterator, context);
324
329
  } else {
325
330
  for (key in obj) {
326
331
  if (obj.hasOwnProperty(key)) {
@@ -657,6 +662,11 @@ function isBoolean(value) {
657
662
  }
658
663
 
659
664
 
665
+ function isPromiseLike(obj) {
666
+ return obj && isFunction(obj.then);
667
+ }
668
+
669
+
660
670
  var trim = (function() {
661
671
  // native trim is way faster: http://jsperf.com/angular-trim-test
662
672
  // but IE doesn't have it... :-(
@@ -805,9 +815,9 @@ function isLeafNode (node) {
805
815
  * @returns {*} The copy or updated `destination`, if `destination` was specified.
806
816
  *
807
817
  * @example
808
- <example>
818
+ <example module="copyExample">
809
819
  <file name="index.html">
810
- <div ng-controller="Controller">
820
+ <div ng-controller="ExampleController">
811
821
  <form novalidate class="simple-form">
812
822
  Name: <input type="text" ng-model="user.name" /><br />
813
823
  E-mail: <input type="email" ng-model="user.email" /><br />
@@ -821,21 +831,22 @@ function isLeafNode (node) {
821
831
  </div>
822
832
 
823
833
  <script>
824
- function Controller($scope) {
825
- $scope.master= {};
834
+ angular.module('copyExample', [])
835
+ .controller('ExampleController', ['$scope', function($scope) {
836
+ $scope.master= {};
826
837
 
827
- $scope.update = function(user) {
828
- // Example with 1 argument
829
- $scope.master= angular.copy(user);
830
- };
838
+ $scope.update = function(user) {
839
+ // Example with 1 argument
840
+ $scope.master= angular.copy(user);
841
+ };
831
842
 
832
- $scope.reset = function() {
833
- // Example with 2 arguments
834
- angular.copy($scope.master, $scope.user);
835
- };
843
+ $scope.reset = function() {
844
+ // Example with 2 arguments
845
+ angular.copy($scope.master, $scope.user);
846
+ };
836
847
 
837
- $scope.reset();
838
- }
848
+ $scope.reset();
849
+ }]);
839
850
  </script>
840
851
  </file>
841
852
  </example>
@@ -854,7 +865,8 @@ function copy(source, destination, stackSource, stackDest) {
854
865
  } else if (isDate(source)) {
855
866
  destination = new Date(source.getTime());
856
867
  } else if (isRegExp(source)) {
857
- destination = new RegExp(source.source);
868
+ destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
869
+ destination.lastIndex = source.lastIndex;
858
870
  } else if (isObject(source)) {
859
871
  destination = copy(source, {}, stackSource, stackDest);
860
872
  }
@@ -998,12 +1010,25 @@ function equals(o1, o2) {
998
1010
  return false;
999
1011
  }
1000
1012
 
1013
+ var csp = function() {
1014
+ if (isDefined(csp.isActive_)) return csp.isActive_;
1015
+
1016
+ var active = !!(document.querySelector('[ng-csp]') ||
1017
+ document.querySelector('[data-ng-csp]'));
1018
+
1019
+ if (!active) {
1020
+ try {
1021
+ /* jshint -W031, -W054 */
1022
+ new Function('');
1023
+ /* jshint +W031, +W054 */
1024
+ } catch (e) {
1025
+ active = true;
1026
+ }
1027
+ }
1028
+
1029
+ return (csp.isActive_ = active);
1030
+ };
1001
1031
 
1002
- function csp() {
1003
- return (document.securityPolicy && document.securityPolicy.isActive) ||
1004
- (document.querySelector &&
1005
- !!(document.querySelector('[ng-csp]') || document.querySelector('[data-ng-csp]')));
1006
- }
1007
1032
 
1008
1033
 
1009
1034
  function concat(array1, array2, index) {
@@ -1175,11 +1200,11 @@ function parseKeyValue(/**string*/keyValue) {
1175
1200
  var obj = {}, key_value, key;
1176
1201
  forEach((keyValue || "").split('&'), function(keyValue) {
1177
1202
  if ( keyValue ) {
1178
- key_value = keyValue.split('=');
1203
+ key_value = keyValue.replace(/\+/g,'%20').split('=');
1179
1204
  key = tryDecodeURIComponent(key_value[0]);
1180
1205
  if ( isDefined(key) ) {
1181
1206
  var val = isDefined(key_value[1]) ? tryDecodeURIComponent(key_value[1]) : true;
1182
- if (!obj[key]) {
1207
+ if (!hasOwnProperty.call(obj, key)) {
1183
1208
  obj[key] = val;
1184
1209
  } else if(isArray(obj[key])) {
1185
1210
  obj[key].push(val);
@@ -1353,7 +1378,7 @@ function angularInit(element, bootstrap) {
1353
1378
  *
1354
1379
  * Angular will detect if it has been loaded into the browser more than once and only allow the
1355
1380
  * first loaded script to be bootstrapped and will report a warning to the browser console for
1356
- * each of the subsequent scripts. This prevents strange results in applications, where otherwise
1381
+ * each of the subsequent scripts. This prevents strange results in applications, where otherwise
1357
1382
  * multiple instances of Angular try to work on the DOM.
1358
1383
  *
1359
1384
  * <example name="multi-bootstrap" module="multi-bootstrap">
@@ -1483,7 +1508,7 @@ function assertArgFn(arg, name, acceptArrayAnnotation) {
1483
1508
  }
1484
1509
 
1485
1510
  assertArg(isFunction(arg), name, 'not a function, got ' +
1486
- (arg && typeof arg == 'object' ? arg.constructor.name || 'Object' : typeof arg));
1511
+ (arg && typeof arg === 'object' ? arg.constructor.name || 'Object' : typeof arg));
1487
1512
  return arg;
1488
1513
  }
1489
1514
 
@@ -1860,12 +1885,11 @@ function setupModuleLoader(window) {
1860
1885
 
1861
1886
  }
1862
1887
 
1863
- /* global
1864
- angularModule: true,
1865
- version: true,
1888
+ /* global angularModule: true,
1889
+ version: true,
1866
1890
 
1867
- $LocaleProvider,
1868
- $CompileProvider,
1891
+ $LocaleProvider,
1892
+ $CompileProvider,
1869
1893
 
1870
1894
  htmlAnchorDirective,
1871
1895
  inputDirective,
@@ -1953,11 +1977,11 @@ function setupModuleLoader(window) {
1953
1977
  * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
1954
1978
  */
1955
1979
  var version = {
1956
- full: '1.2.18', // all of these placeholder strings will be replaced by grunt's
1980
+ full: '1.2.21', // all of these placeholder strings will be replaced by grunt's
1957
1981
  major: 1, // package task
1958
1982
  minor: 2,
1959
- dot: 18,
1960
- codeName: 'ear-extendability'
1983
+ dot: 21,
1984
+ codeName: 'wizard-props'
1961
1985
  };
1962
1986
 
1963
1987
 
@@ -2081,12 +2105,10 @@ function publishExternalAPI(angular){
2081
2105
  ]);
2082
2106
  }
2083
2107
 
2084
- /* global
2085
-
2086
- -JQLitePrototype,
2087
- -addEventListenerFn,
2088
- -removeEventListenerFn,
2089
- -BOOLEAN_ATTR
2108
+ /* global JQLitePrototype: true,
2109
+ addEventListenerFn: true,
2110
+ removeEventListenerFn: true,
2111
+ BOOLEAN_ATTR: true
2090
2112
  */
2091
2113
 
2092
2114
  //////////////////////////////////
@@ -2179,8 +2201,9 @@ function publishExternalAPI(angular){
2179
2201
  * @returns {Object} jQuery object.
2180
2202
  */
2181
2203
 
2204
+ JQLite.expando = 'ng339';
2205
+
2182
2206
  var jqCache = JQLite.cache = {},
2183
- jqName = JQLite.expando = 'ng' + new Date().getTime(),
2184
2207
  jqId = 1,
2185
2208
  addEventListenerFn = (window.document.addEventListener
2186
2209
  ? function(element, type, fn) {element.addEventListener(type, fn, false);}
@@ -2390,7 +2413,7 @@ function jqLiteOff(element, type, fn, unsupported) {
2390
2413
  }
2391
2414
 
2392
2415
  function jqLiteRemoveData(element, name) {
2393
- var expandoId = element[jqName],
2416
+ var expandoId = element.ng339,
2394
2417
  expandoStore = jqCache[expandoId];
2395
2418
 
2396
2419
  if (expandoStore) {
@@ -2404,17 +2427,17 @@ function jqLiteRemoveData(element, name) {
2404
2427
  jqLiteOff(element);
2405
2428
  }
2406
2429
  delete jqCache[expandoId];
2407
- element[jqName] = undefined; // ie does not allow deletion of attributes on elements.
2430
+ element.ng339 = undefined; // don't delete DOM expandos. IE and Chrome don't like it
2408
2431
  }
2409
2432
  }
2410
2433
 
2411
2434
  function jqLiteExpandoStore(element, key, value) {
2412
- var expandoId = element[jqName],
2435
+ var expandoId = element.ng339,
2413
2436
  expandoStore = jqCache[expandoId || -1];
2414
2437
 
2415
2438
  if (isDefined(value)) {
2416
2439
  if (!expandoStore) {
2417
- element[jqName] = expandoId = jqNextId();
2440
+ element.ng339 = expandoId = jqNextId();
2418
2441
  expandoStore = jqCache[expandoId] = {};
2419
2442
  }
2420
2443
  expandoStore[key] = value;
@@ -2499,25 +2522,22 @@ function jqLiteController(element, name) {
2499
2522
  }
2500
2523
 
2501
2524
  function jqLiteInheritedData(element, name, value) {
2502
- element = jqLite(element);
2503
-
2504
2525
  // if element is the document object work with the html element instead
2505
2526
  // this makes $(document).scope() possible
2506
- if(element[0].nodeType == 9) {
2507
- element = element.find('html');
2527
+ if(element.nodeType == 9) {
2528
+ element = element.documentElement;
2508
2529
  }
2509
2530
  var names = isArray(name) ? name : [name];
2510
2531
 
2511
- while (element.length) {
2512
- var node = element[0];
2532
+ while (element) {
2513
2533
  for (var i = 0, ii = names.length; i < ii; i++) {
2514
- if ((value = element.data(names[i])) !== undefined) return value;
2534
+ if ((value = jqLite.data(element, names[i])) !== undefined) return value;
2515
2535
  }
2516
2536
 
2517
2537
  // If dealing with a document fragment node with a host element, and no parent, use the host
2518
2538
  // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM
2519
2539
  // to lookup parent controllers.
2520
- element = jqLite(node.parentNode || (node.nodeType === 11 && node.host));
2540
+ element = element.parentNode || (element.nodeType === 11 && element.host);
2521
2541
  }
2522
2542
  }
2523
2543
 
@@ -2592,18 +2612,25 @@ function getBooleanAttrName(element, name) {
2592
2612
  return booleanAttr && BOOLEAN_ELEMENTS[element.nodeName] && booleanAttr;
2593
2613
  }
2594
2614
 
2615
+ forEach({
2616
+ data: jqLiteData,
2617
+ removeData: jqLiteRemoveData
2618
+ }, function(fn, name) {
2619
+ JQLite[name] = fn;
2620
+ });
2621
+
2595
2622
  forEach({
2596
2623
  data: jqLiteData,
2597
2624
  inheritedData: jqLiteInheritedData,
2598
2625
 
2599
2626
  scope: function(element) {
2600
2627
  // Can't use jqLiteData here directly so we stay compatible with jQuery!
2601
- return jqLite(element).data('$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']);
2628
+ return jqLite.data(element, '$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']);
2602
2629
  },
2603
2630
 
2604
2631
  isolateScope: function(element) {
2605
2632
  // Can't use jqLiteData here directly so we stay compatible with jQuery!
2606
- return jqLite(element).data('$isolateScope') || jqLite(element).data('$isolateScopeNoTemplate');
2633
+ return jqLite.data(element, '$isolateScope') || jqLite.data(element, '$isolateScopeNoTemplate');
2607
2634
  },
2608
2635
 
2609
2636
  controller: jqLiteController,
@@ -3031,7 +3058,9 @@ forEach({
3031
3058
  clone: jqLiteClone,
3032
3059
 
3033
3060
  triggerHandler: function(element, eventName, eventData) {
3034
- var eventFns = (jqLiteExpandoStore(element, 'events') || {})[eventName];
3061
+ // Copy event handlers in case event handlers array is modified during execution.
3062
+ var eventFns = (jqLiteExpandoStore(element, 'events') || {})[eventName],
3063
+ eventFnsCopy = shallowCopy(eventFns || []);
3035
3064
 
3036
3065
  eventData = eventData || [];
3037
3066
 
@@ -3040,7 +3069,7 @@ forEach({
3040
3069
  stopPropagation: noop
3041
3070
  }];
3042
3071
 
3043
- forEach(eventFns, function(fn) {
3072
+ forEach(eventFnsCopy, function(fn) {
3044
3073
  fn.apply(element, event.concat(eventData));
3045
3074
  });
3046
3075
  }
@@ -3081,16 +3110,16 @@ forEach({
3081
3110
  * @returns {string} hash string such that the same input will have the same hash string.
3082
3111
  * The resulting string key is in 'type:hashKey' format.
3083
3112
  */
3084
- function hashKey(obj) {
3113
+ function hashKey(obj, nextUidFn) {
3085
3114
  var objType = typeof obj,
3086
3115
  key;
3087
3116
 
3088
- if (objType == 'object' && obj !== null) {
3117
+ if (objType == 'function' || (objType == 'object' && obj !== null)) {
3089
3118
  if (typeof (key = obj.$$hashKey) == 'function') {
3090
3119
  // must invoke on object to keep the right this
3091
3120
  key = obj.$$hashKey();
3092
3121
  } else if (key === undefined) {
3093
- key = obj.$$hashKey = nextUid();
3122
+ key = obj.$$hashKey = (nextUidFn || nextUid)();
3094
3123
  }
3095
3124
  } else {
3096
3125
  key = obj;
@@ -3102,7 +3131,13 @@ function hashKey(obj) {
3102
3131
  /**
3103
3132
  * HashMap which can use objects as keys
3104
3133
  */
3105
- function HashMap(array){
3134
+ function HashMap(array, isolatedUid) {
3135
+ if (isolatedUid) {
3136
+ var uid = 0;
3137
+ this.nextUid = function() {
3138
+ return ++uid;
3139
+ };
3140
+ }
3106
3141
  forEach(array, this.put, this);
3107
3142
  }
3108
3143
  HashMap.prototype = {
@@ -3112,7 +3147,7 @@ HashMap.prototype = {
3112
3147
  * @param value value to store can be any type
3113
3148
  */
3114
3149
  put: function(key, value) {
3115
- this[hashKey(key)] = value;
3150
+ this[hashKey(key, this.nextUid)] = value;
3116
3151
  },
3117
3152
 
3118
3153
  /**
@@ -3120,7 +3155,7 @@ HashMap.prototype = {
3120
3155
  * @returns {Object} the value for the key
3121
3156
  */
3122
3157
  get: function(key) {
3123
- return this[hashKey(key)];
3158
+ return this[hashKey(key, this.nextUid)];
3124
3159
  },
3125
3160
 
3126
3161
  /**
@@ -3128,7 +3163,7 @@ HashMap.prototype = {
3128
3163
  * @param key
3129
3164
  */
3130
3165
  remove: function(key) {
3131
- var value = this[key = hashKey(key)];
3166
+ var value = this[key = hashKey(key, this.nextUid)];
3132
3167
  delete this[key];
3133
3168
  return value;
3134
3169
  }
@@ -3206,7 +3241,7 @@ function annotate(fn) {
3206
3241
  argDecl,
3207
3242
  last;
3208
3243
 
3209
- if (typeof fn == 'function') {
3244
+ if (typeof fn === 'function') {
3210
3245
  if (!($inject = fn.$inject)) {
3211
3246
  $inject = [];
3212
3247
  if (fn.length) {
@@ -3419,7 +3454,7 @@ function annotate(fn) {
3419
3454
 
3420
3455
 
3421
3456
  /**
3422
- * @ngdoc object
3457
+ * @ngdoc service
3423
3458
  * @name $provide
3424
3459
  *
3425
3460
  * @description
@@ -3725,7 +3760,7 @@ function createInjector(modulesToLoad) {
3725
3760
  var INSTANTIATING = {},
3726
3761
  providerSuffix = 'Provider',
3727
3762
  path = [],
3728
- loadedModules = new HashMap(),
3763
+ loadedModules = new HashMap([], true),
3729
3764
  providerCache = {
3730
3765
  $provide: {
3731
3766
  provider: supportObject(provider),
@@ -3896,8 +3931,7 @@ function createInjector(modulesToLoad) {
3896
3931
  : getService(key)
3897
3932
  );
3898
3933
  }
3899
- if (!fn.$inject) {
3900
- // this means that we must be an array.
3934
+ if (isArray(fn)) {
3901
3935
  fn = fn[length];
3902
3936
  }
3903
3937
 
@@ -5230,7 +5264,7 @@ function $TemplateCacheProvider() {
5230
5264
  * local name. Given `<widget my-attr="count = count + value">` and widget definition of
5231
5265
  * `scope: { localFn:'&myAttr' }`, then isolate scope property `localFn` will point to
5232
5266
  * a function wrapper for the `count = count + value` expression. Often it's desirable to
5233
- * pass data from the isolated scope via an expression and to the parent scope, this can be
5267
+ * pass data from the isolated scope via an expression to the parent scope, this can be
5234
5268
  * done by passing a map of local variable names and values into the expression wrapper fn.
5235
5269
  * For example, if the expression is `increment(amount)` then we can specify the amount value
5236
5270
  * by calling the `localFn` as `localFn({amount: 22})`.
@@ -5281,14 +5315,16 @@ function $TemplateCacheProvider() {
5281
5315
  *
5282
5316
  *
5283
5317
  * #### `template`
5284
- * replace the current element with the contents of the HTML. The replacement process
5285
- * migrates all of the attributes / classes from the old element to the new one. See the
5286
- * {@link guide/directive#creating-custom-directives_creating-directives_template-expanding-directive
5287
- * Directives Guide} for an example.
5318
+ * HTML markup that may:
5319
+ * * Replace the contents of the directive's element (defualt).
5320
+ * * Replace the directive's element itself (if `replace` is true - DEPRECATED).
5321
+ * * Wrap the contents of the directive's element (if `transclude` is true).
5288
5322
  *
5289
- * You can specify `template` as a string representing the template or as a function which takes
5290
- * two arguments `tElement` and `tAttrs` (described in the `compile` function api below) and
5291
- * returns a string value representing the template.
5323
+ * Value may be:
5324
+ *
5325
+ * * A string. For example `<div red-on-hover>{{delete_str}}</div>`.
5326
+ * * A function which takes two arguments `tElement` and `tAttrs` (described in the `compile`
5327
+ * function api below) and returns a string value.
5292
5328
  *
5293
5329
  *
5294
5330
  * #### `templateUrl`
@@ -5303,11 +5339,14 @@ function $TemplateCacheProvider() {
5303
5339
  *
5304
5340
  *
5305
5341
  * #### `replace` ([*DEPRECATED*!], will be removed in next major release)
5306
- * specify where the template should be inserted. Defaults to `false`.
5342
+ * specify what the template should replace. Defaults to `false`.
5307
5343
  *
5308
- * * `true` - the template will replace the current element.
5309
- * * `false` - the template will replace the contents of the current element.
5344
+ * * `true` - the template will replace the directive's element.
5345
+ * * `false` - the template will replace the contents of the directive's element.
5310
5346
  *
5347
+ * The replacement process migrates all of the attributes / classes from the old element to the new
5348
+ * one. See the {@link guide/directive#creating-custom-directives_creating-directives_template-expanding-directive
5349
+ * Directives Guide} for an example.
5311
5350
  *
5312
5351
  * #### `transclude`
5313
5352
  * compile the content of the element and make it available to the directive.
@@ -5321,6 +5360,11 @@ function $TemplateCacheProvider() {
5321
5360
  * * `true` - transclude the content of the directive.
5322
5361
  * * `'element'` - transclude the whole element including any directives defined at lower priority.
5323
5362
  *
5363
+ * <div class="alert alert-warning">
5364
+ * **Note:** When testing an element transclude directive you must not place the directive at the root of the
5365
+ * DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives
5366
+ * Testing Transclusion Directives}.
5367
+ * </div>
5324
5368
  *
5325
5369
  * #### `compile`
5326
5370
  *
@@ -5457,10 +5501,10 @@ function $TemplateCacheProvider() {
5457
5501
  * to illustrate how `$compile` works.
5458
5502
  * </div>
5459
5503
  *
5460
- <example module="compile">
5504
+ <example module="compileExample">
5461
5505
  <file name="index.html">
5462
5506
  <script>
5463
- angular.module('compile', [], function($compileProvider) {
5507
+ angular.module('compileExample', [], function($compileProvider) {
5464
5508
  // configure new 'compile' directive by passing a directive
5465
5509
  // factory function. The factory function injects the '$compile'
5466
5510
  $compileProvider.directive('compile', function($compile) {
@@ -5484,15 +5528,14 @@ function $TemplateCacheProvider() {
5484
5528
  }
5485
5529
  );
5486
5530
  };
5487
- })
5488
- });
5489
-
5490
- function Ctrl($scope) {
5531
+ });
5532
+ })
5533
+ .controller('GreeterController', ['$scope', function($scope) {
5491
5534
  $scope.name = 'Angular';
5492
5535
  $scope.html = 'Hello {{name}}';
5493
- }
5536
+ }]);
5494
5537
  </script>
5495
- <div ng-controller="Ctrl">
5538
+ <div ng-controller="GreeterController">
5496
5539
  <input ng-model="name"> <br>
5497
5540
  <textarea ng-model="html"></textarea> <br>
5498
5541
  <div compile="html"></div>
@@ -5967,7 +6010,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
5967
6010
  : null;
5968
6011
 
5969
6012
  if (nodeLinkFn && nodeLinkFn.scope) {
5970
- safeAddClass(jqLite(nodeList[i]), 'ng-scope');
6013
+ safeAddClass(attrs.$$element, 'ng-scope');
5971
6014
  }
5972
6015
 
5973
6016
  childLinkFn = (nodeLinkFn && nodeLinkFn.terminal ||
@@ -5989,7 +6032,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
5989
6032
  return linkFnFound ? compositeLinkFn : null;
5990
6033
 
5991
6034
  function compositeLinkFn(scope, nodeList, $rootElement, parentBoundTranscludeFn) {
5992
- var nodeLinkFn, childLinkFn, node, $node, childScope, i, ii, n, childBoundTranscludeFn;
6035
+ var nodeLinkFn, childLinkFn, node, childScope, i, ii, n, childBoundTranscludeFn;
5993
6036
 
5994
6037
  // copy nodeList so that linking doesn't break due to live list updates.
5995
6038
  var nodeListLength = nodeList.length,
@@ -6002,12 +6045,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
6002
6045
  node = stableNodeList[n];
6003
6046
  nodeLinkFn = linkFns[i++];
6004
6047
  childLinkFn = linkFns[i++];
6005
- $node = jqLite(node);
6006
6048
 
6007
6049
  if (nodeLinkFn) {
6008
6050
  if (nodeLinkFn.scope) {
6009
6051
  childScope = scope.$new();
6010
- $node.data('$scope', childScope);
6052
+ jqLite.data(node, '$scope', childScope);
6011
6053
  } else {
6012
6054
  childScope = scope;
6013
6055
  }
@@ -6078,7 +6120,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
6078
6120
  directiveNormalize(nodeName_(node).toLowerCase()), 'E', maxPriority, ignoreDirective);
6079
6121
 
6080
6122
  // iterate over the attributes
6081
- for (var attr, name, nName, ngAttrName, value, nAttrs = node.attributes,
6123
+ for (var attr, name, nName, ngAttrName, value, isNgAttr, nAttrs = node.attributes,
6082
6124
  j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
6083
6125
  var attrStartName = false;
6084
6126
  var attrEndName = false;
@@ -6086,9 +6128,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
6086
6128
  attr = nAttrs[j];
6087
6129
  if (!msie || msie >= 8 || attr.specified) {
6088
6130
  name = attr.name;
6131
+ value = trim(attr.value);
6132
+
6089
6133
  // support ngAttr attribute binding
6090
6134
  ngAttrName = directiveNormalize(name);
6091
- if (NG_ATTR_BINDING.test(ngAttrName)) {
6135
+ if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) {
6092
6136
  name = snake_case(ngAttrName.substr(6), '-');
6093
6137
  }
6094
6138
 
@@ -6101,9 +6145,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
6101
6145
 
6102
6146
  nName = directiveNormalize(name.toLowerCase());
6103
6147
  attrsMap[nName] = name;
6104
- attrs[nName] = value = trim(attr.value);
6105
- if (getBooleanAttrName(node, nName)) {
6106
- attrs[nName] = true; // presence means true
6148
+ if (isNgAttr || !attrs.hasOwnProperty(nName)) {
6149
+ attrs[nName] = value;
6150
+ if (getBooleanAttrName(node, nName)) {
6151
+ attrs[nName] = true; // presence means true
6152
+ }
6107
6153
  }
6108
6154
  addAttrInterpolateDirective(node, directives, value, nName);
6109
6155
  addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName,
@@ -6295,12 +6341,12 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
6295
6341
  if (directiveValue == 'element') {
6296
6342
  hasElementTranscludeDirective = true;
6297
6343
  terminalPriority = directive.priority;
6298
- $template = groupScan(compileNode, attrStart, attrEnd);
6344
+ $template = $compileNode;
6299
6345
  $compileNode = templateAttrs.$$element =
6300
6346
  jqLite(document.createComment(' ' + directiveName + ': ' +
6301
6347
  templateAttrs[directiveName] + ' '));
6302
6348
  compileNode = $compileNode[0];
6303
- replaceWith(jqCollection, jqLite(sliceArgs($template)), compileNode);
6349
+ replaceWith(jqCollection, sliceArgs($template), compileNode);
6304
6350
 
6305
6351
  childTranscludeFn = compile($template, transcludeFn, terminalPriority,
6306
6352
  replaceDirective && replaceDirective.name, {
@@ -6477,29 +6523,26 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
6477
6523
  function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
6478
6524
  var attrs, $element, i, ii, linkFn, controller, isolateScope, elementControllers = {}, transcludeFn;
6479
6525
 
6480
- if (compileNode === linkNode) {
6481
- attrs = templateAttrs;
6482
- } else {
6483
- attrs = shallowCopy(templateAttrs, new Attributes(jqLite(linkNode), templateAttrs.$attr));
6484
- }
6526
+ attrs = (compileNode === linkNode)
6527
+ ? templateAttrs
6528
+ : shallowCopy(templateAttrs, new Attributes(jqLite(linkNode), templateAttrs.$attr));
6485
6529
  $element = attrs.$$element;
6486
6530
 
6487
6531
  if (newIsolateScopeDirective) {
6488
6532
  var LOCAL_REGEXP = /^\s*([@=&])(\??)\s*(\w*)\s*$/;
6489
- var $linkNode = jqLite(linkNode);
6490
6533
 
6491
6534
  isolateScope = scope.$new(true);
6492
6535
 
6493
6536
  if (templateDirective && (templateDirective === newIsolateScopeDirective ||
6494
6537
  templateDirective === newIsolateScopeDirective.$$originalDirective)) {
6495
- $linkNode.data('$isolateScope', isolateScope) ;
6538
+ $element.data('$isolateScope', isolateScope);
6496
6539
  } else {
6497
- $linkNode.data('$isolateScopeNoTemplate', isolateScope);
6540
+ $element.data('$isolateScopeNoTemplate', isolateScope);
6498
6541
  }
6499
6542
 
6500
6543
 
6501
6544
 
6502
- safeAddClass($linkNode, 'ng-isolate-scope');
6545
+ safeAddClass($element, 'ng-isolate-scope');
6503
6546
 
6504
6547
  forEach(newIsolateScopeDirective.scope, function(definition, scopeName) {
6505
6548
  var match = definition.match(LOCAL_REGEXP) || [],
@@ -7201,7 +7244,7 @@ function $ControllerProvider() {
7201
7244
  instance = $injector.instantiate(expression, locals);
7202
7245
 
7203
7246
  if (identifier) {
7204
- if (!(locals && typeof locals.$scope == 'object')) {
7247
+ if (!(locals && typeof locals.$scope === 'object')) {
7205
7248
  throw minErr('$controller')('noscp',
7206
7249
  "Cannot export controller '{0}' as '{1}'! No $scope object provided via `locals`.",
7207
7250
  constructor || expression.name, identifier);
@@ -7224,18 +7267,19 @@ function $ControllerProvider() {
7224
7267
  * A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object.
7225
7268
  *
7226
7269
  * @example
7227
- <example>
7270
+ <example module="documentExample">
7228
7271
  <file name="index.html">
7229
- <div ng-controller="MainCtrl">
7272
+ <div ng-controller="ExampleController">
7230
7273
  <p>$document title: <b ng-bind="title"></b></p>
7231
7274
  <p>window.document title: <b ng-bind="windowTitle"></b></p>
7232
7275
  </div>
7233
7276
  </file>
7234
7277
  <file name="script.js">
7235
- function MainCtrl($scope, $document) {
7236
- $scope.title = $document[0].title;
7237
- $scope.windowTitle = angular.element(window.document)[0].title;
7238
- }
7278
+ angular.module('documentExample', [])
7279
+ .controller('ExampleController', ['$scope', '$document', function($scope, $document) {
7280
+ $scope.title = $document[0].title;
7281
+ $scope.windowTitle = angular.element(window.document)[0].title;
7282
+ }]);
7239
7283
  </file>
7240
7284
  </example>
7241
7285
  */
@@ -7302,11 +7346,7 @@ function parseHeaders(headers) {
7302
7346
  val = trim(line.substr(i + 1));
7303
7347
 
7304
7348
  if (key) {
7305
- if (parsed[key]) {
7306
- parsed[key] += ', ' + val;
7307
- } else {
7308
- parsed[key] = val;
7309
- }
7349
+ parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;
7310
7350
  }
7311
7351
  });
7312
7352
 
@@ -7368,12 +7408,39 @@ function isSuccess(status) {
7368
7408
  }
7369
7409
 
7370
7410
 
7411
+ /**
7412
+ * @ngdoc provider
7413
+ * @name $httpProvider
7414
+ * @description
7415
+ * Use `$httpProvider` to change the default behavior of the {@link ng.$http $http} service.
7416
+ * */
7371
7417
  function $HttpProvider() {
7372
7418
  var JSON_START = /^\s*(\[|\{[^\{])/,
7373
7419
  JSON_END = /[\}\]]\s*$/,
7374
7420
  PROTECTION_PREFIX = /^\)\]\}',?\n/,
7375
7421
  CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': 'application/json;charset=utf-8'};
7376
7422
 
7423
+ /**
7424
+ * @ngdoc property
7425
+ * @name $httpProvider#defaults
7426
+ * @description
7427
+ *
7428
+ * Object containing default values for all {@link ng.$http $http} requests.
7429
+ *
7430
+ * - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token.
7431
+ * Defaults value is `'XSRF-TOKEN'`.
7432
+ *
7433
+ * - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the
7434
+ * XSRF token. Defaults value is `'X-XSRF-TOKEN'`.
7435
+ *
7436
+ * - **`defaults.headers`** - {Object} - Default headers for all $http requests.
7437
+ * Refer to {@link ng.$http#setting-http-headers $http} for documentation on
7438
+ * setting default headers.
7439
+ * - **`defaults.headers.common`**
7440
+ * - **`defaults.headers.post`**
7441
+ * - **`defaults.headers.put`**
7442
+ * - **`defaults.headers.patch`**
7443
+ **/
7377
7444
  var defaults = this.defaults = {
7378
7445
  // transform incoming response data
7379
7446
  transformResponse: [function(data) {
@@ -7863,9 +7930,9 @@ function $HttpProvider() {
7863
7930
  *
7864
7931
  *
7865
7932
  * @example
7866
- <example>
7933
+ <example module="httpExample">
7867
7934
  <file name="index.html">
7868
- <div ng-controller="FetchCtrl">
7935
+ <div ng-controller="FetchController">
7869
7936
  <select ng-model="method">
7870
7937
  <option>GET</option>
7871
7938
  <option>JSONP</option>
@@ -7887,30 +7954,32 @@ function $HttpProvider() {
7887
7954
  </div>
7888
7955
  </file>
7889
7956
  <file name="script.js">
7890
- function FetchCtrl($scope, $http, $templateCache) {
7891
- $scope.method = 'GET';
7892
- $scope.url = 'http-hello.html';
7893
-
7894
- $scope.fetch = function() {
7895
- $scope.code = null;
7896
- $scope.response = null;
7897
-
7898
- $http({method: $scope.method, url: $scope.url, cache: $templateCache}).
7899
- success(function(data, status) {
7900
- $scope.status = status;
7901
- $scope.data = data;
7902
- }).
7903
- error(function(data, status) {
7904
- $scope.data = data || "Request failed";
7905
- $scope.status = status;
7906
- });
7907
- };
7957
+ angular.module('httpExample', [])
7958
+ .controller('FetchController', ['$scope', '$http', '$templateCache',
7959
+ function($scope, $http, $templateCache) {
7960
+ $scope.method = 'GET';
7961
+ $scope.url = 'http-hello.html';
7962
+
7963
+ $scope.fetch = function() {
7964
+ $scope.code = null;
7965
+ $scope.response = null;
7966
+
7967
+ $http({method: $scope.method, url: $scope.url, cache: $templateCache}).
7968
+ success(function(data, status) {
7969
+ $scope.status = status;
7970
+ $scope.data = data;
7971
+ }).
7972
+ error(function(data, status) {
7973
+ $scope.data = data || "Request failed";
7974
+ $scope.status = status;
7975
+ });
7976
+ };
7908
7977
 
7909
- $scope.updateModel = function(method, url) {
7910
- $scope.method = method;
7911
- $scope.url = url;
7912
- };
7913
- }
7978
+ $scope.updateModel = function(method, url) {
7979
+ $scope.method = method;
7980
+ $scope.url = url;
7981
+ };
7982
+ }]);
7914
7983
  </file>
7915
7984
  <file name="http-hello.html">
7916
7985
  Hello, $http!
@@ -7964,7 +8033,7 @@ function $HttpProvider() {
7964
8033
  var reqData = transformData(config.data, headersGetter(headers), config.transformRequest);
7965
8034
 
7966
8035
  // strip content-type if data is undefined
7967
- if (isUndefined(config.data)) {
8036
+ if (isUndefined(reqData)) {
7968
8037
  forEach(headers, function(value, header) {
7969
8038
  if (lowercase(header) === 'content-type') {
7970
8039
  delete headers[header];
@@ -8033,10 +8102,6 @@ function $HttpProvider() {
8033
8102
 
8034
8103
  defHeaders = extend({}, defHeaders.common, defHeaders[lowercase(config.method)]);
8035
8104
 
8036
- // execute if header value is function
8037
- execHeaders(defHeaders);
8038
- execHeaders(reqHeaders);
8039
-
8040
8105
  // using for-in instead of forEach to avoid unecessary iteration after header has been found
8041
8106
  defaultHeadersIteration:
8042
8107
  for (defHeaderName in defHeaders) {
@@ -8051,6 +8116,8 @@ function $HttpProvider() {
8051
8116
  reqHeaders[defHeaderName] = defHeaders[defHeaderName];
8052
8117
  }
8053
8118
 
8119
+ // execute if header value is a function for merged headers
8120
+ execHeaders(reqHeaders);
8054
8121
  return reqHeaders;
8055
8122
 
8056
8123
  function execHeaders(headers) {
@@ -8116,7 +8183,7 @@ function $HttpProvider() {
8116
8183
  * Shortcut method to perform `JSONP` request.
8117
8184
  *
8118
8185
  * @param {string} url Relative or absolute URL specifying the destination of the request.
8119
- * Should contain `JSON_CALLBACK` string.
8186
+ * The name of the callback should be the string `JSON_CALLBACK`.
8120
8187
  * @param {Object=} config Optional configuration object
8121
8188
  * @returns {HttpPromise} Future object
8122
8189
  */
@@ -8216,7 +8283,7 @@ function $HttpProvider() {
8216
8283
  if (cache) {
8217
8284
  cachedResp = cache.get(url);
8218
8285
  if (isDefined(cachedResp)) {
8219
- if (cachedResp.then) {
8286
+ if (isPromiseLike(cachedResp)) {
8220
8287
  // cached request has already been sent, but there is no response yet
8221
8288
  cachedResp.then(removePendingReq, removePendingReq);
8222
8289
  return cachedResp;
@@ -8298,27 +8365,29 @@ function $HttpProvider() {
8298
8365
 
8299
8366
 
8300
8367
  function buildUrl(url, params) {
8301
- if (!params) return url;
8302
- var parts = [];
8303
- forEachSorted(params, function(value, key) {
8304
- if (value === null || isUndefined(value)) return;
8305
- if (!isArray(value)) value = [value];
8306
-
8307
- forEach(value, function(v) {
8308
- if (isObject(v)) {
8309
- v = toJson(v);
8310
- }
8311
- parts.push(encodeUriQuery(key) + '=' +
8312
- encodeUriQuery(v));
8313
- });
8314
- });
8315
- if(parts.length > 0) {
8316
- url += ((url.indexOf('?') == -1) ? '?' : '&') + parts.join('&');
8368
+ if (!params) return url;
8369
+ var parts = [];
8370
+ forEachSorted(params, function(value, key) {
8371
+ if (value === null || isUndefined(value)) return;
8372
+ if (!isArray(value)) value = [value];
8373
+
8374
+ forEach(value, function(v) {
8375
+ if (isObject(v)) {
8376
+ if (isDate(v)){
8377
+ v = v.toISOString();
8378
+ } else if (isObject(v)) {
8379
+ v = toJson(v);
8380
+ }
8317
8381
  }
8318
- return url;
8319
- }
8320
-
8321
-
8382
+ parts.push(encodeUriQuery(key) + '=' +
8383
+ encodeUriQuery(v));
8384
+ });
8385
+ });
8386
+ if(parts.length > 0) {
8387
+ url += ((url.indexOf('?') == -1) ? '?' : '&') + parts.join('&');
8388
+ }
8389
+ return url;
8390
+ }
8322
8391
  }];
8323
8392
  }
8324
8393
 
@@ -8403,7 +8472,8 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
8403
8472
  // Safari respectively.
8404
8473
  if (xhr && xhr.readyState == 4) {
8405
8474
  var responseHeaders = null,
8406
- response = null;
8475
+ response = null,
8476
+ statusText = '';
8407
8477
 
8408
8478
  if(status !== ABORTED) {
8409
8479
  responseHeaders = xhr.getAllResponseHeaders();
@@ -8413,11 +8483,17 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
8413
8483
  response = ('response' in xhr) ? xhr.response : xhr.responseText;
8414
8484
  }
8415
8485
 
8486
+ // Accessing statusText on an aborted xhr object will
8487
+ // throw an 'c00c023f error' in IE9 and lower, don't touch it.
8488
+ if (!(status === ABORTED && msie < 10)) {
8489
+ statusText = xhr.statusText;
8490
+ }
8491
+
8416
8492
  completeRequest(callback,
8417
8493
  status || xhr.status,
8418
8494
  response,
8419
8495
  responseHeaders,
8420
- xhr.statusText || '');
8496
+ statusText);
8421
8497
  }
8422
8498
  };
8423
8499
 
@@ -8447,7 +8523,7 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
8447
8523
 
8448
8524
  if (timeout > 0) {
8449
8525
  var timeoutId = $browserDefer(timeoutRequest, timeout);
8450
- } else if (timeout && timeout.then) {
8526
+ } else if (isPromiseLike(timeout)) {
8451
8527
  timeout.then(timeoutRequest);
8452
8528
  }
8453
8529
 
@@ -8819,25 +8895,27 @@ function $IntervalProvider() {
8819
8895
  * @returns {promise} A promise which will be notified on each iteration.
8820
8896
  *
8821
8897
  * @example
8822
- * <example module="time">
8823
- * <file name="index.html">
8824
- * <script>
8825
- * function Ctrl2($scope,$interval) {
8826
- * $scope.format = 'M/d/yy h:mm:ss a';
8827
- * $scope.blood_1 = 100;
8828
- * $scope.blood_2 = 120;
8898
+ * <example module="intervalExample">
8899
+ * <file name="index.html">
8900
+ * <script>
8901
+ * angular.module('intervalExample', [])
8902
+ * .controller('ExampleController', ['$scope', '$interval',
8903
+ * function($scope, $interval) {
8904
+ * $scope.format = 'M/d/yy h:mm:ss a';
8905
+ * $scope.blood_1 = 100;
8906
+ * $scope.blood_2 = 120;
8829
8907
  *
8830
- * var stop;
8831
- * $scope.fight = function() {
8832
- * // Don't start a new fight if we are already fighting
8833
- * if ( angular.isDefined(stop) ) return;
8908
+ * var stop;
8909
+ * $scope.fight = function() {
8910
+ * // Don't start a new fight if we are already fighting
8911
+ * if ( angular.isDefined(stop) ) return;
8834
8912
  *
8835
8913
  * stop = $interval(function() {
8836
8914
  * if ($scope.blood_1 > 0 && $scope.blood_2 > 0) {
8837
- * $scope.blood_1 = $scope.blood_1 - 3;
8838
- * $scope.blood_2 = $scope.blood_2 - 4;
8915
+ * $scope.blood_1 = $scope.blood_1 - 3;
8916
+ * $scope.blood_2 = $scope.blood_2 - 4;
8839
8917
  * } else {
8840
- * $scope.stopFight();
8918
+ * $scope.stopFight();
8841
8919
  * }
8842
8920
  * }, 100);
8843
8921
  * };
@@ -8852,22 +8930,21 @@ function $IntervalProvider() {
8852
8930
  * $scope.resetFight = function() {
8853
8931
  * $scope.blood_1 = 100;
8854
8932
  * $scope.blood_2 = 120;
8855
- * }
8933
+ * };
8856
8934
  *
8857
8935
  * $scope.$on('$destroy', function() {
8858
- * // Make sure that the interval is destroyed too
8936
+ * // Make sure that the interval nis destroyed too
8859
8937
  * $scope.stopFight();
8860
8938
  * });
8861
- * }
8862
- *
8863
- * angular.module('time', [])
8864
- * // Register the 'myCurrentTime' directive factory method.
8865
- * // We inject $interval and dateFilter service since the factory method is DI.
8866
- * .directive('myCurrentTime', function($interval, dateFilter) {
8939
+ * }])
8940
+ * // Register the 'myCurrentTime' directive factory method.
8941
+ * // We inject $interval and dateFilter service since the factory method is DI.
8942
+ * .directive('myCurrentTime', ['$interval', 'dateFilter',
8943
+ * function($interval, dateFilter) {
8867
8944
  * // return the directive link function. (compile function not needed)
8868
8945
  * return function(scope, element, attrs) {
8869
8946
  * var format, // date format
8870
- * stopTime; // so that we can cancel the time updates
8947
+ * stopTime; // so that we can cancel the time updates
8871
8948
  *
8872
8949
  * // used to update the UI
8873
8950
  * function updateTime() {
@@ -8883,28 +8960,28 @@ function $IntervalProvider() {
8883
8960
  * stopTime = $interval(updateTime, 1000);
8884
8961
  *
8885
8962
  * // listen on DOM destroy (removal) event, and cancel the next UI update
8886
- * // to prevent updating time ofter the DOM element was removed.
8963
+ * // to prevent updating time after the DOM element was removed.
8887
8964
  * element.bind('$destroy', function() {
8888
8965
  * $interval.cancel(stopTime);
8889
8966
  * });
8890
8967
  * }
8891
- * });
8892
- * </script>
8968
+ * }]);
8969
+ * </script>
8893
8970
  *
8894
- * <div>
8895
- * <div ng-controller="Ctrl2">
8896
- * Date format: <input ng-model="format"> <hr/>
8897
- * Current time is: <span my-current-time="format"></span>
8898
- * <hr/>
8899
- * Blood 1 : <font color='red'>{{blood_1}}</font>
8900
- * Blood 2 : <font color='red'>{{blood_2}}</font>
8901
- * <button type="button" data-ng-click="fight()">Fight</button>
8902
- * <button type="button" data-ng-click="stopFight()">StopFight</button>
8903
- * <button type="button" data-ng-click="resetFight()">resetFight</button>
8904
- * </div>
8971
+ * <div>
8972
+ * <div ng-controller="ExampleController">
8973
+ * Date format: <input ng-model="format"> <hr/>
8974
+ * Current time is: <span my-current-time="format"></span>
8975
+ * <hr/>
8976
+ * Blood 1 : <font color='red'>{{blood_1}}</font>
8977
+ * Blood 2 : <font color='red'>{{blood_2}}</font>
8978
+ * <button type="button" data-ng-click="fight()">Fight</button>
8979
+ * <button type="button" data-ng-click="stopFight()">StopFight</button>
8980
+ * <button type="button" data-ng-click="resetFight()">resetFight</button>
8905
8981
  * </div>
8982
+ * </div>
8906
8983
  *
8907
- * </file>
8984
+ * </file>
8908
8985
  * </example>
8909
8986
  */
8910
8987
  function interval(fn, delay, count, invokeApply) {
@@ -8951,7 +9028,7 @@ function $IntervalProvider() {
8951
9028
  interval.cancel = function(promise) {
8952
9029
  if (promise && promise.$$intervalId in intervals) {
8953
9030
  intervals[promise.$$intervalId].reject('canceled');
8954
- clearInterval(promise.$$intervalId);
9031
+ $window.clearInterval(promise.$$intervalId);
8955
9032
  delete intervals[promise.$$intervalId];
8956
9033
  return true;
8957
9034
  }
@@ -9461,14 +9538,17 @@ LocationHashbangInHtml5Url.prototype =
9461
9538
  * If the argument is a hash object containing an array of values, these values will be encoded
9462
9539
  * as duplicate search parameters in the url.
9463
9540
  *
9464
- * @param {(string|Array<string>)=} paramValue If `search` is a string, then `paramValue` will
9465
- * override only a single search property.
9541
+ * @param {(string|Array<string>|boolean)=} paramValue If `search` is a string, then `paramValue`
9542
+ * will override only a single search property.
9466
9543
  *
9467
9544
  * If `paramValue` is an array, it will override the property of the `search` component of
9468
9545
  * `$location` specified via the first argument.
9469
9546
  *
9470
9547
  * If `paramValue` is `null`, the property specified via the first argument will be deleted.
9471
9548
  *
9549
+ * If `paramValue` is `true`, the property specified via the first argument will be added with no
9550
+ * value nor trailing equal sign.
9551
+ *
9472
9552
  * @return {Object} If called with no arguments returns the parsed `search` object. If called with
9473
9553
  * one or more arguments returns `$location` object itself.
9474
9554
  */
@@ -9480,6 +9560,11 @@ LocationHashbangInHtml5Url.prototype =
9480
9560
  if (isString(search)) {
9481
9561
  this.$$search = parseKeyValue(search);
9482
9562
  } else if (isObject(search)) {
9563
+ // remove object undefined or null properties
9564
+ forEach(search, function(value, key) {
9565
+ if (value == null) delete search[key];
9566
+ });
9567
+
9483
9568
  this.$$search = search;
9484
9569
  } else {
9485
9570
  throw $locationMinErr('isrcharg',
@@ -9585,7 +9670,7 @@ function $LocationProvider(){
9585
9670
  html5Mode = false;
9586
9671
 
9587
9672
  /**
9588
- * @ngdoc property
9673
+ * @ngdoc method
9589
9674
  * @name $locationProvider#hashPrefix
9590
9675
  * @description
9591
9676
  * @param {string=} prefix Prefix for hash part (containing path and search)
@@ -9601,7 +9686,7 @@ function $LocationProvider(){
9601
9686
  };
9602
9687
 
9603
9688
  /**
9604
- * @ngdoc property
9689
+ * @ngdoc method
9605
9690
  * @name $locationProvider#html5Mode
9606
9691
  * @description
9607
9692
  * @param {boolean=} mode Use HTML5 strategy if available.
@@ -9801,15 +9886,16 @@ function $LocationProvider(){
9801
9886
  * {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this.
9802
9887
  *
9803
9888
  * @example
9804
- <example>
9889
+ <example module="logExample">
9805
9890
  <file name="script.js">
9806
- function LogCtrl($scope, $log) {
9807
- $scope.$log = $log;
9808
- $scope.message = 'Hello World!';
9809
- }
9891
+ angular.module('logExample', [])
9892
+ .controller('LogController', ['$scope', '$log', function($scope, $log) {
9893
+ $scope.$log = $log;
9894
+ $scope.message = 'Hello World!';
9895
+ }]);
9810
9896
  </file>
9811
9897
  <file name="index.html">
9812
- <div ng-controller="LogCtrl">
9898
+ <div ng-controller="LogController">
9813
9899
  <p>Reload this page with open console, enter text and hit the log button...</p>
9814
9900
  Message:
9815
9901
  <input type="text" ng-model="message"/>
@@ -9833,7 +9919,7 @@ function $LogProvider(){
9833
9919
  self = this;
9834
9920
 
9835
9921
  /**
9836
- * @ngdoc property
9922
+ * @ngdoc method
9837
9923
  * @name $logProvider#debugEnabled
9838
9924
  * @description
9839
9925
  * @param {boolean=} flag enable or disable debug level messages
@@ -9959,14 +10045,7 @@ var promiseWarning;
9959
10045
  //
9960
10046
  // As an example, consider the following Angular expression:
9961
10047
  //
9962
- // {}.toString.constructor(alert("evil JS code"))
9963
- //
9964
- // We want to prevent this type of access. For the sake of performance, during the lexing phase we
9965
- // disallow any "dotted" access to any member named "constructor".
9966
- //
9967
- // For reflective calls (a[b]) we check that the value of the lookup is not the Function constructor
9968
- // while evaluating the expression, which is a stronger but more expensive test. Since reflective
9969
- // calls are expensive anyway, this is not such a big deal compared to static dereferencing.
10048
+ // {}.toString.constructor('alert("evil JS code")')
9970
10049
  //
9971
10050
  // This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits
9972
10051
  // against the expression language, but not to prevent exploits that were enabled by exposing
@@ -9974,17 +10053,19 @@ var promiseWarning;
9974
10053
  // practice and therefore we are not even trying to protect against interaction with an object
9975
10054
  // explicitly exposed in this way.
9976
10055
  //
9977
- // A developer could foil the name check by aliasing the Function constructor under a different
9978
- // name on the scope.
9979
- //
9980
10056
  // In general, it is not possible to access a Window object from an angular expression unless a
9981
10057
  // window or some DOM object that has a reference to window is published onto a Scope.
10058
+ // Similarly we prevent invocations of function known to be dangerous, as well as assignments to
10059
+ // native objects.
10060
+
9982
10061
 
9983
10062
  function ensureSafeMemberName(name, fullExpression) {
9984
- if (name === "constructor") {
10063
+ if (name === "__defineGetter__" || name === "__defineSetter__"
10064
+ || name === "__lookupGetter__" || name === "__lookupSetter__"
10065
+ || name === "__proto__") {
9985
10066
  throw $parseMinErr('isecfld',
9986
- 'Referencing "constructor" field in Angular expressions is disallowed! Expression: {0}',
9987
- fullExpression);
10067
+ 'Attempting to access a disallowed field in Angular expressions! '
10068
+ +'Expression: {0}', fullExpression);
9988
10069
  }
9989
10070
  return name;
9990
10071
  }
@@ -10006,11 +10087,34 @@ function ensureSafeObject(obj, fullExpression) {
10006
10087
  throw $parseMinErr('isecdom',
10007
10088
  'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}',
10008
10089
  fullExpression);
10090
+ } else if (// block Object so that we can't get hold of dangerous Object.* methods
10091
+ obj === Object) {
10092
+ throw $parseMinErr('isecobj',
10093
+ 'Referencing Object in Angular expressions is disallowed! Expression: {0}',
10094
+ fullExpression);
10009
10095
  }
10010
10096
  }
10011
10097
  return obj;
10012
10098
  }
10013
10099
 
10100
+ var CALL = Function.prototype.call;
10101
+ var APPLY = Function.prototype.apply;
10102
+ var BIND = Function.prototype.bind;
10103
+
10104
+ function ensureSafeFunction(obj, fullExpression) {
10105
+ if (obj) {
10106
+ if (obj.constructor === obj) {
10107
+ throw $parseMinErr('isecfn',
10108
+ 'Referencing Function in Angular expressions is disallowed! Expression: {0}',
10109
+ fullExpression);
10110
+ } else if (obj === CALL || obj === APPLY || (BIND && obj === BIND)) {
10111
+ throw $parseMinErr('isecff',
10112
+ 'Referencing call, apply or bind in Angular expressions is disallowed! Expression: {0}',
10113
+ fullExpression);
10114
+ }
10115
+ }
10116
+ }
10117
+
10014
10118
  var OPERATORS = {
10015
10119
  /* jshint bitwise : false */
10016
10120
  'null':function(){return null;},
@@ -10290,11 +10394,7 @@ Lexer.prototype = {
10290
10394
  string += String.fromCharCode(parseInt(hex, 16));
10291
10395
  } else {
10292
10396
  var rep = ESCAPE[ch];
10293
- if (rep) {
10294
- string += rep;
10295
- } else {
10296
- string += ch;
10297
- }
10397
+ string = string + (rep || ch);
10298
10398
  }
10299
10399
  escape = false;
10300
10400
  } else if (ch === '\\') {
@@ -10645,6 +10745,7 @@ Parser.prototype = {
10645
10745
  i = indexFn(self, locals),
10646
10746
  v, p;
10647
10747
 
10748
+ ensureSafeMemberName(i, parser.text);
10648
10749
  if (!o) return undefined;
10649
10750
  v = ensureSafeObject(o[i], parser.text);
10650
10751
  if (v && v.then && parser.options.unwrapPromises) {
@@ -10687,7 +10788,7 @@ Parser.prototype = {
10687
10788
  var fnPtr = fn(scope, locals, context) || noop;
10688
10789
 
10689
10790
  ensureSafeObject(context, parser.text);
10690
- ensureSafeObject(fnPtr, parser.text);
10791
+ ensureSafeFunction(fnPtr, parser.text);
10691
10792
 
10692
10793
  // IE stupidity! (IE doesn't have apply for some native functions)
10693
10794
  var v = fnPtr.apply
@@ -10796,6 +10897,8 @@ function setter(obj, path, setValue, fullExp, options) {
10796
10897
  }
10797
10898
  }
10798
10899
  key = ensureSafeMemberName(element.shift(), fullExp);
10900
+ ensureSafeObject(obj, fullExp);
10901
+ ensureSafeObject(obj[key], fullExp);
10799
10902
  obj[key] = setValue;
10800
10903
  return setValue;
10801
10904
  }
@@ -10911,26 +11014,6 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, options) {
10911
11014
  };
10912
11015
  }
10913
11016
 
10914
- function simpleGetterFn1(key0, fullExp) {
10915
- ensureSafeMemberName(key0, fullExp);
10916
-
10917
- return function simpleGetterFn1(scope, locals) {
10918
- if (scope == null) return undefined;
10919
- return ((locals && locals.hasOwnProperty(key0)) ? locals : scope)[key0];
10920
- };
10921
- }
10922
-
10923
- function simpleGetterFn2(key0, key1, fullExp) {
10924
- ensureSafeMemberName(key0, fullExp);
10925
- ensureSafeMemberName(key1, fullExp);
10926
-
10927
- return function simpleGetterFn2(scope, locals) {
10928
- if (scope == null) return undefined;
10929
- scope = ((locals && locals.hasOwnProperty(key0)) ? locals : scope)[key0];
10930
- return scope == null ? undefined : scope[key1];
10931
- };
10932
- }
10933
-
10934
11017
  function getterFn(path, options, fullExp) {
10935
11018
  // Check whether the cache has this getter already.
10936
11019
  // We can use hasOwnProperty directly on the cache because we ensure,
@@ -10943,13 +11026,8 @@ function getterFn(path, options, fullExp) {
10943
11026
  pathKeysLength = pathKeys.length,
10944
11027
  fn;
10945
11028
 
10946
- // When we have only 1 or 2 tokens, use optimized special case closures.
10947
11029
  // http://jsperf.com/angularjs-parse-getter/6
10948
- if (!options.unwrapPromises && pathKeysLength === 1) {
10949
- fn = simpleGetterFn1(pathKeys[0], fullExp);
10950
- } else if (!options.unwrapPromises && pathKeysLength === 2) {
10951
- fn = simpleGetterFn2(pathKeys[0], pathKeys[1], fullExp);
10952
- } else if (options.csp) {
11030
+ if (options.csp) {
10953
11031
  if (pathKeysLength < 6) {
10954
11032
  fn = cspSafeGetterFn(pathKeys[0], pathKeys[1], pathKeys[2], pathKeys[3], pathKeys[4], fullExp,
10955
11033
  options);
@@ -11215,17 +11293,13 @@ function $ParseProvider() {
11215
11293
  * var deferred = $q.defer();
11216
11294
  *
11217
11295
  * setTimeout(function() {
11218
- * // since this fn executes async in a future turn of the event loop, we need to wrap
11219
- * // our code into an $apply call so that the model changes are properly observed.
11220
- * scope.$apply(function() {
11221
- * deferred.notify('About to greet ' + name + '.');
11222
- *
11223
- * if (okToGreet(name)) {
11224
- * deferred.resolve('Hello, ' + name + '!');
11225
- * } else {
11226
- * deferred.reject('Greeting ' + name + ' is not allowed.');
11227
- * }
11228
- * });
11296
+ * deferred.notify('About to greet ' + name + '.');
11297
+ *
11298
+ * if (okToGreet(name)) {
11299
+ * deferred.resolve('Hello, ' + name + '!');
11300
+ * } else {
11301
+ * deferred.reject('Greeting ' + name + ' is not allowed.');
11302
+ * }
11229
11303
  * }, 1000);
11230
11304
  *
11231
11305
  * return deferred.promise;
@@ -11499,7 +11573,7 @@ function qFactory(nextTick, exceptionHandler) {
11499
11573
  } catch(e) {
11500
11574
  return makePromise(e, false);
11501
11575
  }
11502
- if (callbackOutput && isFunction(callbackOutput.then)) {
11576
+ if (isPromiseLike(callbackOutput)) {
11503
11577
  return callbackOutput.then(function() {
11504
11578
  return makePromise(value, isResolved);
11505
11579
  }, function(error) {
@@ -11524,7 +11598,7 @@ function qFactory(nextTick, exceptionHandler) {
11524
11598
 
11525
11599
 
11526
11600
  var ref = function(value) {
11527
- if (value && isFunction(value.then)) return value;
11601
+ if (isPromiseLike(value)) return value;
11528
11602
  return {
11529
11603
  then: function(callback) {
11530
11604
  var result = defer();
@@ -12191,7 +12265,7 @@ function $RootScopeProvider(){
12191
12265
 
12192
12266
  function $watchCollectionWatch() {
12193
12267
  newValue = objGetter(self);
12194
- var newLength, key;
12268
+ var newLength, key, bothNaN;
12195
12269
 
12196
12270
  if (!isObject(newValue)) { // if primitive
12197
12271
  if (oldValue !== newValue) {
@@ -12215,7 +12289,7 @@ function $RootScopeProvider(){
12215
12289
  }
12216
12290
  // copy the items to oldValue and look for changes.
12217
12291
  for (var i = 0; i < newLength; i++) {
12218
- var bothNaN = (oldValue[i] !== oldValue[i]) &&
12292
+ bothNaN = (oldValue[i] !== oldValue[i]) &&
12219
12293
  (newValue[i] !== newValue[i]);
12220
12294
  if (!bothNaN && (oldValue[i] !== newValue[i])) {
12221
12295
  changeDetected++;
@@ -12235,7 +12309,9 @@ function $RootScopeProvider(){
12235
12309
  if (newValue.hasOwnProperty(key)) {
12236
12310
  newLength++;
12237
12311
  if (oldValue.hasOwnProperty(key)) {
12238
- if (oldValue[key] !== newValue[key]) {
12312
+ bothNaN = (oldValue[key] !== oldValue[key]) &&
12313
+ (newValue[key] !== newValue[key]);
12314
+ if (!bothNaN && (oldValue[key] !== newValue[key])) {
12239
12315
  changeDetected++;
12240
12316
  oldValue[key] = newValue[key];
12241
12317
  }
@@ -12387,7 +12463,7 @@ function $RootScopeProvider(){
12387
12463
  if ((value = watch.get(current)) !== (last = watch.last) &&
12388
12464
  !(watch.eq
12389
12465
  ? equals(value, last)
12390
- : (typeof value == 'number' && typeof last == 'number'
12466
+ : (typeof value === 'number' && typeof last === 'number'
12391
12467
  && isNaN(value) && isNaN(last)))) {
12392
12468
  dirty = true;
12393
12469
  lastDirtyWatch = watch;
@@ -13080,19 +13156,21 @@ function adjustMatchers(matchers) {
13080
13156
  *
13081
13157
  * Here is what a secure configuration for this scenario might look like:
13082
13158
  *
13083
- * <pre class="prettyprint">
13084
- * angular.module('myApp', []).config(function($sceDelegateProvider) {
13085
- * $sceDelegateProvider.resourceUrlWhitelist([
13086
- * // Allow same origin resource loads.
13087
- * 'self',
13088
- * // Allow loading from our assets domain. Notice the difference between * and **.
13089
- * 'http://srv*.assets.example.com/**']);
13090
- *
13091
- * // The blacklist overrides the whitelist so the open redirect here is blocked.
13092
- * $sceDelegateProvider.resourceUrlBlacklist([
13093
- * 'http://myapp.example.com/clickThru**']);
13094
- * });
13095
- * </pre>
13159
+ * ```
13160
+ * angular.module('myApp', []).config(function($sceDelegateProvider) {
13161
+ * $sceDelegateProvider.resourceUrlWhitelist([
13162
+ * // Allow same origin resource loads.
13163
+ * 'self',
13164
+ * // Allow loading from our assets domain. Notice the difference between * and **.
13165
+ * 'http://srv*.assets.example.com/**'
13166
+ * ]);
13167
+ *
13168
+ * // The blacklist overrides the whitelist so the open redirect here is blocked.
13169
+ * $sceDelegateProvider.resourceUrlBlacklist([
13170
+ * 'http://myapp.example.com/clickThru**'
13171
+ * ]);
13172
+ * });
13173
+ * ```
13096
13174
  */
13097
13175
 
13098
13176
  function $SceDelegateProvider() {
@@ -13387,10 +13465,10 @@ function $SceDelegateProvider() {
13387
13465
  *
13388
13466
  * Here's an example of a binding in a privileged context:
13389
13467
  *
13390
- * <pre class="prettyprint">
13391
- * <input ng-model="userHtml">
13392
- * <div ng-bind-html="userHtml">
13393
- * </pre>
13468
+ * ```
13469
+ * <input ng-model="userHtml">
13470
+ * <div ng-bind-html="userHtml"></div>
13471
+ * ```
13394
13472
  *
13395
13473
  * Notice that `ng-bind-html` is bound to `userHtml` controlled by the user. With SCE
13396
13474
  * disabled, this application allows the user to render arbitrary HTML into the DIV.
@@ -13430,15 +13508,15 @@ function $SceDelegateProvider() {
13430
13508
  * ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}. Here's the actual code (slightly
13431
13509
  * simplified):
13432
13510
  *
13433
- * <pre class="prettyprint">
13434
- * var ngBindHtmlDirective = ['$sce', function($sce) {
13435
- * return function(scope, element, attr) {
13436
- * scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) {
13437
- * element.html(value || '');
13438
- * });
13439
- * };
13440
- * }];
13441
- * </pre>
13511
+ * ```
13512
+ * var ngBindHtmlDirective = ['$sce', function($sce) {
13513
+ * return function(scope, element, attr) {
13514
+ * scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) {
13515
+ * element.html(value || '');
13516
+ * });
13517
+ * };
13518
+ * }];
13519
+ * ```
13442
13520
  *
13443
13521
  * ## Impact on loading templates
13444
13522
  *
@@ -13542,66 +13620,65 @@ function $SceDelegateProvider() {
13542
13620
  *
13543
13621
  * ## Show me an example using SCE.
13544
13622
  *
13545
- * @example
13546
- <example module="mySceApp" deps="angular-sanitize.js">
13547
- <file name="index.html">
13548
- <div ng-controller="myAppController as myCtrl">
13549
- <i ng-bind-html="myCtrl.explicitlyTrustedHtml" id="explicitlyTrustedHtml"></i><br><br>
13550
- <b>User comments</b><br>
13551
- By default, HTML that isn't explicitly trusted (e.g. Alice's comment) is sanitized when
13552
- $sanitize is available. If $sanitize isn't available, this results in an error instead of an
13553
- exploit.
13554
- <div class="well">
13555
- <div ng-repeat="userComment in myCtrl.userComments">
13556
- <b>{{userComment.name}}</b>:
13557
- <span ng-bind-html="userComment.htmlComment" class="htmlComment"></span>
13558
- <br>
13559
- </div>
13560
- </div>
13561
- </div>
13562
- </file>
13563
-
13564
- <file name="script.js">
13565
- var mySceApp = angular.module('mySceApp', ['ngSanitize']);
13566
-
13567
- mySceApp.controller("myAppController", function myAppController($http, $templateCache, $sce) {
13568
- var self = this;
13569
- $http.get("test_data.json", {cache: $templateCache}).success(function(userComments) {
13570
- self.userComments = userComments;
13571
- });
13572
- self.explicitlyTrustedHtml = $sce.trustAsHtml(
13573
- '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' +
13574
- 'sanitization.&quot;">Hover over this text.</span>');
13575
- });
13576
- </file>
13577
-
13578
- <file name="test_data.json">
13579
- [
13580
- { "name": "Alice",
13581
- "htmlComment":
13582
- "<span onmouseover='this.textContent=\"PWN3D!\"'>Is <i>anyone</i> reading this?</span>"
13583
- },
13584
- { "name": "Bob",
13585
- "htmlComment": "<i>Yes!</i> Am I the only other one?"
13586
- }
13587
- ]
13588
- </file>
13589
-
13590
- <file name="protractor.js" type="protractor">
13591
- describe('SCE doc demo', function() {
13592
- it('should sanitize untrusted values', function() {
13593
- expect(element(by.css('.htmlComment')).getInnerHtml())
13594
- .toBe('<span>Is <i>anyone</i> reading this?</span>');
13595
- });
13596
-
13597
- it('should NOT sanitize explicitly trusted values', function() {
13598
- expect(element(by.id('explicitlyTrustedHtml')).getInnerHtml()).toBe(
13599
- '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' +
13600
- 'sanitization.&quot;">Hover over this text.</span>');
13601
- });
13602
- });
13603
- </file>
13604
- </example>
13623
+ * <example module="mySceApp" deps="angular-sanitize.js">
13624
+ * <file name="index.html">
13625
+ * <div ng-controller="myAppController as myCtrl">
13626
+ * <i ng-bind-html="myCtrl.explicitlyTrustedHtml" id="explicitlyTrustedHtml"></i><br><br>
13627
+ * <b>User comments</b><br>
13628
+ * By default, HTML that isn't explicitly trusted (e.g. Alice's comment) is sanitized when
13629
+ * $sanitize is available. If $sanitize isn't available, this results in an error instead of an
13630
+ * exploit.
13631
+ * <div class="well">
13632
+ * <div ng-repeat="userComment in myCtrl.userComments">
13633
+ * <b>{{userComment.name}}</b>:
13634
+ * <span ng-bind-html="userComment.htmlComment" class="htmlComment"></span>
13635
+ * <br>
13636
+ * </div>
13637
+ * </div>
13638
+ * </div>
13639
+ * </file>
13640
+ *
13641
+ * <file name="script.js">
13642
+ * var mySceApp = angular.module('mySceApp', ['ngSanitize']);
13643
+ *
13644
+ * mySceApp.controller("myAppController", function myAppController($http, $templateCache, $sce) {
13645
+ * var self = this;
13646
+ * $http.get("test_data.json", {cache: $templateCache}).success(function(userComments) {
13647
+ * self.userComments = userComments;
13648
+ * });
13649
+ * self.explicitlyTrustedHtml = $sce.trustAsHtml(
13650
+ * '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' +
13651
+ * 'sanitization.&quot;">Hover over this text.</span>');
13652
+ * });
13653
+ * </file>
13654
+ *
13655
+ * <file name="test_data.json">
13656
+ * [
13657
+ * { "name": "Alice",
13658
+ * "htmlComment":
13659
+ * "<span onmouseover='this.textContent=\"PWN3D!\"'>Is <i>anyone</i> reading this?</span>"
13660
+ * },
13661
+ * { "name": "Bob",
13662
+ * "htmlComment": "<i>Yes!</i> Am I the only other one?"
13663
+ * }
13664
+ * ]
13665
+ * </file>
13666
+ *
13667
+ * <file name="protractor.js" type="protractor">
13668
+ * describe('SCE doc demo', function() {
13669
+ * it('should sanitize untrusted values', function() {
13670
+ * expect(element.all(by.css('.htmlComment')).first().getInnerHtml())
13671
+ * .toBe('<span>Is <i>anyone</i> reading this?</span>');
13672
+ * });
13673
+ *
13674
+ * it('should NOT sanitize explicitly trusted values', function() {
13675
+ * expect(element(by.id('explicitlyTrustedHtml')).getInnerHtml()).toBe(
13676
+ * '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' +
13677
+ * 'sanitization.&quot;">Hover over this text.</span>');
13678
+ * });
13679
+ * });
13680
+ * </file>
13681
+ * </example>
13605
13682
  *
13606
13683
  *
13607
13684
  *
@@ -13615,13 +13692,13 @@ function $SceDelegateProvider() {
13615
13692
  *
13616
13693
  * That said, here's how you can completely disable SCE:
13617
13694
  *
13618
- * <pre class="prettyprint">
13619
- * angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) {
13620
- * // Completely disable SCE. For demonstration purposes only!
13621
- * // Do not use in new projects.
13622
- * $sceProvider.enabled(false);
13623
- * });
13624
- * </pre>
13695
+ * ```
13696
+ * angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) {
13697
+ * // Completely disable SCE. For demonstration purposes only!
13698
+ * // Do not use in new projects.
13699
+ * $sceProvider.enabled(false);
13700
+ * });
13701
+ * ```
13625
13702
  *
13626
13703
  */
13627
13704
  /* jshint maxlen: 100 */
@@ -13732,7 +13809,7 @@ function $SceProvider() {
13732
13809
 
13733
13810
  /**
13734
13811
  * @ngdoc method
13735
- * @name $sce#parse
13812
+ * @name $sce#parseAs
13736
13813
  *
13737
13814
  * @description
13738
13815
  * Converts Angular {@link guide/expression expression} into a function. This is like {@link
@@ -14318,17 +14395,18 @@ function urlIsSameOrigin(requestUrl) {
14318
14395
  * expression.
14319
14396
  *
14320
14397
  * @example
14321
- <example>
14398
+ <example module="windowExample">
14322
14399
  <file name="index.html">
14323
14400
  <script>
14324
- function Ctrl($scope, $window) {
14325
- $scope.greeting = 'Hello, World!';
14326
- $scope.doGreeting = function(greeting) {
14401
+ angular.module('windowExample', [])
14402
+ .controller('ExampleController', ['$scope', '$window', function ($scope, $window) {
14403
+ $scope.greeting = 'Hello, World!';
14404
+ $scope.doGreeting = function(greeting) {
14327
14405
  $window.alert(greeting);
14328
- };
14329
- }
14406
+ };
14407
+ }]);
14330
14408
  </script>
14331
- <div ng-controller="Ctrl">
14409
+ <div ng-controller="ExampleController">
14332
14410
  <input type="text" ng-model="greeting" />
14333
14411
  <button ng-click="doGreeting(greeting)">ALERT</button>
14334
14412
  </div>
@@ -14346,6 +14424,17 @@ function $WindowProvider(){
14346
14424
  this.$get = valueFn(window);
14347
14425
  }
14348
14426
 
14427
+ /* global currencyFilter: true,
14428
+ dateFilter: true,
14429
+ filterFilter: true,
14430
+ jsonFilter: true,
14431
+ limitToFilter: true,
14432
+ lowercaseFilter: true,
14433
+ numberFilter: true,
14434
+ orderByFilter: true,
14435
+ uppercaseFilter: true,
14436
+ */
14437
+
14349
14438
  /**
14350
14439
  * @ngdoc provider
14351
14440
  * @name $filterProvider
@@ -14688,7 +14777,7 @@ function filterFilter() {
14688
14777
  // jshint +W086
14689
14778
  for (var key in expression) {
14690
14779
  (function(path) {
14691
- if (typeof expression[path] == 'undefined') return;
14780
+ if (typeof expression[path] === 'undefined') return;
14692
14781
  predicates.push(function(value) {
14693
14782
  return search(path == '$' ? value : (value && value[path]), expression[path]);
14694
14783
  });
@@ -14727,14 +14816,15 @@ function filterFilter() {
14727
14816
  *
14728
14817
  *
14729
14818
  * @example
14730
- <example>
14819
+ <example module="currencyExample">
14731
14820
  <file name="index.html">
14732
14821
  <script>
14733
- function Ctrl($scope) {
14734
- $scope.amount = 1234.56;
14735
- }
14822
+ angular.module('currencyExample', [])
14823
+ .controller('ExampleController', ['$scope', function($scope) {
14824
+ $scope.amount = 1234.56;
14825
+ }]);
14736
14826
  </script>
14737
- <div ng-controller="Ctrl">
14827
+ <div ng-controller="ExampleController">
14738
14828
  <input type="number" ng-model="amount"> <br>
14739
14829
  default currency symbol ($): <span id="currency-default">{{amount | currency}}</span><br>
14740
14830
  custom currency identifier (USD$): <span>{{amount | currency:"USD$"}}</span>
@@ -14786,14 +14876,15 @@ function currencyFilter($locale) {
14786
14876
  * @returns {string} Number rounded to decimalPlaces and places a “,” after each third digit.
14787
14877
  *
14788
14878
  * @example
14789
- <example>
14879
+ <example module="numberFilterExample">
14790
14880
  <file name="index.html">
14791
14881
  <script>
14792
- function Ctrl($scope) {
14793
- $scope.val = 1234.56789;
14794
- }
14882
+ angular.module('numberFilterExample', [])
14883
+ .controller('ExampleController', ['$scope', function($scope) {
14884
+ $scope.val = 1234.56789;
14885
+ }]);
14795
14886
  </script>
14796
- <div ng-controller="Ctrl">
14887
+ <div ng-controller="ExampleController">
14797
14888
  Enter number: <input ng-model='val'><br>
14798
14889
  Default formatting: <span id='number-default'>{{val | number}}</span><br>
14799
14890
  No fractions: <span>{{val | number:0}}</span><br>
@@ -14843,6 +14934,7 @@ function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
14843
14934
  var match = numStr.match(/([\d\.]+)e(-?)(\d+)/);
14844
14935
  if (match && match[2] == '-' && match[3] > fractionSize + 1) {
14845
14936
  numStr = '0';
14937
+ number = 0;
14846
14938
  } else {
14847
14939
  formatedText = numStr;
14848
14940
  hasExponent = true;
@@ -14857,8 +14949,11 @@ function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
14857
14949
  fractionSize = Math.min(Math.max(pattern.minFrac, fractionLen), pattern.maxFrac);
14858
14950
  }
14859
14951
 
14860
- var pow = Math.pow(10, fractionSize + 1);
14861
- number = Math.floor(number * pow + 5) / pow;
14952
+ // safely round numbers in JS without hitting imprecisions of floating-point arithmetics
14953
+ // inspired by:
14954
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
14955
+ number = +(Math.round(+(number.toString() + 'e' + fractionSize)).toString() + 'e' + -fractionSize);
14956
+
14862
14957
  var fraction = ('' + number).split(DECIMAL_SEP);
14863
14958
  var whole = fraction[0];
14864
14959
  fraction = fraction[1] || '';
@@ -15101,11 +15196,7 @@ function dateFilter($locale) {
15101
15196
  format = format || 'mediumDate';
15102
15197
  format = $locale.DATETIME_FORMATS[format] || format;
15103
15198
  if (isString(date)) {
15104
- if (NUMBER_STRING.test(date)) {
15105
- date = int(date);
15106
- } else {
15107
- date = jsonStringToDate(date);
15108
- }
15199
+ date = NUMBER_STRING.test(date) ? int(date) : jsonStringToDate(date);
15109
15200
  }
15110
15201
 
15111
15202
  if (isNumber(date)) {
@@ -15213,17 +15304,18 @@ var uppercaseFilter = valueFn(uppercase);
15213
15304
  * had less than `limit` elements.
15214
15305
  *
15215
15306
  * @example
15216
- <example>
15307
+ <example module="limitToExample">
15217
15308
  <file name="index.html">
15218
15309
  <script>
15219
- function Ctrl($scope) {
15220
- $scope.numbers = [1,2,3,4,5,6,7,8,9];
15221
- $scope.letters = "abcdefghi";
15222
- $scope.numLimit = 3;
15223
- $scope.letterLimit = 3;
15224
- }
15310
+ angular.module('limitToExample', [])
15311
+ .controller('ExampleController', ['$scope', function($scope) {
15312
+ $scope.numbers = [1,2,3,4,5,6,7,8,9];
15313
+ $scope.letters = "abcdefghi";
15314
+ $scope.numLimit = 3;
15315
+ $scope.letterLimit = 3;
15316
+ }]);
15225
15317
  </script>
15226
- <div ng-controller="Ctrl">
15318
+ <div ng-controller="ExampleController">
15227
15319
  Limit {{numbers}} to: <input type="integer" ng-model="numLimit">
15228
15320
  <p>Output numbers: {{ numbers | limitTo:numLimit }}</p>
15229
15321
  Limit {{letters}} to: <input type="integer" ng-model="letterLimit">
@@ -15335,20 +15427,21 @@ function limitToFilter(){
15335
15427
  * @returns {Array} Sorted copy of the source array.
15336
15428
  *
15337
15429
  * @example
15338
- <example>
15430
+ <example module="orderByExample">
15339
15431
  <file name="index.html">
15340
15432
  <script>
15341
- function Ctrl($scope) {
15342
- $scope.friends =
15343
- [{name:'John', phone:'555-1212', age:10},
15344
- {name:'Mary', phone:'555-9876', age:19},
15345
- {name:'Mike', phone:'555-4321', age:21},
15346
- {name:'Adam', phone:'555-5678', age:35},
15347
- {name:'Julie', phone:'555-8765', age:29}]
15348
- $scope.predicate = '-age';
15349
- }
15433
+ angular.module('orderByExample', [])
15434
+ .controller('ExampleController', ['$scope', function($scope) {
15435
+ $scope.friends =
15436
+ [{name:'John', phone:'555-1212', age:10},
15437
+ {name:'Mary', phone:'555-9876', age:19},
15438
+ {name:'Mike', phone:'555-4321', age:21},
15439
+ {name:'Adam', phone:'555-5678', age:35},
15440
+ {name:'Julie', phone:'555-8765', age:29}];
15441
+ $scope.predicate = '-age';
15442
+ }]);
15350
15443
  </script>
15351
- <div ng-controller="Ctrl">
15444
+ <div ng-controller="ExampleController">
15352
15445
  <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
15353
15446
  <hr/>
15354
15447
  [ <a href="" ng-click="predicate=''">unsorted</a> ]
@@ -15376,9 +15469,9 @@ function limitToFilter(){
15376
15469
  * Example:
15377
15470
  *
15378
15471
  * @example
15379
- <example>
15472
+ <example module="orderByExample">
15380
15473
  <file name="index.html">
15381
- <div ng-controller="Ctrl">
15474
+ <div ng-controller="ExampleController">
15382
15475
  <table class="friend">
15383
15476
  <tr>
15384
15477
  <th><a href="" ng-click="reverse=false;order('name', false)">Name</a>
@@ -15396,21 +15489,21 @@ function limitToFilter(){
15396
15489
  </file>
15397
15490
 
15398
15491
  <file name="script.js">
15399
- function Ctrl($scope, $filter) {
15400
- var orderBy = $filter('orderBy');
15401
- $scope.friends = [
15402
- { name: 'John', phone: '555-1212', age: 10 },
15403
- { name: 'Mary', phone: '555-9876', age: 19 },
15404
- { name: 'Mike', phone: '555-4321', age: 21 },
15405
- { name: 'Adam', phone: '555-5678', age: 35 },
15406
- { name: 'Julie', phone: '555-8765', age: 29 }
15407
- ];
15408
-
15409
- $scope.order = function(predicate, reverse) {
15410
- $scope.friends = orderBy($scope.friends, predicate, reverse);
15411
- };
15412
- $scope.order('-age',false);
15413
- }
15492
+ angular.module('orderByExample', [])
15493
+ .controller('ExampleController', ['$scope', '$filter', function($scope, $filter) {
15494
+ var orderBy = $filter('orderBy');
15495
+ $scope.friends = [
15496
+ { name: 'John', phone: '555-1212', age: 10 },
15497
+ { name: 'Mary', phone: '555-9876', age: 19 },
15498
+ { name: 'Mike', phone: '555-4321', age: 21 },
15499
+ { name: 'Adam', phone: '555-5678', age: 35 },
15500
+ { name: 'Julie', phone: '555-8765', age: 29 }
15501
+ ];
15502
+ $scope.order = function(predicate, reverse) {
15503
+ $scope.friends = orderBy($scope.friends, predicate, reverse);
15504
+ };
15505
+ $scope.order('-age',false);
15506
+ }]);
15414
15507
  </file>
15415
15508
  </example>
15416
15509
  */
@@ -15459,6 +15552,10 @@ function orderByFilter($parse){
15459
15552
  var t1 = typeof v1;
15460
15553
  var t2 = typeof v2;
15461
15554
  if (t1 == t2) {
15555
+ if (isDate(v1) && isDate(v2)) {
15556
+ v1 = v1.valueOf();
15557
+ v2 = v2.valueOf();
15558
+ }
15462
15559
  if (t1 == "string") {
15463
15560
  v1 = v1.toLowerCase();
15464
15561
  v2 = v2.toLowerCase();
@@ -15596,7 +15693,7 @@ var htmlAnchorDirective = valueFn({
15596
15693
  return browser.driver.getCurrentUrl().then(function(url) {
15597
15694
  return url.match(/\/123$/);
15598
15695
  });
15599
- }, 1000, 'page should navigate to /123');
15696
+ }, 5000, 'page should navigate to /123');
15600
15697
  });
15601
15698
 
15602
15699
  xit('should execute ng-click but not reload when href empty string and name specified', function() {
@@ -15624,7 +15721,7 @@ var htmlAnchorDirective = valueFn({
15624
15721
  return browser.driver.getCurrentUrl().then(function(url) {
15625
15722
  return url.match(/\/6$/);
15626
15723
  });
15627
- }, 1000, 'page should navigate to /6');
15724
+ }, 5000, 'page should navigate to /6');
15628
15725
  });
15629
15726
  </file>
15630
15727
  </example>
@@ -16240,12 +16337,13 @@ function FormController(element, attrs, $scope, $animate) {
16240
16337
  * </pre>
16241
16338
  *
16242
16339
  * @example
16243
- <example deps="angular-animate.js" animations="true" fixBase="true">
16340
+ <example deps="angular-animate.js" animations="true" fixBase="true" module="formExample">
16244
16341
  <file name="index.html">
16245
16342
  <script>
16246
- function Ctrl($scope) {
16247
- $scope.userType = 'guest';
16248
- }
16343
+ angular.module('formExample', [])
16344
+ .controller('FormController', ['$scope', function($scope) {
16345
+ $scope.userType = 'guest';
16346
+ }]);
16249
16347
  </script>
16250
16348
  <style>
16251
16349
  .my-form {
@@ -16257,7 +16355,7 @@ function FormController(element, attrs, $scope, $animate) {
16257
16355
  background: red;
16258
16356
  }
16259
16357
  </style>
16260
- <form name="myForm" ng-controller="Ctrl" class="my-form">
16358
+ <form name="myForm" ng-controller="FormController" class="my-form">
16261
16359
  userType: <input name="input" ng-model="userType" required>
16262
16360
  <span class="error" ng-show="myForm.input.$error.required">Required!</span><br>
16263
16361
  <tt>userType = {{userType}}</tt><br>
@@ -16351,16 +16449,14 @@ var formDirectiveFactory = function(isNgForm) {
16351
16449
  var formDirective = formDirectiveFactory();
16352
16450
  var ngFormDirective = formDirectiveFactory(true);
16353
16451
 
16354
- /* global
16355
-
16356
- -VALID_CLASS,
16357
- -INVALID_CLASS,
16358
- -PRISTINE_CLASS,
16359
- -DIRTY_CLASS
16452
+ /* global VALID_CLASS: true,
16453
+ INVALID_CLASS: true,
16454
+ PRISTINE_CLASS: true,
16455
+ DIRTY_CLASS: true
16360
16456
  */
16361
16457
 
16362
16458
  var URL_REGEXP = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/;
16363
- var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+/=?^_`{|}~.-]+@[a-z0-9-]+(\.[a-z0-9-]+)*$/i;
16459
+ var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;
16364
16460
  var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/;
16365
16461
 
16366
16462
  var inputType = {
@@ -16390,15 +16486,16 @@ var inputType = {
16390
16486
  * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
16391
16487
  *
16392
16488
  * @example
16393
- <example name="text-input-directive">
16489
+ <example name="text-input-directive" module="textInputExample">
16394
16490
  <file name="index.html">
16395
16491
  <script>
16396
- function Ctrl($scope) {
16397
- $scope.text = 'guest';
16398
- $scope.word = /^\s*\w*\s*$/;
16399
- }
16492
+ angular.module('textInputExample', [])
16493
+ .controller('ExampleController', ['$scope', function($scope) {
16494
+ $scope.text = 'guest';
16495
+ $scope.word = /^\s*\w*\s*$/;
16496
+ }]);
16400
16497
  </script>
16401
- <form name="myForm" ng-controller="Ctrl">
16498
+ <form name="myForm" ng-controller="ExampleController">
16402
16499
  Single word: <input type="text" name="input" ng-model="text"
16403
16500
  ng-pattern="word" required ng-trim="false">
16404
16501
  <span class="error" ng-show="myForm.input.$error.required">
@@ -16470,14 +16567,15 @@ var inputType = {
16470
16567
  * interaction with the input element.
16471
16568
  *
16472
16569
  * @example
16473
- <example name="number-input-directive">
16570
+ <example name="number-input-directive" module="numberExample">
16474
16571
  <file name="index.html">
16475
16572
  <script>
16476
- function Ctrl($scope) {
16477
- $scope.value = 12;
16478
- }
16573
+ angular.module('numberExample', [])
16574
+ .controller('ExampleController', ['$scope', function($scope) {
16575
+ $scope.value = 12;
16576
+ }]);
16479
16577
  </script>
16480
- <form name="myForm" ng-controller="Ctrl">
16578
+ <form name="myForm" ng-controller="ExampleController">
16481
16579
  Number: <input type="number" name="input" ng-model="value"
16482
16580
  min="0" max="99" required>
16483
16581
  <span class="error" ng-show="myForm.input.$error.required">
@@ -16545,14 +16643,15 @@ var inputType = {
16545
16643
  * interaction with the input element.
16546
16644
  *
16547
16645
  * @example
16548
- <example name="url-input-directive">
16646
+ <example name="url-input-directive" module="urlExample">
16549
16647
  <file name="index.html">
16550
16648
  <script>
16551
- function Ctrl($scope) {
16552
- $scope.text = 'http://google.com';
16553
- }
16649
+ angular.module('urlExample', [])
16650
+ .controller('ExampleController', ['$scope', function($scope) {
16651
+ $scope.text = 'http://google.com';
16652
+ }]);
16554
16653
  </script>
16555
- <form name="myForm" ng-controller="Ctrl">
16654
+ <form name="myForm" ng-controller="ExampleController">
16556
16655
  URL: <input type="url" name="input" ng-model="text" required>
16557
16656
  <span class="error" ng-show="myForm.input.$error.required">
16558
16657
  Required!</span>
@@ -16621,14 +16720,15 @@ var inputType = {
16621
16720
  * interaction with the input element.
16622
16721
  *
16623
16722
  * @example
16624
- <example name="email-input-directive">
16723
+ <example name="email-input-directive" module="emailExample">
16625
16724
  <file name="index.html">
16626
16725
  <script>
16627
- function Ctrl($scope) {
16628
- $scope.text = 'me@example.com';
16629
- }
16726
+ angular.module('emailExample', [])
16727
+ .controller('ExampleController', ['$scope', function($scope) {
16728
+ $scope.text = 'me@example.com';
16729
+ }]);
16630
16730
  </script>
16631
- <form name="myForm" ng-controller="Ctrl">
16731
+ <form name="myForm" ng-controller="ExampleController">
16632
16732
  Email: <input type="email" name="input" ng-model="text" required>
16633
16733
  <span class="error" ng-show="myForm.input.$error.required">
16634
16734
  Required!</span>
@@ -16687,18 +16787,19 @@ var inputType = {
16687
16787
  * be set when selected.
16688
16788
  *
16689
16789
  * @example
16690
- <example name="radio-input-directive">
16790
+ <example name="radio-input-directive" module="radioExample">
16691
16791
  <file name="index.html">
16692
16792
  <script>
16693
- function Ctrl($scope) {
16694
- $scope.color = 'blue';
16695
- $scope.specialValue = {
16696
- "id": "12345",
16697
- "value": "green"
16698
- };
16699
- }
16793
+ angular.module('radioExample', [])
16794
+ .controller('ExampleController', ['$scope', function($scope) {
16795
+ $scope.color = 'blue';
16796
+ $scope.specialValue = {
16797
+ "id": "12345",
16798
+ "value": "green"
16799
+ };
16800
+ }]);
16700
16801
  </script>
16701
- <form name="myForm" ng-controller="Ctrl">
16802
+ <form name="myForm" ng-controller="ExampleController">
16702
16803
  <input type="radio" ng-model="color" value="red"> Red <br/>
16703
16804
  <input type="radio" ng-model="color" ng-value="specialValue"> Green <br/>
16704
16805
  <input type="radio" ng-model="color" value="blue"> Blue <br/>
@@ -16737,15 +16838,16 @@ var inputType = {
16737
16838
  * interaction with the input element.
16738
16839
  *
16739
16840
  * @example
16740
- <example name="checkbox-input-directive">
16841
+ <example name="checkbox-input-directive" module="checkboxExample">
16741
16842
  <file name="index.html">
16742
16843
  <script>
16743
- function Ctrl($scope) {
16744
- $scope.value1 = true;
16745
- $scope.value2 = 'YES'
16746
- }
16844
+ angular.module('checkboxExample', [])
16845
+ .controller('ExampleController', ['$scope', function($scope) {
16846
+ $scope.value1 = true;
16847
+ $scope.value2 = 'YES'
16848
+ }]);
16747
16849
  </script>
16748
- <form name="myForm" ng-controller="Ctrl">
16850
+ <form name="myForm" ng-controller="ExampleController">
16749
16851
  Value1: <input type="checkbox" ng-model="value1"> <br/>
16750
16852
  Value2: <input type="checkbox" ng-model="value2"
16751
16853
  ng-true-value="YES" ng-false-value="NO"> <br/>
@@ -16786,15 +16888,29 @@ function validate(ctrl, validatorName, validity, value){
16786
16888
  return validity ? value : undefined;
16787
16889
  }
16788
16890
 
16891
+ function testFlags(validity, flags) {
16892
+ var i, flag;
16893
+ if (flags) {
16894
+ for (i=0; i<flags.length; ++i) {
16895
+ flag = flags[i];
16896
+ if (validity[flag]) {
16897
+ return true;
16898
+ }
16899
+ }
16900
+ }
16901
+ return false;
16902
+ }
16789
16903
 
16790
- function addNativeHtml5Validators(ctrl, validatorName, element) {
16791
- var validity = element.prop('validity');
16904
+ // Pass validity so that behaviour can be mocked easier.
16905
+ function addNativeHtml5Validators(ctrl, validatorName, badFlags, ignoreFlags, validity) {
16792
16906
  if (isObject(validity)) {
16907
+ ctrl.$$hasNativeValidators = true;
16793
16908
  var validator = function(value) {
16794
16909
  // Don't overwrite previous validation, don't consider valueMissing to apply (ng-required can
16795
16910
  // perform the required validation)
16796
- if (!ctrl.$error[validatorName] && (validity.badInput || validity.customError ||
16797
- validity.typeMismatch) && !validity.valueMissing) {
16911
+ if (!ctrl.$error[validatorName] &&
16912
+ !testFlags(validity, ignoreFlags) &&
16913
+ testFlags(validity, badFlags)) {
16798
16914
  ctrl.$setValidity(validatorName, false);
16799
16915
  return;
16800
16916
  }
@@ -16805,8 +16921,9 @@ function addNativeHtml5Validators(ctrl, validatorName, element) {
16805
16921
  }
16806
16922
 
16807
16923
  function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
16808
- var validity = element.prop('validity');
16924
+ var validity = element.prop(VALIDITY_STATE_PROPERTY);
16809
16925
  var placeholder = element[0].placeholder, noevent = {};
16926
+ ctrl.$$validityState = validity;
16810
16927
 
16811
16928
  // In composition mode, users are still inputing intermediate text buffer,
16812
16929
  // hold the listener until composition is done.
@@ -16844,11 +16961,11 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
16844
16961
  value = trim(value);
16845
16962
  }
16846
16963
 
16847
- if (ctrl.$viewValue !== value ||
16848
- // If the value is still empty/falsy, and there is no `required` error, run validators
16849
- // again. This enables HTML5 constraint validation errors to affect Angular validation
16850
- // even when the first character entered causes an error.
16851
- (validity && value === '' && !validity.valueMissing)) {
16964
+ // If a control is suffering from bad input, browsers discard its value, so it may be
16965
+ // necessary to revalidate even if the control's value is the same empty value twice in
16966
+ // a row.
16967
+ var revalidate = validity && ctrl.$$hasNativeValidators;
16968
+ if (ctrl.$viewValue !== value || (value === '' && revalidate)) {
16852
16969
  if (scope.$$phase) {
16853
16970
  ctrl.$setViewValue(value);
16854
16971
  } else {
@@ -16954,6 +17071,8 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
16954
17071
  }
16955
17072
  }
16956
17073
 
17074
+ var numberBadFlags = ['badInput'];
17075
+
16957
17076
  function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
16958
17077
  textInputType(scope, element, attr, ctrl, $sniffer, $browser);
16959
17078
 
@@ -16968,7 +17087,7 @@ function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
16968
17087
  }
16969
17088
  });
16970
17089
 
16971
- addNativeHtml5Validators(ctrl, 'number', element);
17090
+ addNativeHtml5Validators(ctrl, 'number', numberBadFlags, null, ctrl.$$validityState);
16972
17091
 
16973
17092
  ctrl.$formatters.push(function(value) {
16974
17093
  return ctrl.$isEmpty(value) ? '' : '' + value;
@@ -17128,14 +17247,15 @@ function checkboxInputType(scope, element, attr, ctrl) {
17128
17247
  * interaction with the input element.
17129
17248
  *
17130
17249
  * @example
17131
- <example name="input-directive">
17250
+ <example name="input-directive" module="inputExample">
17132
17251
  <file name="index.html">
17133
17252
  <script>
17134
- function Ctrl($scope) {
17135
- $scope.user = {name: 'guest', last: 'visitor'};
17136
- }
17253
+ angular.module('inputExample', [])
17254
+ .controller('ExampleController', ['$scope', function($scope) {
17255
+ $scope.user = {name: 'guest', last: 'visitor'};
17256
+ }]);
17137
17257
  </script>
17138
- <div ng-controller="Ctrl">
17258
+ <div ng-controller="ExampleController">
17139
17259
  <form name="myForm">
17140
17260
  User name: <input type="text" name="userName" ng-model="user.name" required>
17141
17261
  <span class="error" ng-show="myForm.userName.$error.required">
@@ -17450,7 +17570,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
17450
17570
  * This method should be called by validators - i.e. the parser or formatter functions.
17451
17571
  *
17452
17572
  * @param {string} validationErrorKey Name of the validator. the `validationErrorKey` will assign
17453
- * to `$error[validationErrorKey]=isValid` so that it is available for data-binding.
17573
+ * to `$error[validationErrorKey]=!isValid` so that it is available for data-binding.
17454
17574
  * The `validationErrorKey` should be in camelCase and will get converted into dash-case
17455
17575
  * for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error`
17456
17576
  * class and can be bound to as `{{someForm.someControl.$error.myError}}` .
@@ -17653,12 +17773,13 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
17653
17773
  * </pre>
17654
17774
  *
17655
17775
  * @example
17656
- * <example deps="angular-animate.js" animations="true" fixBase="true">
17776
+ * <example deps="angular-animate.js" animations="true" fixBase="true" module="inputExample">
17657
17777
  <file name="index.html">
17658
17778
  <script>
17659
- function Ctrl($scope) {
17660
- $scope.val = '1';
17661
- }
17779
+ angular.module('inputExample', [])
17780
+ .controller('ExampleController', ['$scope', function($scope) {
17781
+ $scope.val = '1';
17782
+ }]);
17662
17783
  </script>
17663
17784
  <style>
17664
17785
  .my-input {
@@ -17673,7 +17794,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
17673
17794
  </style>
17674
17795
  Update input to see transitions when valid/invalid.
17675
17796
  Integer is a valid value.
17676
- <form name="testForm" ng-controller="Ctrl">
17797
+ <form name="testForm" ng-controller="ExampleController">
17677
17798
  <input ng-model="val" ng-pattern="/^\d+$/" name="anim" class="my-input" />
17678
17799
  </form>
17679
17800
  </file>
@@ -17717,17 +17838,18 @@ var ngModelDirective = function() {
17717
17838
  * in input value.
17718
17839
  *
17719
17840
  * @example
17720
- * <example name="ngChange-directive">
17841
+ * <example name="ngChange-directive" module="changeExample">
17721
17842
  * <file name="index.html">
17722
17843
  * <script>
17723
- * function Controller($scope) {
17724
- * $scope.counter = 0;
17725
- * $scope.change = function() {
17726
- * $scope.counter++;
17727
- * };
17728
- * }
17844
+ * angular.module('changeExample', [])
17845
+ * .controller('ExampleController', ['$scope', function($scope) {
17846
+ * $scope.counter = 0;
17847
+ * $scope.change = function() {
17848
+ * $scope.counter++;
17849
+ * };
17850
+ * }]);
17729
17851
  * </script>
17730
- * <div ng-controller="Controller">
17852
+ * <div ng-controller="ExampleController">
17731
17853
  * <input type="checkbox" ng-model="confirmed" ng-change="change()" id="ng-change-example1" />
17732
17854
  * <input type="checkbox" ng-model="confirmed" id="ng-change-example2" />
17733
17855
  * <label for="ng-change-example2">Confirmed</label><br />
@@ -17808,14 +17930,15 @@ var requiredDirective = function() {
17808
17930
  * specified in form `/something/` then the value will be converted into a regular expression.
17809
17931
  *
17810
17932
  * @example
17811
- <example name="ngList-directive">
17933
+ <example name="ngList-directive" module="listExample">
17812
17934
  <file name="index.html">
17813
17935
  <script>
17814
- function Ctrl($scope) {
17815
- $scope.names = ['igor', 'misko', 'vojta'];
17816
- }
17936
+ angular.module('listExample', [])
17937
+ .controller('ExampleController', ['$scope', function($scope) {
17938
+ $scope.names = ['igor', 'misko', 'vojta'];
17939
+ }]);
17817
17940
  </script>
17818
- <form name="myForm" ng-controller="Ctrl">
17941
+ <form name="myForm" ng-controller="ExampleController">
17819
17942
  List: <input name="namesInput" ng-model="names" ng-list required>
17820
17943
  <span class="error" ng-show="myForm.namesInput.$error.required">
17821
17944
  Required!</span>
@@ -17907,15 +18030,16 @@ var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
17907
18030
  * of the `input` element
17908
18031
  *
17909
18032
  * @example
17910
- <example name="ngValue-directive">
18033
+ <example name="ngValue-directive" module="valueExample">
17911
18034
  <file name="index.html">
17912
18035
  <script>
17913
- function Ctrl($scope) {
17914
- $scope.names = ['pizza', 'unicorns', 'robots'];
17915
- $scope.my = { favorite: 'unicorns' };
17916
- }
18036
+ angular.module('valueExample', [])
18037
+ .controller('ExampleController', ['$scope', function($scope) {
18038
+ $scope.names = ['pizza', 'unicorns', 'robots'];
18039
+ $scope.my = { favorite: 'unicorns' };
18040
+ }]);
17917
18041
  </script>
17918
- <form ng-controller="Ctrl">
18042
+ <form ng-controller="ExampleController">
17919
18043
  <h2>Which is your favorite?</h2>
17920
18044
  <label ng-repeat="name in names" for="{{name}}">
17921
18045
  {{name}}
@@ -17973,7 +18097,7 @@ var ngValueDirective = function() {
17973
18097
  * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
17974
18098
  * `{{ expression }}` which is similar but less verbose.
17975
18099
  *
17976
- * It is preferable to use `ngBind` instead of `{{ expression }}` when a template is momentarily
18100
+ * It is preferable to use `ngBind` instead of `{{ expression }}` if a template is momentarily
17977
18101
  * displayed by the browser in its raw state before Angular compiles it. Since `ngBind` is an
17978
18102
  * element attribute, it makes the bindings invisible to the user while the page is loading.
17979
18103
  *
@@ -17986,14 +18110,15 @@ var ngValueDirective = function() {
17986
18110
  *
17987
18111
  * @example
17988
18112
  * Enter a name in the Live Preview text box; the greeting below the text box changes instantly.
17989
- <example>
18113
+ <example module="bindExample">
17990
18114
  <file name="index.html">
17991
18115
  <script>
17992
- function Ctrl($scope) {
17993
- $scope.name = 'Whirled';
17994
- }
18116
+ angular.module('bindExample', [])
18117
+ .controller('ExampleController', ['$scope', function($scope) {
18118
+ $scope.name = 'Whirled';
18119
+ }]);
17995
18120
  </script>
17996
- <div ng-controller="Ctrl">
18121
+ <div ng-controller="ExampleController">
17997
18122
  Enter name: <input type="text" ng-model="name"><br>
17998
18123
  Hello <span ng-bind="name"></span>!
17999
18124
  </div>
@@ -18044,15 +18169,16 @@ var ngBindDirective = ngDirective({
18044
18169
  *
18045
18170
  * @example
18046
18171
  * Try it here: enter text in text box and watch the greeting change.
18047
- <example>
18172
+ <example module="bindExample">
18048
18173
  <file name="index.html">
18049
18174
  <script>
18050
- function Ctrl($scope) {
18051
- $scope.salutation = 'Hello';
18052
- $scope.name = 'World';
18053
- }
18175
+ angular.module('bindExample', [])
18176
+ .controller('ExampleController', ['$scope', function ($scope) {
18177
+ $scope.salutation = 'Hello';
18178
+ $scope.name = 'World';
18179
+ }]);
18054
18180
  </script>
18055
- <div ng-controller="Ctrl">
18181
+ <div ng-controller="ExampleController">
18056
18182
  Salutation: <input type="text" ng-model="salutation"><br>
18057
18183
  Name: <input type="text" ng-model="name"><br>
18058
18184
  <pre ng-bind-template="{{salutation}} {{name}}!"></pre>
@@ -18110,20 +18236,20 @@ var ngBindTemplateDirective = ['$interpolate', function($interpolate) {
18110
18236
  * @example
18111
18237
  Try it here: enter text in text box and watch the greeting change.
18112
18238
 
18113
- <example module="ngBindHtmlExample" deps="angular-sanitize.js">
18239
+ <example module="bindHtmlExample" deps="angular-sanitize.js">
18114
18240
  <file name="index.html">
18115
- <div ng-controller="ngBindHtmlCtrl">
18241
+ <div ng-controller="ExampleController">
18116
18242
  <p ng-bind-html="myHTML"></p>
18117
18243
  </div>
18118
18244
  </file>
18119
18245
 
18120
18246
  <file name="script.js">
18121
- angular.module('ngBindHtmlExample', ['ngSanitize'])
18122
-
18123
- .controller('ngBindHtmlCtrl', ['$scope', function ngBindHtmlCtrl($scope) {
18124
- $scope.myHTML =
18125
- 'I am an <code>HTML</code>string with <a href="#">links!</a> and other <em>stuff</em>';
18126
- }]);
18247
+ angular.module('bindHtmlExample', ['ngSanitize'])
18248
+ .controller('ExampleController', ['$scope', function($scope) {
18249
+ $scope.myHTML =
18250
+ 'I am an <code>HTML</code>string with ' +
18251
+ '<a href="#">links!</a> and other <em>stuff</em>';
18252
+ }]);
18127
18253
  </file>
18128
18254
 
18129
18255
  <file name="protractor.js" type="protractor">
@@ -18135,15 +18261,24 @@ var ngBindTemplateDirective = ['$interpolate', function($interpolate) {
18135
18261
  </example>
18136
18262
  */
18137
18263
  var ngBindHtmlDirective = ['$sce', '$parse', function($sce, $parse) {
18138
- return function(scope, element, attr) {
18139
- element.addClass('ng-binding').data('$binding', attr.ngBindHtml);
18264
+ return {
18265
+ compile: function (tElement) {
18266
+ tElement.addClass('ng-binding');
18140
18267
 
18141
- var parsed = $parse(attr.ngBindHtml);
18142
- function getStringValue() { return (parsed(scope) || '').toString(); }
18268
+ return function (scope, element, attr) {
18269
+ element.data('$binding', attr.ngBindHtml);
18143
18270
 
18144
- scope.$watch(getStringValue, function ngBindHtmlWatchAction(value) {
18145
- element.html($sce.getTrustedHtml(parsed(scope)) || '');
18146
- });
18271
+ var parsed = $parse(attr.ngBindHtml);
18272
+
18273
+ function getStringValue() {
18274
+ return (parsed(scope) || '').toString();
18275
+ }
18276
+
18277
+ scope.$watch(getStringValue, function ngBindHtmlWatchAction(value) {
18278
+ element.html($sce.getTrustedHtml(parsed(scope)) || '');
18279
+ });
18280
+ };
18281
+ }
18147
18282
  };
18148
18283
  }];
18149
18284
 
@@ -18622,7 +18757,7 @@ var ngCloakDirective = ngDirective({
18622
18757
  *
18623
18758
  * This example demonstrates the `controller as` syntax.
18624
18759
  *
18625
- * <example name="ngControllerAs">
18760
+ * <example name="ngControllerAs" module="controllerAsExample">
18626
18761
  * <file name="index.html">
18627
18762
  * <div id="ctrl-as-exmpl" ng-controller="SettingsController1 as settings">
18628
18763
  * Name: <input type="text" ng-model="settings.name"/>
@@ -18643,6 +18778,9 @@ var ngCloakDirective = ngDirective({
18643
18778
  * </div>
18644
18779
  * </file>
18645
18780
  * <file name="app.js">
18781
+ * angular.module('controllerAsExample', [])
18782
+ * .controller('SettingsController1', SettingsController1);
18783
+ *
18646
18784
  * function SettingsController1() {
18647
18785
  * this.name = "John Smith";
18648
18786
  * this.contacts = [
@@ -18671,29 +18809,29 @@ var ngCloakDirective = ngDirective({
18671
18809
  * <file name="protractor.js" type="protractor">
18672
18810
  * it('should check controller as', function() {
18673
18811
  * var container = element(by.id('ctrl-as-exmpl'));
18674
- * expect(container.findElement(by.model('settings.name'))
18812
+ * expect(container.element(by.model('settings.name'))
18675
18813
  * .getAttribute('value')).toBe('John Smith');
18676
18814
  *
18677
18815
  * var firstRepeat =
18678
- * container.findElement(by.repeater('contact in settings.contacts').row(0));
18816
+ * container.element(by.repeater('contact in settings.contacts').row(0));
18679
18817
  * var secondRepeat =
18680
- * container.findElement(by.repeater('contact in settings.contacts').row(1));
18818
+ * container.element(by.repeater('contact in settings.contacts').row(1));
18681
18819
  *
18682
- * expect(firstRepeat.findElement(by.model('contact.value')).getAttribute('value'))
18820
+ * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
18683
18821
  * .toBe('408 555 1212');
18684
18822
  *
18685
- * expect(secondRepeat.findElement(by.model('contact.value')).getAttribute('value'))
18823
+ * expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
18686
18824
  * .toBe('john.smith@example.org');
18687
18825
  *
18688
- * firstRepeat.findElement(by.linkText('clear')).click();
18826
+ * firstRepeat.element(by.linkText('clear')).click();
18689
18827
  *
18690
- * expect(firstRepeat.findElement(by.model('contact.value')).getAttribute('value'))
18828
+ * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
18691
18829
  * .toBe('');
18692
18830
  *
18693
- * container.findElement(by.linkText('add')).click();
18831
+ * container.element(by.linkText('add')).click();
18694
18832
  *
18695
- * expect(container.findElement(by.repeater('contact in settings.contacts').row(2))
18696
- * .findElement(by.model('contact.value'))
18833
+ * expect(container.element(by.repeater('contact in settings.contacts').row(2))
18834
+ * .element(by.model('contact.value'))
18697
18835
  * .getAttribute('value'))
18698
18836
  * .toBe('yourname@example.org');
18699
18837
  * });
@@ -18702,7 +18840,7 @@ var ngCloakDirective = ngDirective({
18702
18840
  *
18703
18841
  * This example demonstrates the "attach to `$scope`" style of controller.
18704
18842
  *
18705
- * <example name="ngController">
18843
+ * <example name="ngController" module="controllerExample">
18706
18844
  * <file name="index.html">
18707
18845
  * <div id="ctrl-exmpl" ng-controller="SettingsController2">
18708
18846
  * Name: <input type="text" ng-model="name"/>
@@ -18723,6 +18861,9 @@ var ngCloakDirective = ngDirective({
18723
18861
  * </div>
18724
18862
  * </file>
18725
18863
  * <file name="app.js">
18864
+ * angular.module('controllerExample', [])
18865
+ * .controller('SettingsController2', ['$scope', SettingsController2]);
18866
+ *
18726
18867
  * function SettingsController2($scope) {
18727
18868
  * $scope.name = "John Smith";
18728
18869
  * $scope.contacts = [
@@ -18752,28 +18893,28 @@ var ngCloakDirective = ngDirective({
18752
18893
  * it('should check controller', function() {
18753
18894
  * var container = element(by.id('ctrl-exmpl'));
18754
18895
  *
18755
- * expect(container.findElement(by.model('name'))
18896
+ * expect(container.element(by.model('name'))
18756
18897
  * .getAttribute('value')).toBe('John Smith');
18757
18898
  *
18758
18899
  * var firstRepeat =
18759
- * container.findElement(by.repeater('contact in contacts').row(0));
18900
+ * container.element(by.repeater('contact in contacts').row(0));
18760
18901
  * var secondRepeat =
18761
- * container.findElement(by.repeater('contact in contacts').row(1));
18902
+ * container.element(by.repeater('contact in contacts').row(1));
18762
18903
  *
18763
- * expect(firstRepeat.findElement(by.model('contact.value')).getAttribute('value'))
18904
+ * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
18764
18905
  * .toBe('408 555 1212');
18765
- * expect(secondRepeat.findElement(by.model('contact.value')).getAttribute('value'))
18906
+ * expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
18766
18907
  * .toBe('john.smith@example.org');
18767
18908
  *
18768
- * firstRepeat.findElement(by.linkText('clear')).click();
18909
+ * firstRepeat.element(by.linkText('clear')).click();
18769
18910
  *
18770
- * expect(firstRepeat.findElement(by.model('contact.value')).getAttribute('value'))
18911
+ * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
18771
18912
  * .toBe('');
18772
18913
  *
18773
- * container.findElement(by.linkText('add')).click();
18914
+ * container.element(by.linkText('add')).click();
18774
18915
  *
18775
- * expect(container.findElement(by.repeater('contact in contacts').row(2))
18776
- * .findElement(by.model('contact.value'))
18916
+ * expect(container.element(by.repeater('contact in contacts').row(2))
18917
+ * .element(by.model('contact.value'))
18777
18918
  * .getAttribute('value'))
18778
18919
  * .toBe('yourname@example.org');
18779
18920
  * });
@@ -18800,8 +18941,10 @@ var ngControllerDirective = [function() {
18800
18941
  * This is necessary when developing things like Google Chrome Extensions.
18801
18942
  *
18802
18943
  * CSP forbids apps to use `eval` or `Function(string)` generated functions (among other things).
18803
- * For us to be compatible, we just need to implement the "getterFn" in $parse without violating
18804
- * any of these restrictions.
18944
+ * For Angular to be CSP compatible there are only two things that we need to do differently:
18945
+ *
18946
+ * - don't use `Function` constructor to generate optimized value getters
18947
+ * - don't inject custom stylesheet into the document
18805
18948
  *
18806
18949
  * AngularJS uses `Function(string)` generated functions as a speed optimization. Applying the `ngCsp`
18807
18950
  * directive will cause Angular to use CSP compatibility mode. When this mode is on AngularJS will
@@ -18812,7 +18955,18 @@ var ngControllerDirective = [function() {
18812
18955
  * includes some CSS rules (e.g. {@link ng.directive:ngCloak ngCloak}).
18813
18956
  * To make those directives work in CSP mode, include the `angular-csp.css` manually.
18814
18957
  *
18815
- * In order to use this feature put the `ngCsp` directive on the root element of the application.
18958
+ * Angular tries to autodetect if CSP is active and automatically turn on the CSP-safe mode. This
18959
+ * autodetection however triggers a CSP error to be logged in the console:
18960
+ *
18961
+ * ```
18962
+ * Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of
18963
+ * script in the following Content Security Policy directive: "default-src 'self'". Note that
18964
+ * 'script-src' was not explicitly set, so 'default-src' is used as a fallback.
18965
+ * ```
18966
+ *
18967
+ * This error is harmless but annoying. To prevent the error from showing up, put the `ngCsp`
18968
+ * directive on the root element of the application or on the `angular.js` script tag, whichever
18969
+ * appears first in the html document.
18816
18970
  *
18817
18971
  * *Note: This directive is only available in the `ng-csp` and `data-ng-csp` attribute form.*
18818
18972
  *
@@ -18827,9 +18981,9 @@ var ngControllerDirective = [function() {
18827
18981
  ```
18828
18982
  */
18829
18983
 
18830
- // ngCsp is not implemented as a proper directive any more, because we need it be processed while we bootstrap
18831
- // the system (before $parse is instantiated), for this reason we just have a csp() fn that looks for ng-csp attribute
18832
- // anywhere in the current doc
18984
+ // ngCsp is not implemented as a proper directive any more, because we need it be processed while we
18985
+ // bootstrap the system (before $parse is instantiated), for this reason we just have
18986
+ // the csp.isActive() fn that looks for ng-csp attribute anywhere in the current doc
18833
18987
 
18834
18988
  /**
18835
18989
  * @ngdoc directive
@@ -19144,21 +19298,22 @@ forEach(
19144
19298
  * ({@link guide/expression#-event- Event object is available as `$event`})
19145
19299
  *
19146
19300
  * @example
19147
- <example>
19301
+ <example module="submitExample">
19148
19302
  <file name="index.html">
19149
19303
  <script>
19150
- function Ctrl($scope) {
19151
- $scope.list = [];
19152
- $scope.text = 'hello';
19153
- $scope.submit = function() {
19154
- if ($scope.text) {
19155
- $scope.list.push(this.text);
19156
- $scope.text = '';
19157
- }
19158
- };
19159
- }
19304
+ angular.module('submitExample', [])
19305
+ .controller('ExampleController', ['$scope', function($scope) {
19306
+ $scope.list = [];
19307
+ $scope.text = 'hello';
19308
+ $scope.submit = function() {
19309
+ if ($scope.text) {
19310
+ $scope.list.push(this.text);
19311
+ $scope.text = '';
19312
+ }
19313
+ };
19314
+ }]);
19160
19315
  </script>
19161
- <form ng-submit="submit()" ng-controller="Ctrl">
19316
+ <form ng-submit="submit()" ng-controller="ExampleController">
19162
19317
  Enter text and hit enter:
19163
19318
  <input type="text" ng-model="text" name="text" />
19164
19319
  <input type="submit" id="submit" value="Submit" />
@@ -19170,7 +19325,7 @@ forEach(
19170
19325
  expect(element(by.binding('list')).getText()).toBe('list=[]');
19171
19326
  element(by.css('#submit')).click();
19172
19327
  expect(element(by.binding('list')).getText()).toContain('hello');
19173
- expect(element(by.input('text')).getAttribute('value')).toBe('');
19328
+ expect(element(by.model('text')).getAttribute('value')).toBe('');
19174
19329
  });
19175
19330
  it('should ignore empty strings', function() {
19176
19331
  expect(element(by.binding('list')).getText()).toBe('list=[]');
@@ -19443,9 +19598,9 @@ var ngIfDirective = ['$animate', function($animate) {
19443
19598
  * - Otherwise enable scrolling only if the expression evaluates to truthy value.
19444
19599
  *
19445
19600
  * @example
19446
- <example module="ngAnimate" deps="angular-animate.js" animations="true">
19601
+ <example module="includeExample" deps="angular-animate.js" animations="true">
19447
19602
  <file name="index.html">
19448
- <div ng-controller="Ctrl">
19603
+ <div ng-controller="ExampleController">
19449
19604
  <select ng-model="template" ng-options="t.name for t in templates">
19450
19605
  <option value="">(blank)</option>
19451
19606
  </select>
@@ -19457,12 +19612,13 @@ var ngIfDirective = ['$animate', function($animate) {
19457
19612
  </div>
19458
19613
  </file>
19459
19614
  <file name="script.js">
19460
- function Ctrl($scope) {
19461
- $scope.templates =
19462
- [ { name: 'template1.html', url: 'template1.html'},
19463
- { name: 'template2.html', url: 'template2.html'} ];
19464
- $scope.template = $scope.templates[0];
19465
- }
19615
+ angular.module('includeExample', ['ngAnimate'])
19616
+ .controller('ExampleController', ['$scope', function($scope) {
19617
+ $scope.templates =
19618
+ [ { name: 'template1.html', url: 'template1.html'},
19619
+ { name: 'template2.html', url: 'template2.html'} ];
19620
+ $scope.template = $scope.templates[0];
19621
+ }]);
19466
19622
  </file>
19467
19623
  <file name="template1.html">
19468
19624
  Content of template1.html
@@ -19525,7 +19681,7 @@ var ngIfDirective = ['$animate', function($animate) {
19525
19681
  return;
19526
19682
  }
19527
19683
  templateSelect.click();
19528
- templateSelect.element.all(by.css('option')).get(2).click();
19684
+ templateSelect.all(by.css('option')).get(2).click();
19529
19685
  expect(includeElem.getText()).toMatch(/Content of template2.html/);
19530
19686
  });
19531
19687
 
@@ -19535,7 +19691,7 @@ var ngIfDirective = ['$animate', function($animate) {
19535
19691
  return;
19536
19692
  }
19537
19693
  templateSelect.click();
19538
- templateSelect.element.all(by.css('option')).get(0).click();
19694
+ templateSelect.all(by.css('option')).get(0).click();
19539
19695
  expect(includeElem.isPresent()).toBe(false);
19540
19696
  });
19541
19697
  </file>
@@ -19687,14 +19843,15 @@ var ngIncludeFillContentDirective = ['$compile',
19687
19843
  * @param {expression} ngInit {@link guide/expression Expression} to eval.
19688
19844
  *
19689
19845
  * @example
19690
- <example>
19846
+ <example module="initExample">
19691
19847
  <file name="index.html">
19692
19848
  <script>
19693
- function Ctrl($scope) {
19694
- $scope.list = [['a', 'b'], ['c', 'd']];
19695
- }
19849
+ angular.module('initExample', [])
19850
+ .controller('ExampleController', ['$scope', function($scope) {
19851
+ $scope.list = [['a', 'b'], ['c', 'd']];
19852
+ }]);
19696
19853
  </script>
19697
- <div ng-controller="Ctrl">
19854
+ <div ng-controller="ExampleController">
19698
19855
  <div ng-repeat="innerList in list" ng-init="outerIndex = $index">
19699
19856
  <div ng-repeat="value in innerList" ng-init="innerIndex = $index">
19700
19857
  <span class="example-init">list[ {{outerIndex}} ][ {{innerIndex}} ] = {{value}};</span>
@@ -19834,7 +19991,7 @@ var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 });
19834
19991
  * When one person, perhaps John, views the document, "John is viewing" will be shown.
19835
19992
  * When three people view the document, no explicit number rule is found, so
19836
19993
  * an offset of 2 is taken off 3, and Angular uses 1 to decide the plural category.
19837
- * In this case, plural category 'one' is matched and "John, Marry and one other person are viewing"
19994
+ * In this case, plural category 'one' is matched and "John, Mary and one other person are viewing"
19838
19995
  * is shown.
19839
19996
  *
19840
19997
  * Note that when you specify offsets, you must provide explicit number rules for
@@ -19847,16 +20004,17 @@ var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 });
19847
20004
  * @param {number=} offset Offset to deduct from the total number.
19848
20005
  *
19849
20006
  * @example
19850
- <example>
20007
+ <example module="pluralizeExample">
19851
20008
  <file name="index.html">
19852
20009
  <script>
19853
- function Ctrl($scope) {
19854
- $scope.person1 = 'Igor';
19855
- $scope.person2 = 'Misko';
19856
- $scope.personCount = 1;
19857
- }
20010
+ angular.module('pluralizeExample', [])
20011
+ .controller('ExampleController', ['$scope', function($scope) {
20012
+ $scope.person1 = 'Igor';
20013
+ $scope.person2 = 'Misko';
20014
+ $scope.personCount = 1;
20015
+ }]);
19858
20016
  </script>
19859
- <div ng-controller="Ctrl">
20017
+ <div ng-controller="ExampleController">
19860
20018
  Person 1:<input type="text" ng-model="person1" value="Igor" /><br/>
19861
20019
  Person 2:<input type="text" ng-model="person2" value="Misko" /><br/>
19862
20020
  Number of People:<input type="text" ng-model="personCount" value="1" /><br/>
@@ -20716,7 +20874,7 @@ var ngHideDirective = ['$animate', function($animate) {
20716
20874
  <file name="protractor.js" type="protractor">
20717
20875
  var colorSpan = element(by.css('span'));
20718
20876
 
20719
- iit('should check ng-style', function() {
20877
+ it('should check ng-style', function() {
20720
20878
  expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
20721
20879
  element(by.css('input[value=\'set color\']')).click();
20722
20880
  expect(colorSpan.getCssValue('color')).toBe('rgba(255, 0, 0, 1)');
@@ -20790,9 +20948,9 @@ var ngStyleDirective = ngDirective(function(scope, element, attr) {
20790
20948
  *
20791
20949
  *
20792
20950
  * @example
20793
- <example module="ngAnimate" deps="angular-animate.js" animations="true">
20951
+ <example module="switchExample" deps="angular-animate.js" animations="true">
20794
20952
  <file name="index.html">
20795
- <div ng-controller="Ctrl">
20953
+ <div ng-controller="ExampleController">
20796
20954
  <select ng-model="selection" ng-options="item for item in items">
20797
20955
  </select>
20798
20956
  <tt>selection={{selection}}</tt>
@@ -20806,10 +20964,11 @@ var ngStyleDirective = ngDirective(function(scope, element, attr) {
20806
20964
  </div>
20807
20965
  </file>
20808
20966
  <file name="script.js">
20809
- function Ctrl($scope) {
20810
- $scope.items = ['settings', 'home', 'other'];
20811
- $scope.selection = $scope.items[0];
20812
- }
20967
+ angular.module('switchExample', ['ngAnimate'])
20968
+ .controller('ExampleController', ['$scope', function($scope) {
20969
+ $scope.items = ['settings', 'home', 'other'];
20970
+ $scope.selection = $scope.items[0];
20971
+ }]);
20813
20972
  </file>
20814
20973
  <file name="animations.css">
20815
20974
  .animate-switch-container {
@@ -20852,11 +21011,11 @@ var ngStyleDirective = ngDirective(function(scope, element, attr) {
20852
21011
  expect(switchElem.getText()).toMatch(/Settings Div/);
20853
21012
  });
20854
21013
  it('should change to home', function() {
20855
- select.element.all(by.css('option')).get(1).click();
21014
+ select.all(by.css('option')).get(1).click();
20856
21015
  expect(switchElem.getText()).toMatch(/Home Span/);
20857
21016
  });
20858
21017
  it('should select default', function() {
20859
- select.element.all(by.css('option')).get(2).click();
21018
+ select.all(by.css('option')).get(2).click();
20860
21019
  expect(switchElem.getText()).toMatch(/default/);
20861
21020
  });
20862
21021
  </file>
@@ -20948,15 +21107,10 @@ var ngSwitchDefaultDirective = ngDirective({
20948
21107
  * @element ANY
20949
21108
  *
20950
21109
  * @example
20951
- <example module="transclude">
21110
+ <example module="transcludeExample">
20952
21111
  <file name="index.html">
20953
21112
  <script>
20954
- function Ctrl($scope) {
20955
- $scope.title = 'Lorem Ipsum';
20956
- $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
20957
- }
20958
-
20959
- angular.module('transclude', [])
21113
+ angular.module('transcludeExample', [])
20960
21114
  .directive('pane', function(){
20961
21115
  return {
20962
21116
  restrict: 'E',
@@ -20967,9 +21121,13 @@ var ngSwitchDefaultDirective = ngDirective({
20967
21121
  '<div ng-transclude></div>' +
20968
21122
  '</div>'
20969
21123
  };
20970
- });
21124
+ })
21125
+ .controller('ExampleController', ['$scope', function($scope) {
21126
+ $scope.title = 'Lorem Ipsum';
21127
+ $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
21128
+ }]);
20971
21129
  </script>
20972
- <div ng-controller="Ctrl">
21130
+ <div ng-controller="ExampleController">
20973
21131
  <input ng-model="title"><br>
20974
21132
  <textarea ng-model="text"></textarea> <br/>
20975
21133
  <pane title="{{title}}">{{text}}</pane>
@@ -21128,21 +21286,22 @@ var ngOptionsMinErr = minErr('ngOptions');
21128
21286
  * `value` variable (e.g. `value.propertyName`).
21129
21287
  *
21130
21288
  * @example
21131
- <example>
21289
+ <example module="selectExample">
21132
21290
  <file name="index.html">
21133
21291
  <script>
21134
- function MyCntrl($scope) {
21135
- $scope.colors = [
21136
- {name:'black', shade:'dark'},
21137
- {name:'white', shade:'light'},
21138
- {name:'red', shade:'dark'},
21139
- {name:'blue', shade:'dark'},
21140
- {name:'yellow', shade:'light'}
21141
- ];
21142
- $scope.myColor = $scope.colors[2]; // red
21143
- }
21292
+ angular.module('selectExample', [])
21293
+ .controller('ExampleController', ['$scope', function($scope) {
21294
+ $scope.colors = [
21295
+ {name:'black', shade:'dark'},
21296
+ {name:'white', shade:'light'},
21297
+ {name:'red', shade:'dark'},
21298
+ {name:'blue', shade:'dark'},
21299
+ {name:'yellow', shade:'light'}
21300
+ ];
21301
+ $scope.myColor = $scope.colors[2]; // red
21302
+ }]);
21144
21303
  </script>
21145
- <div ng-controller="MyCntrl">
21304
+ <div ng-controller="ExampleController">
21146
21305
  <ul>
21147
21306
  <li ng-repeat="color in colors">
21148
21307
  Name: <input ng-model="color.name">
@@ -21179,7 +21338,7 @@ var ngOptionsMinErr = minErr('ngOptions');
21179
21338
  <file name="protractor.js" type="protractor">
21180
21339
  it('should check ng-options', function() {
21181
21340
  expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('red');
21182
- element.all(by.select('myColor')).first().click();
21341
+ element.all(by.model('myColor')).first().click();
21183
21342
  element.all(by.css('select[ng-model="myColor"] option')).first().click();
21184
21343
  expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('black');
21185
21344
  element(by.css('.nullable select[ng-model="myColor"]')).click();
@@ -21595,6 +21754,12 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
21595
21754
  // lastElement.prop('selected') provided by jQuery has side-effects
21596
21755
  if (existingOption.selected !== option.selected) {
21597
21756
  lastElement.prop('selected', (existingOption.selected = option.selected));
21757
+ if (msie) {
21758
+ // See #7692
21759
+ // The selected item wouldn't visually update on IE without this.
21760
+ // Tested on Win7: IE9, IE10 and IE11. Future IEs should be tested as well
21761
+ lastElement.prop('selected', existingOption.selected);
21762
+ }
21598
21763
  }
21599
21764
  } else {
21600
21765
  // grow elements
@@ -21609,7 +21774,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
21609
21774
  // rather then the element.
21610
21775
  (element = optionTemplate.clone())
21611
21776
  .val(option.id)
21612
- .attr('selected', option.selected)
21777
+ .prop('selected', option.selected)
21613
21778
  .text(option.label);
21614
21779
  }
21615
21780