angularjs-on-rails 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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