angularjs-rails 1.4.8 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,6 @@
1
1
  /**
2
- * @license AngularJS v1.4.8
3
- * (c) 2010-2015 Google, Inc. http://angularjs.org
2
+ * @license AngularJS v1.5.0
3
+ * (c) 2010-2016 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
6
6
  (function(window, angular, undefined) {'use strict';
@@ -21,19 +21,23 @@
21
21
  *
22
22
  * For ngAria to do its magic, simply include the module `ngAria` as a dependency. The following
23
23
  * directives are supported:
24
- * `ngModel`, `ngDisabled`, `ngShow`, `ngHide`, `ngClick`, `ngDblClick`, and `ngMessages`.
24
+ * `ngModel`, `ngChecked`, `ngRequired`, `ngValue`, `ngDisabled`, `ngShow`, `ngHide`, `ngClick`,
25
+ * `ngDblClick`, and `ngMessages`.
25
26
  *
26
27
  * Below is a more detailed breakdown of the attributes handled by ngAria:
27
28
  *
28
29
  * | Directive | Supported Attributes |
29
30
  * |---------------------------------------------|----------------------------------------------------------------------------------------|
31
+ * | {@link ng.directive:ngModel ngModel} | aria-checked, aria-valuemin, aria-valuemax, aria-valuenow, aria-invalid, aria-required, input roles |
30
32
  * | {@link ng.directive:ngDisabled ngDisabled} | aria-disabled |
33
+ * | {@link ng.directive:ngRequired ngRequired} | aria-required |
34
+ * | {@link ng.directive:ngChecked ngChecked} | aria-checked |
35
+ * | {@link ng.directive:ngValue ngValue} | aria-checked |
31
36
  * | {@link ng.directive:ngShow ngShow} | aria-hidden |
32
37
  * | {@link ng.directive:ngHide ngHide} | aria-hidden |
33
38
  * | {@link ng.directive:ngDblclick ngDblclick} | tabindex |
34
39
  * | {@link module:ngMessages ngMessages} | aria-live |
35
- * | {@link ng.directive:ngModel ngModel} | aria-checked, aria-valuemin, aria-valuemax, aria-valuenow, aria-invalid, aria-required, input roles |
36
- * | {@link ng.directive:ngClick ngClick} | tabindex, keypress event, button role |
40
+ * | {@link ng.directive:ngClick ngClick} | tabindex, keypress event, button role |
37
41
  *
38
42
  * Find out more information about each directive by reading the
39
43
  * {@link guide/accessibility ngAria Developer Guide}.
@@ -95,7 +99,6 @@ function $AriaProvider() {
95
99
  ariaDisabled: true,
96
100
  ariaRequired: true,
97
101
  ariaInvalid: true,
98
- ariaMultiline: true,
99
102
  ariaValue: true,
100
103
  tabindex: true,
101
104
  bindKeypress: true,
@@ -113,11 +116,10 @@ function $AriaProvider() {
113
116
  * - **ariaDisabled** – `{boolean}` – Enables/disables aria-disabled tags
114
117
  * - **ariaRequired** – `{boolean}` – Enables/disables aria-required tags
115
118
  * - **ariaInvalid** – `{boolean}` – Enables/disables aria-invalid tags
116
- * - **ariaMultiline** – `{boolean}` – Enables/disables aria-multiline tags
117
119
  * - **ariaValue** – `{boolean}` – Enables/disables aria-valuemin, aria-valuemax and aria-valuenow tags
118
120
  * - **tabindex** – `{boolean}` – Enables/disables tabindex tags
119
- * - **bindKeypress** – `{boolean}` – Enables/disables keypress event binding on `<div>` and
120
- * `<li>` elements with ng-click
121
+ * - **bindKeypress** – `{boolean}` – Enables/disables keypress event binding on `div` and
122
+ * `li` elements with ng-click
121
123
  * - **bindRoleForClick** – `{boolean}` – Adds role=button to non-interactive elements like `div`
122
124
  * using ng-click, making them more accessible to users of assistive technologies
123
125
  *
@@ -156,15 +158,15 @@ function $AriaProvider() {
156
158
  *
157
159
  *```js
158
160
  * ngAriaModule.directive('ngDisabled', ['$aria', function($aria) {
159
- * return $aria.$$watchExpr('ngDisabled', 'aria-disabled');
161
+ * return $aria.$$watchExpr('ngDisabled', 'aria-disabled', nodeBlackList, false);
160
162
  * }])
161
163
  *```
162
164
  * Shown above, the ngAria module creates a directive with the same signature as the
163
165
  * traditional `ng-disabled` directive. But this ngAria version is dedicated to
164
- * solely managing accessibility attributes. The internal `$aria` service is used to watch the
165
- * boolean attribute `ngDisabled`. If it has not been explicitly set by the developer,
166
- * `aria-disabled` is injected as an attribute with its value synchronized to the value in
167
- * `ngDisabled`.
166
+ * solely managing accessibility attributes on custom elements. The internal `$aria` service is
167
+ * used to watch the boolean attribute `ngDisabled`. If it has not been explicitly set by the
168
+ * developer, `aria-disabled` is injected as an attribute with its value synchronized to the
169
+ * value in `ngDisabled`.
168
170
  *
169
171
  * Because ngAria hooks into the `ng-disabled` directive, developers do not have to do
170
172
  * anything to enable this feature. The `aria-disabled` attribute is automatically managed
@@ -172,12 +174,15 @@ function $AriaProvider() {
172
174
  *
173
175
  * The full list of directives that interface with ngAria:
174
176
  * * **ngModel**
177
+ * * **ngChecked**
178
+ * * **ngRequired**
179
+ * * **ngDisabled**
180
+ * * **ngValue**
175
181
  * * **ngShow**
176
182
  * * **ngHide**
177
183
  * * **ngClick**
178
184
  * * **ngDblclick**
179
185
  * * **ngMessages**
180
- * * **ngDisabled**
181
186
  *
182
187
  * Read the {@link guide/accessibility ngAria Developer Guide} for a thorough explanation of each
183
188
  * directive.
@@ -203,13 +208,25 @@ ngAriaModule.directive('ngShow', ['$aria', function($aria) {
203
208
  .directive('ngHide', ['$aria', function($aria) {
204
209
  return $aria.$$watchExpr('ngHide', 'aria-hidden', [], false);
205
210
  }])
211
+ .directive('ngValue', ['$aria', function($aria) {
212
+ return $aria.$$watchExpr('ngValue', 'aria-checked', nodeBlackList, false);
213
+ }])
214
+ .directive('ngChecked', ['$aria', function($aria) {
215
+ return $aria.$$watchExpr('ngChecked', 'aria-checked', nodeBlackList, false);
216
+ }])
217
+ .directive('ngRequired', ['$aria', function($aria) {
218
+ return $aria.$$watchExpr('ngRequired', 'aria-required', nodeBlackList, false);
219
+ }])
206
220
  .directive('ngModel', ['$aria', function($aria) {
207
221
 
208
- function shouldAttachAttr(attr, normalizedAttr, elem) {
209
- return $aria.config(normalizedAttr) && !elem.attr(attr);
222
+ function shouldAttachAttr(attr, normalizedAttr, elem, allowBlacklistEls) {
223
+ return $aria.config(normalizedAttr) && !elem.attr(attr) && (allowBlacklistEls || !isNodeOneOf(elem, nodeBlackList));
210
224
  }
211
225
 
212
226
  function shouldAttachRole(role, elem) {
227
+ // if element does not have role attribute
228
+ // AND element type is equal to role (if custom element has a type equaling shape) <-- remove?
229
+ // AND element is not INPUT
213
230
  return !elem.attr('role') && (elem.attr('type') === role) && (elem[0].nodeName !== 'INPUT');
214
231
  }
215
232
 
@@ -219,20 +236,19 @@ ngAriaModule.directive('ngShow', ['$aria', function($aria) {
219
236
 
220
237
  return ((type || role) === 'checkbox' || role === 'menuitemcheckbox') ? 'checkbox' :
221
238
  ((type || role) === 'radio' || role === 'menuitemradio') ? 'radio' :
222
- (type === 'range' || role === 'progressbar' || role === 'slider') ? 'range' :
223
- (type || role) === 'textbox' || elem[0].nodeName === 'TEXTAREA' ? 'multiline' : '';
239
+ (type === 'range' || role === 'progressbar' || role === 'slider') ? 'range' : '';
224
240
  }
225
241
 
226
242
  return {
227
243
  restrict: 'A',
228
- require: '?ngModel',
244
+ require: 'ngModel',
229
245
  priority: 200, //Make sure watches are fired after any other directives that affect the ngModel value
230
246
  compile: function(elem, attr) {
231
247
  var shape = getShape(attr, elem);
232
248
 
233
249
  return {
234
250
  pre: function(scope, elem, attr, ngModel) {
235
- if (shape === 'checkbox' && attr.type !== 'checkbox') {
251
+ if (shape === 'checkbox') {
236
252
  //Use the input[checkbox] $isEmpty implementation for elements with checkbox roles
237
253
  ngModel.$isEmpty = function(value) {
238
254
  return value === false;
@@ -240,29 +256,18 @@ ngAriaModule.directive('ngShow', ['$aria', function($aria) {
240
256
  }
241
257
  },
242
258
  post: function(scope, elem, attr, ngModel) {
243
- var needsTabIndex = shouldAttachAttr('tabindex', 'tabindex', elem)
244
- && !isNodeOneOf(elem, nodeBlackList);
259
+ var needsTabIndex = shouldAttachAttr('tabindex', 'tabindex', elem, false);
245
260
 
246
261
  function ngAriaWatchModelValue() {
247
262
  return ngModel.$modelValue;
248
263
  }
249
264
 
250
- function getRadioReaction() {
251
- if (needsTabIndex) {
252
- needsTabIndex = false;
253
- return function ngAriaRadioReaction(newVal) {
254
- var boolVal = (attr.value == ngModel.$viewValue);
255
- elem.attr('aria-checked', boolVal);
256
- elem.attr('tabindex', 0 - !boolVal);
257
- };
258
- } else {
259
- return function ngAriaRadioReaction(newVal) {
260
- elem.attr('aria-checked', (attr.value == ngModel.$viewValue));
261
- };
262
- }
265
+ function getRadioReaction(newVal) {
266
+ var boolVal = (attr.value == ngModel.$viewValue);
267
+ elem.attr('aria-checked', boolVal);
263
268
  }
264
269
 
265
- function ngAriaCheckboxReaction() {
270
+ function getCheckboxReaction() {
266
271
  elem.attr('aria-checked', !ngModel.$isEmpty(ngModel.$viewValue));
267
272
  }
268
273
 
@@ -272,9 +277,9 @@ ngAriaModule.directive('ngShow', ['$aria', function($aria) {
272
277
  if (shouldAttachRole(shape, elem)) {
273
278
  elem.attr('role', shape);
274
279
  }
275
- if (shouldAttachAttr('aria-checked', 'ariaChecked', elem)) {
280
+ if (shouldAttachAttr('aria-checked', 'ariaChecked', elem, false)) {
276
281
  scope.$watch(ngAriaWatchModelValue, shape === 'radio' ?
277
- getRadioReaction() : ngAriaCheckboxReaction);
282
+ getRadioReaction : getCheckboxReaction);
278
283
  }
279
284
  if (needsTabIndex) {
280
285
  elem.attr('tabindex', 0);
@@ -311,22 +316,17 @@ ngAriaModule.directive('ngShow', ['$aria', function($aria) {
311
316
  elem.attr('tabindex', 0);
312
317
  }
313
318
  break;
314
- case 'multiline':
315
- if (shouldAttachAttr('aria-multiline', 'ariaMultiline', elem)) {
316
- elem.attr('aria-multiline', true);
317
- }
318
- break;
319
319
  }
320
320
 
321
- if (ngModel.$validators.required && shouldAttachAttr('aria-required', 'ariaRequired', elem)) {
322
- scope.$watch(function ngAriaRequiredWatch() {
323
- return ngModel.$error.required;
324
- }, function ngAriaRequiredReaction(newVal) {
325
- elem.attr('aria-required', !!newVal);
321
+ if (!attr.hasOwnProperty('ngRequired') && ngModel.$validators.required
322
+ && shouldAttachAttr('aria-required', 'ariaRequired', elem, false)) {
323
+ // ngModel.$error.required is undefined on custom controls
324
+ attr.$observe('required', function() {
325
+ elem.attr('aria-required', !!attr['required']);
326
326
  });
327
327
  }
328
328
 
329
- if (shouldAttachAttr('aria-invalid', 'ariaInvalid', elem)) {
329
+ if (shouldAttachAttr('aria-invalid', 'ariaInvalid', elem, true)) {
330
330
  scope.$watch(function ngAriaInvalidWatch() {
331
331
  return ngModel.$invalid;
332
332
  }, function ngAriaInvalidReaction(newVal) {
@@ -339,7 +339,7 @@ ngAriaModule.directive('ngShow', ['$aria', function($aria) {
339
339
  };
340
340
  }])
341
341
  .directive('ngDisabled', ['$aria', function($aria) {
342
- return $aria.$$watchExpr('ngDisabled', 'aria-disabled', []);
342
+ return $aria.$$watchExpr('ngDisabled', 'aria-disabled', nodeBlackList, false);
343
343
  }])
344
344
  .directive('ngMessages', function() {
345
345
  return {
@@ -1,6 +1,6 @@
1
1
  /**
2
- * @license AngularJS v1.4.8
3
- * (c) 2010-2015 Google, Inc. http://angularjs.org
2
+ * @license AngularJS v1.5.0
3
+ * (c) 2010-2016 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
6
6
  (function(window, angular, undefined) {'use strict';
@@ -39,16 +39,17 @@ angular.module('ngCookies', ['ng']).
39
39
  * The object may have following properties:
40
40
  *
41
41
  * - **path** - `{string}` - The cookie will be available only for this path and its
42
- * sub-paths. By default, this would be the URL that appears in your base tag.
42
+ * sub-paths. By default, this is the URL that appears in your `<base>` tag.
43
43
  * - **domain** - `{string}` - The cookie will be available only for this domain and
44
- * its sub-domains. For obvious security reasons the user agent will not accept the
45
- * cookie if the current domain is not a sub domain or equals to the requested domain.
44
+ * its sub-domains. For security reasons the user agent will not accept the cookie
45
+ * if the current domain is not a sub-domain of this domain or equal to it.
46
46
  * - **expires** - `{string|Date}` - String of the form "Wdy, DD Mon YYYY HH:MM:SS GMT"
47
47
  * or a Date object indicating the exact date/time this cookie will expire.
48
- * - **secure** - `{boolean}` - The cookie will be available only in secured connection.
48
+ * - **secure** - `{boolean}` - If `true`, then the cookie will only be available through a
49
+ * secured connection.
49
50
  *
50
- * Note: by default the address that appears in your `<base>` tag will be used as path.
51
- * This is important so that cookies will be visible for all routes in case html5mode is enabled
51
+ * Note: By default, the address that appears in your `<base>` tag will be used as the path.
52
+ * This is important so that cookies will be visible for all routes when html5mode is enabled.
52
53
  *
53
54
  **/
54
55
  var defaults = this.defaults = {};
@@ -1,12 +1,40 @@
1
1
  /**
2
- * @license AngularJS v1.4.8
3
- * (c) 2010-2015 Google, Inc. http://angularjs.org
2
+ * @license AngularJS v1.5.0
3
+ * (c) 2010-2016 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
6
6
 
7
7
  (function() {'use strict';
8
8
  function isFunction(value) {return typeof value === 'function';};
9
9
 
10
+ /* global: toDebugString: true */
11
+
12
+ function serializeObject(obj) {
13
+ var seen = [];
14
+
15
+ return JSON.stringify(obj, function(key, val) {
16
+ val = toJsonReplacer(key, val);
17
+ if (isObject(val)) {
18
+
19
+ if (seen.indexOf(val) >= 0) return '...';
20
+
21
+ seen.push(val);
22
+ }
23
+ return val;
24
+ });
25
+ }
26
+
27
+ function toDebugString(obj) {
28
+ if (typeof obj === 'function') {
29
+ return obj.toString().replace(/ \{[\s\S]*$/, '');
30
+ } else if (isUndefined(obj)) {
31
+ return 'undefined';
32
+ } else if (typeof obj !== 'string') {
33
+ return serializeObject(obj);
34
+ }
35
+ return obj;
36
+ }
37
+
10
38
  /**
11
39
  * @description
12
40
  *
@@ -59,7 +87,7 @@ function minErr(module, ErrorConstructor) {
59
87
  return match;
60
88
  });
61
89
 
62
- message += '\nhttp://errors.angularjs.org/1.4.8/' +
90
+ message += '\nhttp://errors.angularjs.org/1.5.0/' +
63
91
  (module ? module + '/' : '') + code;
64
92
 
65
93
  for (i = SKIP_INDEXES, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
@@ -147,7 +175,7 @@ function setupModuleLoader(window) {
147
175
  * unspecified then the module is being retrieved for further configuration.
148
176
  * @param {Function=} configFn Optional configuration function for the module. Same as
149
177
  * {@link angular.Module#config Module#config()}.
150
- * @returns {module} new module with the {@link angular.Module} api.
178
+ * @returns {angular.Module} new module with the {@link angular.Module} api.
151
179
  */
152
180
  return function module(name, requires, configFn) {
153
181
  var assertNotHasOwnProperty = function(name, context) {
@@ -353,6 +381,19 @@ function setupModuleLoader(window) {
353
381
  */
354
382
  directive: invokeLaterAndSetModuleName('$compileProvider', 'directive'),
355
383
 
384
+ /**
385
+ * @ngdoc method
386
+ * @name angular.Module#component
387
+ * @module ng
388
+ * @param {string} name Name of the component in camel-case (i.e. myComp which will match as my-comp)
389
+ * @param {Object} options Component definition object (a simplified
390
+ * {@link ng.$compile#directive-definition-object directive definition object})
391
+ *
392
+ * @description
393
+ * See {@link ng.$compileProvider#component $compileProvider.component()}.
394
+ */
395
+ component: invokeLaterAndSetModuleName('$compileProvider', 'component'),
396
+
356
397
  /**
357
398
  * @ngdoc method
358
399
  * @name angular.Module#config
@@ -1,6 +1,6 @@
1
1
  /**
2
- * @license AngularJS v1.4.8
3
- * (c) 2010-2015 Google, Inc. http://angularjs.org
2
+ * @license AngularJS v1.5.0
3
+ * (c) 2010-2016 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
6
6
  (function(window, angular, undefined) {'use strict';
@@ -1,6 +1,6 @@
1
1
  /**
2
- * @license AngularJS v1.4.8
3
- * (c) 2010-2015 Google, Inc. http://angularjs.org
2
+ * @license AngularJS v1.5.0
3
+ * (c) 2010-2016 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
6
6
  (function(window, angular, undefined) {'use strict';
@@ -570,13 +570,14 @@ angular.module('ngMessages', [])
570
570
  *
571
571
  * @param {expression} ngMessage|when a string value corresponding to the message key.
572
572
  */
573
- .directive('ngMessage', ngMessageDirectiveFactory('AE'))
573
+ .directive('ngMessage', ngMessageDirectiveFactory())
574
574
 
575
575
 
576
576
  /**
577
577
  * @ngdoc directive
578
578
  * @name ngMessageExp
579
579
  * @restrict AE
580
+ * @priority 1
580
581
  * @scope
581
582
  *
582
583
  * @description
@@ -602,13 +603,14 @@ angular.module('ngMessages', [])
602
603
  *
603
604
  * @param {expression} ngMessageExp|whenExp an expression value corresponding to the message key.
604
605
  */
605
- .directive('ngMessageExp', ngMessageDirectiveFactory('A'));
606
+ .directive('ngMessageExp', ngMessageDirectiveFactory());
606
607
 
607
- function ngMessageDirectiveFactory(restrict) {
608
+ function ngMessageDirectiveFactory() {
608
609
  return ['$animate', function($animate) {
609
610
  return {
610
611
  restrict: 'AE',
611
612
  transclude: 'element',
613
+ priority: 1, // must run before ngBind, otherwise the text is set on the comment
612
614
  terminal: true,
613
615
  require: '^^ngMessages',
614
616
  link: function(scope, element, attrs, ngMessagesCtrl, $transclude) {
@@ -1,6 +1,6 @@
1
1
  /**
2
- * @license AngularJS v1.4.8
3
- * (c) 2010-2015 Google, Inc. http://angularjs.org
2
+ * @license AngularJS v1.5.0
3
+ * (c) 2010-2016 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
6
6
  (function(window, angular, undefined) {
@@ -758,6 +758,15 @@ angular.mock.TzDate = function(offset, timestamp) {
758
758
  angular.mock.TzDate.prototype = Date.prototype;
759
759
  /* jshint +W101 */
760
760
 
761
+
762
+ /**
763
+ * @ngdoc service
764
+ * @name $animate
765
+ *
766
+ * @description
767
+ * Mock implementation of the {@link ng.$animate `$animate`} service. Exposes two additional methods
768
+ * for testing animations.
769
+ */
761
770
  angular.mock.animate = angular.module('ngAnimateMock', ['ng'])
762
771
 
763
772
  .config(['$provide', function($provide) {
@@ -790,9 +799,50 @@ angular.mock.animate = angular.module('ngAnimateMock', ['ng'])
790
799
  return queueFn;
791
800
  });
792
801
 
793
- $provide.decorator('$animate', ['$delegate', '$timeout', '$browser', '$$rAF',
802
+ $provide.decorator('$$animateJs', ['$delegate', function($delegate) {
803
+ var runners = [];
804
+
805
+ var animateJsConstructor = function() {
806
+ var animator = $delegate.apply($delegate, arguments);
807
+ // If no javascript animation is found, animator is undefined
808
+ if (animator) {
809
+ runners.push(animator);
810
+ }
811
+ return animator;
812
+ };
813
+
814
+ animateJsConstructor.$closeAndFlush = function() {
815
+ runners.forEach(function(runner) {
816
+ runner.end();
817
+ });
818
+ runners = [];
819
+ };
820
+
821
+ return animateJsConstructor;
822
+ }]);
823
+
824
+ $provide.decorator('$animateCss', ['$delegate', function($delegate) {
825
+ var runners = [];
826
+
827
+ var animateCssConstructor = function(element, options) {
828
+ var animator = $delegate(element, options);
829
+ runners.push(animator);
830
+ return animator;
831
+ };
832
+
833
+ animateCssConstructor.$closeAndFlush = function() {
834
+ runners.forEach(function(runner) {
835
+ runner.end();
836
+ });
837
+ runners = [];
838
+ };
839
+
840
+ return animateCssConstructor;
841
+ }]);
842
+
843
+ $provide.decorator('$animate', ['$delegate', '$timeout', '$browser', '$$rAF', '$animateCss', '$$animateJs',
794
844
  '$$forceReflow', '$$animateAsyncRun', '$rootScope',
795
- function($delegate, $timeout, $browser, $$rAF,
845
+ function($delegate, $timeout, $browser, $$rAF, $animateCss, $$animateJs,
796
846
  $$forceReflow, $$animateAsyncRun, $rootScope) {
797
847
  var animate = {
798
848
  queue: [],
@@ -804,7 +854,35 @@ angular.mock.animate = angular.module('ngAnimateMock', ['ng'])
804
854
  return $$forceReflow.totalReflows;
805
855
  },
806
856
  enabled: $delegate.enabled,
807
- flush: function() {
857
+ /**
858
+ * @ngdoc method
859
+ * @name $animate#closeAndFlush
860
+ * @description
861
+ *
862
+ * This method will close all pending animations (both {@link ngAnimate#javascript-based-animations Javascript}
863
+ * and {@link ngAnimate.$animateCss CSS}) and it will also flush any remaining animation frames and/or callbacks.
864
+ */
865
+ closeAndFlush: function() {
866
+ // we allow the flush command to swallow the errors
867
+ // because depending on whether CSS or JS animations are
868
+ // used, there may not be a RAF flush. The primary flush
869
+ // at the end of this function must throw an exception
870
+ // because it will track if there were pending animations
871
+ this.flush(true);
872
+ $animateCss.$closeAndFlush();
873
+ $$animateJs.$closeAndFlush();
874
+ this.flush();
875
+ },
876
+ /**
877
+ * @ngdoc method
878
+ * @name $animate#flush
879
+ * @description
880
+ *
881
+ * This method is used to flush the pending callbacks and animation frames to either start
882
+ * an animation or conclude an animation. Note that this will not actually close an
883
+ * actively running animation (see {@link ngMock.$animate#closeAndFlush `closeAndFlush()`} for that).
884
+ */
885
+ flush: function(hideErrors) {
808
886
  $rootScope.$digest();
809
887
 
810
888
  var doNextRun, somethingFlushed = false;
@@ -821,7 +899,7 @@ angular.mock.animate = angular.module('ngAnimateMock', ['ng'])
821
899
  }
822
900
  } while (doNextRun);
823
901
 
824
- if (!somethingFlushed) {
902
+ if (!somethingFlushed && !hideErrors) {
825
903
  throw new Error('No pending animations ready to be closed or flushed');
826
904
  }
827
905
 
@@ -950,7 +1028,7 @@ angular.mock.dump = function(object) {
950
1028
  * - `$httpBackend.when` - specifies a backend definition
951
1029
  *
952
1030
  *
953
- * # Request Expectations vs Backend Definitions
1031
+ * ## Request Expectations vs Backend Definitions
954
1032
  *
955
1033
  * Request expectations provide a way to make assertions about requests made by the application and
956
1034
  * to define responses for those requests. The test will fail if the expected requests are not made
@@ -1006,7 +1084,7 @@ angular.mock.dump = function(object) {
1006
1084
  * the request. The response from the first matched definition is returned.
1007
1085
  *
1008
1086
  *
1009
- * # Flushing HTTP requests
1087
+ * ## Flushing HTTP requests
1010
1088
  *
1011
1089
  * The $httpBackend used in production always responds to requests asynchronously. If we preserved
1012
1090
  * this behavior in unit testing, we'd have to create async unit tests, which are hard to write,
@@ -1016,7 +1094,7 @@ angular.mock.dump = function(object) {
1016
1094
  * the async api of the backend, while allowing the test to execute synchronously.
1017
1095
  *
1018
1096
  *
1019
- * # Unit testing with mock $httpBackend
1097
+ * ## Unit testing with mock $httpBackend
1020
1098
  * The following code shows how to setup and use the mock backend when unit testing a controller.
1021
1099
  * First we create the controller under test:
1022
1100
  *
@@ -1030,18 +1108,18 @@ angular.mock.dump = function(object) {
1030
1108
  function MyController($scope, $http) {
1031
1109
  var authToken;
1032
1110
 
1033
- $http.get('/auth.py').success(function(data, status, headers) {
1034
- authToken = headers('A-Token');
1035
- $scope.user = data;
1111
+ $http.get('/auth.py').then(function(response) {
1112
+ authToken = response.headers('A-Token');
1113
+ $scope.user = response.data;
1036
1114
  });
1037
1115
 
1038
1116
  $scope.saveMessage = function(message) {
1039
1117
  var headers = { 'Authorization': authToken };
1040
1118
  $scope.status = 'Saving...';
1041
1119
 
1042
- $http.post('/add-msg.py', message, { headers: headers } ).success(function(response) {
1120
+ $http.post('/add-msg.py', message, { headers: headers } ).then(function(response) {
1043
1121
  $scope.status = '';
1044
- }).error(function() {
1122
+ }).catch(function() {
1045
1123
  $scope.status = 'Failed...';
1046
1124
  });
1047
1125
  };
@@ -1132,7 +1210,87 @@ angular.mock.dump = function(object) {
1132
1210
  $httpBackend.flush();
1133
1211
  });
1134
1212
  });
1135
- ```
1213
+ ```
1214
+ *
1215
+ * ## Dynamic responses
1216
+ *
1217
+ * You define a response to a request by chaining a call to `respond()` onto a definition or expectation.
1218
+ * If you provide a **callback** as the first parameter to `respond(callback)` then you can dynamically generate
1219
+ * a response based on the properties of the request.
1220
+ *
1221
+ * The `callback` function should be of the form `function(method, url, data, headers, params)`.
1222
+ *
1223
+ * ### Query parameters
1224
+ *
1225
+ * By default, query parameters on request URLs are parsed into the `params` object. So a request URL
1226
+ * of `/list?q=searchstr&orderby=-name` would set `params` to be `{q: 'searchstr', orderby: '-name'}`.
1227
+ *
1228
+ * ### Regex parameter matching
1229
+ *
1230
+ * If an expectation or definition uses a **regex** to match the URL, you can provide an array of **keys** via a
1231
+ * `params` argument. The index of each **key** in the array will match the index of a **group** in the
1232
+ * **regex**.
1233
+ *
1234
+ * The `params` object in the **callback** will now have properties with these keys, which hold the value of the
1235
+ * corresponding **group** in the **regex**.
1236
+ *
1237
+ * This also applies to the `when` and `expect` shortcut methods.
1238
+ *
1239
+ *
1240
+ * ```js
1241
+ * $httpBackend.expect('GET', /\/user\/(.+)/, undefined, undefined, ['id'])
1242
+ * .respond(function(method, url, data, headers, params) {
1243
+ * // for requested url of '/user/1234' params is {id: '1234'}
1244
+ * });
1245
+ *
1246
+ * $httpBackend.whenPATCH(/\/user\/(.+)\/article\/(.+)/, undefined, undefined, ['user', 'article'])
1247
+ * .respond(function(method, url, data, headers, params) {
1248
+ * // for url of '/user/1234/article/567' params is {user: '1234', article: '567'}
1249
+ * });
1250
+ * ```
1251
+ *
1252
+ * ## Matching route requests
1253
+ *
1254
+ * For extra convenience, `whenRoute` and `expectRoute` shortcuts are available. These methods offer colon
1255
+ * delimited matching of the url path, ignoring the query string. This allows declarations
1256
+ * similar to how application routes are configured with `$routeProvider`. Because these methods convert
1257
+ * the definition url to regex, declaration order is important. Combined with query parameter parsing,
1258
+ * the following is possible:
1259
+ *
1260
+ ```js
1261
+ $httpBackend.whenRoute('GET', '/users/:id')
1262
+ .respond(function(method, url, data, headers, params) {
1263
+ return [200, MockUserList[Number(params.id)]];
1264
+ });
1265
+
1266
+ $httpBackend.whenRoute('GET', '/users')
1267
+ .respond(function(method, url, data, headers, params) {
1268
+ var userList = angular.copy(MockUserList),
1269
+ defaultSort = 'lastName',
1270
+ count, pages, isPrevious, isNext;
1271
+
1272
+ // paged api response '/v1/users?page=2'
1273
+ params.page = Number(params.page) || 1;
1274
+
1275
+ // query for last names '/v1/users?q=Archer'
1276
+ if (params.q) {
1277
+ userList = $filter('filter')({lastName: params.q});
1278
+ }
1279
+
1280
+ pages = Math.ceil(userList.length / pagingLength);
1281
+ isPrevious = params.page > 1;
1282
+ isNext = params.page < pages;
1283
+
1284
+ return [200, {
1285
+ count: userList.length,
1286
+ previous: isPrevious,
1287
+ next: isNext,
1288
+ // sort field -> '/v1/users?sortBy=firstName'
1289
+ results: $filter('orderBy')(userList, params.sortBy || defaultSort)
1290
+ .splice((params.page - 1) * pagingLength, pagingLength)
1291
+ }];
1292
+ });
1293
+ ```
1136
1294
  */
1137
1295
  angular.mock.$HttpBackendProvider = function() {
1138
1296
  this.$get = ['$rootScope', '$timeout', createHttpBackendMock];
@@ -1189,7 +1347,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
1189
1347
  return handleResponse;
1190
1348
 
1191
1349
  function handleResponse() {
1192
- var response = wrapped.response(method, url, data, headers);
1350
+ var response = wrapped.response(method, url, data, headers, wrapped.params(url));
1193
1351
  xhr.$$respHeaders = response[2];
1194
1352
  callback(copy(response[0]), copy(response[1]), xhr.getAllResponseHeaders(),
1195
1353
  copy(response[3] || ''));
@@ -1258,20 +1416,21 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
1258
1416
  * data string and returns true if the data is as expected.
1259
1417
  * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
1260
1418
  * object and returns true if the headers match the current definition.
1419
+ * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1261
1420
  * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1262
1421
  * request is handled. You can save this object for later use and invoke `respond` again in
1263
1422
  * order to change how a matched request is handled.
1264
1423
  *
1265
1424
  * - respond –
1266
1425
  * `{function([status,] data[, headers, statusText])
1267
- * | function(function(method, url, data, headers)}`
1426
+ * | function(function(method, url, data, headers, params)}`
1268
1427
  * – The respond method takes a set of static data to be returned or a function that can
1269
1428
  * return an array containing response status (number), response data (string), response
1270
1429
  * headers (Object), and the text for the status (string). The respond method returns the
1271
1430
  * `requestHandler` object for possible overrides.
1272
1431
  */
1273
- $httpBackend.when = function(method, url, data, headers) {
1274
- var definition = new MockHttpExpectation(method, url, data, headers),
1432
+ $httpBackend.when = function(method, url, data, headers, keys) {
1433
+ var definition = new MockHttpExpectation(method, url, data, headers, keys),
1275
1434
  chain = {
1276
1435
  respond: function(status, data, headers, statusText) {
1277
1436
  definition.passThrough = undefined;
@@ -1301,6 +1460,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
1301
1460
  * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1302
1461
  * and returns true if the url matches the current definition.
1303
1462
  * @param {(Object|function(Object))=} headers HTTP headers.
1463
+ * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1304
1464
  * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1305
1465
  * request is handled. You can save this object for later use and invoke `respond` again in
1306
1466
  * order to change how a matched request is handled.
@@ -1315,6 +1475,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
1315
1475
  * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1316
1476
  * and returns true if the url matches the current definition.
1317
1477
  * @param {(Object|function(Object))=} headers HTTP headers.
1478
+ * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1318
1479
  * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1319
1480
  * request is handled. You can save this object for later use and invoke `respond` again in
1320
1481
  * order to change how a matched request is handled.
@@ -1329,6 +1490,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
1329
1490
  * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1330
1491
  * and returns true if the url matches the current definition.
1331
1492
  * @param {(Object|function(Object))=} headers HTTP headers.
1493
+ * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1332
1494
  * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1333
1495
  * request is handled. You can save this object for later use and invoke `respond` again in
1334
1496
  * order to change how a matched request is handled.
@@ -1345,6 +1507,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
1345
1507
  * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
1346
1508
  * data string and returns true if the data is as expected.
1347
1509
  * @param {(Object|function(Object))=} headers HTTP headers.
1510
+ * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1348
1511
  * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1349
1512
  * request is handled. You can save this object for later use and invoke `respond` again in
1350
1513
  * order to change how a matched request is handled.
@@ -1361,6 +1524,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
1361
1524
  * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
1362
1525
  * data string and returns true if the data is as expected.
1363
1526
  * @param {(Object|function(Object))=} headers HTTP headers.
1527
+ * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1364
1528
  * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1365
1529
  * request is handled. You can save this object for later use and invoke `respond` again in
1366
1530
  * order to change how a matched request is handled.
@@ -1374,12 +1538,59 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
1374
1538
  *
1375
1539
  * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1376
1540
  * and returns true if the url matches the current definition.
1541
+ * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1377
1542
  * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1378
1543
  * request is handled. You can save this object for later use and invoke `respond` again in
1379
1544
  * order to change how a matched request is handled.
1380
1545
  */
1381
1546
  createShortMethods('when');
1382
1547
 
1548
+ /**
1549
+ * @ngdoc method
1550
+ * @name $httpBackend#whenRoute
1551
+ * @description
1552
+ * Creates a new backend definition that compares only with the requested route.
1553
+ *
1554
+ * @param {string} method HTTP method.
1555
+ * @param {string} url HTTP url string that supports colon param matching.
1556
+ * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1557
+ * request is handled. You can save this object for later use and invoke `respond` again in
1558
+ * order to change how a matched request is handled. See #when for more info.
1559
+ */
1560
+ $httpBackend.whenRoute = function(method, url) {
1561
+ var pathObj = parseRoute(url);
1562
+ return $httpBackend.when(method, pathObj.regexp, undefined, undefined, pathObj.keys);
1563
+ };
1564
+
1565
+ function parseRoute(url) {
1566
+ var ret = {
1567
+ regexp: url
1568
+ },
1569
+ keys = ret.keys = [];
1570
+
1571
+ if (!url || !angular.isString(url)) return ret;
1572
+
1573
+ url = url
1574
+ .replace(/([().])/g, '\\$1')
1575
+ .replace(/(\/)?:(\w+)([\?\*])?/g, function(_, slash, key, option) {
1576
+ var optional = option === '?' ? option : null;
1577
+ var star = option === '*' ? option : null;
1578
+ keys.push({ name: key, optional: !!optional });
1579
+ slash = slash || '';
1580
+ return ''
1581
+ + (optional ? '' : slash)
1582
+ + '(?:'
1583
+ + (optional ? slash : '')
1584
+ + (star && '(.+?)' || '([^/]+)')
1585
+ + (optional || '')
1586
+ + ')'
1587
+ + (optional || '');
1588
+ })
1589
+ .replace(/([\/$\*])/g, '\\$1');
1590
+
1591
+ ret.regexp = new RegExp('^' + url, 'i');
1592
+ return ret;
1593
+ }
1383
1594
 
1384
1595
  /**
1385
1596
  * @ngdoc method
@@ -1395,20 +1606,21 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
1395
1606
  * is in JSON format.
1396
1607
  * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
1397
1608
  * object and returns true if the headers match the current expectation.
1609
+ * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1398
1610
  * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1399
1611
  * request is handled. You can save this object for later use and invoke `respond` again in
1400
1612
  * order to change how a matched request is handled.
1401
1613
  *
1402
1614
  * - respond –
1403
1615
  * `{function([status,] data[, headers, statusText])
1404
- * | function(function(method, url, data, headers)}`
1616
+ * | function(function(method, url, data, headers, params)}`
1405
1617
  * – The respond method takes a set of static data to be returned or a function that can
1406
1618
  * return an array containing response status (number), response data (string), response
1407
1619
  * headers (Object), and the text for the status (string). The respond method returns the
1408
1620
  * `requestHandler` object for possible overrides.
1409
1621
  */
1410
- $httpBackend.expect = function(method, url, data, headers) {
1411
- var expectation = new MockHttpExpectation(method, url, data, headers),
1622
+ $httpBackend.expect = function(method, url, data, headers, keys) {
1623
+ var expectation = new MockHttpExpectation(method, url, data, headers, keys),
1412
1624
  chain = {
1413
1625
  respond: function(status, data, headers, statusText) {
1414
1626
  expectation.response = createResponse(status, data, headers, statusText);
@@ -1420,7 +1632,6 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
1420
1632
  return chain;
1421
1633
  };
1422
1634
 
1423
-
1424
1635
  /**
1425
1636
  * @ngdoc method
1426
1637
  * @name $httpBackend#expectGET
@@ -1430,6 +1641,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
1430
1641
  * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1431
1642
  * and returns true if the url matches the current definition.
1432
1643
  * @param {Object=} headers HTTP headers.
1644
+ * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1433
1645
  * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1434
1646
  * request is handled. You can save this object for later use and invoke `respond` again in
1435
1647
  * order to change how a matched request is handled. See #expect for more info.
@@ -1444,6 +1656,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
1444
1656
  * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1445
1657
  * and returns true if the url matches the current definition.
1446
1658
  * @param {Object=} headers HTTP headers.
1659
+ * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1447
1660
  * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1448
1661
  * request is handled. You can save this object for later use and invoke `respond` again in
1449
1662
  * order to change how a matched request is handled.
@@ -1458,6 +1671,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
1458
1671
  * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1459
1672
  * and returns true if the url matches the current definition.
1460
1673
  * @param {Object=} headers HTTP headers.
1674
+ * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1461
1675
  * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1462
1676
  * request is handled. You can save this object for later use and invoke `respond` again in
1463
1677
  * order to change how a matched request is handled.
@@ -1475,6 +1689,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
1475
1689
  * receives data string and returns true if the data is as expected, or Object if request body
1476
1690
  * is in JSON format.
1477
1691
  * @param {Object=} headers HTTP headers.
1692
+ * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1478
1693
  * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1479
1694
  * request is handled. You can save this object for later use and invoke `respond` again in
1480
1695
  * order to change how a matched request is handled.
@@ -1492,6 +1707,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
1492
1707
  * receives data string and returns true if the data is as expected, or Object if request body
1493
1708
  * is in JSON format.
1494
1709
  * @param {Object=} headers HTTP headers.
1710
+ * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1495
1711
  * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1496
1712
  * request is handled. You can save this object for later use and invoke `respond` again in
1497
1713
  * order to change how a matched request is handled.
@@ -1509,6 +1725,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
1509
1725
  * receives data string and returns true if the data is as expected, or Object if request body
1510
1726
  * is in JSON format.
1511
1727
  * @param {Object=} headers HTTP headers.
1728
+ * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1512
1729
  * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1513
1730
  * request is handled. You can save this object for later use and invoke `respond` again in
1514
1731
  * order to change how a matched request is handled.
@@ -1522,12 +1739,30 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
1522
1739
  *
1523
1740
  * @param {string|RegExp|function(string)} url HTTP url or function that receives an url
1524
1741
  * and returns true if the url matches the current definition.
1742
+ * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
1525
1743
  * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1526
1744
  * request is handled. You can save this object for later use and invoke `respond` again in
1527
1745
  * order to change how a matched request is handled.
1528
1746
  */
1529
1747
  createShortMethods('expect');
1530
1748
 
1749
+ /**
1750
+ * @ngdoc method
1751
+ * @name $httpBackend#expectRoute
1752
+ * @description
1753
+ * Creates a new request expectation that compares only with the requested route.
1754
+ *
1755
+ * @param {string} method HTTP method.
1756
+ * @param {string} url HTTP url string that supports colon param matching.
1757
+ * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1758
+ * request is handled. You can save this object for later use and invoke `respond` again in
1759
+ * order to change how a matched request is handled. See #expect for more info.
1760
+ */
1761
+ $httpBackend.expectRoute = function(method, url) {
1762
+ var pathObj = parseRoute(url);
1763
+ return $httpBackend.expect(method, pathObj.regexp, undefined, undefined, pathObj.keys);
1764
+ };
1765
+
1531
1766
 
1532
1767
  /**
1533
1768
  * @ngdoc method
@@ -1617,20 +1852,20 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
1617
1852
 
1618
1853
  function createShortMethods(prefix) {
1619
1854
  angular.forEach(['GET', 'DELETE', 'JSONP', 'HEAD'], function(method) {
1620
- $httpBackend[prefix + method] = function(url, headers) {
1621
- return $httpBackend[prefix](method, url, undefined, headers);
1855
+ $httpBackend[prefix + method] = function(url, headers, keys) {
1856
+ return $httpBackend[prefix](method, url, undefined, headers, keys);
1622
1857
  };
1623
1858
  });
1624
1859
 
1625
1860
  angular.forEach(['PUT', 'POST', 'PATCH'], function(method) {
1626
- $httpBackend[prefix + method] = function(url, data, headers) {
1627
- return $httpBackend[prefix](method, url, data, headers);
1861
+ $httpBackend[prefix + method] = function(url, data, headers, keys) {
1862
+ return $httpBackend[prefix](method, url, data, headers, keys);
1628
1863
  };
1629
1864
  });
1630
1865
  }
1631
1866
  }
1632
1867
 
1633
- function MockHttpExpectation(method, url, data, headers) {
1868
+ function MockHttpExpectation(method, url, data, headers, keys) {
1634
1869
 
1635
1870
  this.data = data;
1636
1871
  this.headers = headers;
@@ -1669,6 +1904,59 @@ function MockHttpExpectation(method, url, data, headers) {
1669
1904
  this.toString = function() {
1670
1905
  return method + ' ' + url;
1671
1906
  };
1907
+
1908
+ this.params = function(u) {
1909
+ return angular.extend(parseQuery(), pathParams());
1910
+
1911
+ function pathParams() {
1912
+ var keyObj = {};
1913
+ if (!url || !angular.isFunction(url.test) || !keys || keys.length === 0) return keyObj;
1914
+
1915
+ var m = url.exec(u);
1916
+ if (!m) return keyObj;
1917
+ for (var i = 1, len = m.length; i < len; ++i) {
1918
+ var key = keys[i - 1];
1919
+ var val = m[i];
1920
+ if (key && val) {
1921
+ keyObj[key.name || key] = val;
1922
+ }
1923
+ }
1924
+
1925
+ return keyObj;
1926
+ }
1927
+
1928
+ function parseQuery() {
1929
+ var obj = {}, key_value, key,
1930
+ queryStr = u.indexOf('?') > -1
1931
+ ? u.substring(u.indexOf('?') + 1)
1932
+ : "";
1933
+
1934
+ angular.forEach(queryStr.split('&'), function(keyValue) {
1935
+ if (keyValue) {
1936
+ key_value = keyValue.replace(/\+/g,'%20').split('=');
1937
+ key = tryDecodeURIComponent(key_value[0]);
1938
+ if (angular.isDefined(key)) {
1939
+ var val = angular.isDefined(key_value[1]) ? tryDecodeURIComponent(key_value[1]) : true;
1940
+ if (!hasOwnProperty.call(obj, key)) {
1941
+ obj[key] = val;
1942
+ } else if (angular.isArray(obj[key])) {
1943
+ obj[key].push(val);
1944
+ } else {
1945
+ obj[key] = [obj[key],val];
1946
+ }
1947
+ }
1948
+ }
1949
+ });
1950
+ return obj;
1951
+ }
1952
+ function tryDecodeURIComponent(value) {
1953
+ try {
1954
+ return decodeURIComponent(value);
1955
+ } catch (e) {
1956
+ // Ignore any invalid uri component
1957
+ }
1958
+ }
1959
+ };
1672
1960
  }
1673
1961
 
1674
1962
  function createMockXhr() {
@@ -1837,7 +2125,7 @@ angular.mock.$RootElementProvider = function() {
1837
2125
  *
1838
2126
  * // Controller definition ...
1839
2127
  *
1840
- * myMod.controller('MyDirectiveController', ['log', function($log) {
2128
+ * myMod.controller('MyDirectiveController', ['$log', function($log) {
1841
2129
  * $log.info(this.name);
1842
2130
  * })];
1843
2131
  *
@@ -1883,6 +2171,49 @@ angular.mock.$ControllerDecorator = ['$delegate', function($delegate) {
1883
2171
  };
1884
2172
  }];
1885
2173
 
2174
+ /**
2175
+ * @ngdoc service
2176
+ * @name $componentController
2177
+ * @description
2178
+ * A service that can be used to create instances of component controllers.
2179
+ * <div class="alert alert-info">
2180
+ * Be aware that the controller will be instantiated and attached to the scope as specified in
2181
+ * the component definition object. That means that you must always provide a `$scope` object
2182
+ * in the `locals` param.
2183
+ * </div>
2184
+ * @param {string} componentName the name of the component whose controller we want to instantiate
2185
+ * @param {Object} locals Injection locals for Controller.
2186
+ * @param {Object=} bindings Properties to add to the controller before invoking the constructor. This is used
2187
+ * to simulate the `bindToController` feature and simplify certain kinds of tests.
2188
+ * @param {string=} ident Override the property name to use when attaching the controller to the scope.
2189
+ * @return {Object} Instance of requested controller.
2190
+ */
2191
+ angular.mock.$ComponentControllerProvider = ['$compileProvider', function($compileProvider) {
2192
+ return {
2193
+ $get: ['$controller','$injector', function($controller,$injector) {
2194
+ return function $componentController(componentName, locals, bindings, ident) {
2195
+ // get all directives associated to the component name
2196
+ var directives = $injector.get(componentName + 'Directive');
2197
+ // look for those directives that are components
2198
+ var candidateDirectives = directives.filter(function(directiveInfo) {
2199
+ // components have controller, controllerAs and restrict:'E'
2200
+ return directiveInfo.controller && directiveInfo.controllerAs && directiveInfo.restrict === 'E';
2201
+ });
2202
+ // check if valid directives found
2203
+ if (candidateDirectives.length === 0) {
2204
+ throw new Error('No component found');
2205
+ }
2206
+ if (candidateDirectives.length > 1) {
2207
+ throw new Error('Too many components found');
2208
+ }
2209
+ // get the info of the component
2210
+ var directiveInfo = candidateDirectives[0];
2211
+ return $controller(directiveInfo.controller, locals, bindings, ident || directiveInfo.controllerAs);
2212
+ };
2213
+ }]
2214
+ };
2215
+ }];
2216
+
1886
2217
 
1887
2218
  /**
1888
2219
  * @ngdoc module
@@ -1906,7 +2237,8 @@ angular.module('ngMock', ['ng']).provider({
1906
2237
  $log: angular.mock.$LogProvider,
1907
2238
  $interval: angular.mock.$IntervalProvider,
1908
2239
  $httpBackend: angular.mock.$HttpBackendProvider,
1909
- $rootElement: angular.mock.$RootElementProvider
2240
+ $rootElement: angular.mock.$RootElementProvider,
2241
+ $componentController: angular.mock.$ComponentControllerProvider
1910
2242
  }).config(['$provide', function($provide) {
1911
2243
  $provide.decorator('$timeout', angular.mock.$TimeoutDecorator);
1912
2244
  $provide.decorator('$$rAF', angular.mock.$RAFDecorator);
@@ -1993,13 +2325,15 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
1993
2325
  * @param {(string|RegExp)=} data HTTP request body.
1994
2326
  * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
1995
2327
  * object and returns true if the headers match the current definition.
2328
+ * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
2329
+ * {@link ngMock.$httpBackend $httpBackend mock}.
1996
2330
  * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1997
2331
  * control how a matched request is handled. You can save this object for later use and invoke
1998
2332
  * `respond` or `passThrough` again in order to change how a matched request is handled.
1999
2333
  *
2000
2334
  * - respond –
2001
2335
  * `{function([status,] data[, headers, statusText])
2002
- * | function(function(method, url, data, headers)}`
2336
+ * | function(function(method, url, data, headers, params)}`
2003
2337
  * – The respond method takes a set of static data to be returned or a function that can return
2004
2338
  * an array containing response status (number), response data (string), response headers
2005
2339
  * (Object), and the text for the status (string).
@@ -2019,6 +2353,8 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
2019
2353
  * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
2020
2354
  * and returns true if the url matches the current definition.
2021
2355
  * @param {(Object|function(Object))=} headers HTTP headers.
2356
+ * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
2357
+ * {@link ngMock.$httpBackend $httpBackend mock}.
2022
2358
  * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
2023
2359
  * control how a matched request is handled. You can save this object for later use and invoke
2024
2360
  * `respond` or `passThrough` again in order to change how a matched request is handled.
@@ -2034,6 +2370,8 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
2034
2370
  * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
2035
2371
  * and returns true if the url matches the current definition.
2036
2372
  * @param {(Object|function(Object))=} headers HTTP headers.
2373
+ * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
2374
+ * {@link ngMock.$httpBackend $httpBackend mock}.
2037
2375
  * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
2038
2376
  * control how a matched request is handled. You can save this object for later use and invoke
2039
2377
  * `respond` or `passThrough` again in order to change how a matched request is handled.
@@ -2049,6 +2387,8 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
2049
2387
  * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
2050
2388
  * and returns true if the url matches the current definition.
2051
2389
  * @param {(Object|function(Object))=} headers HTTP headers.
2390
+ * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
2391
+ * {@link ngMock.$httpBackend $httpBackend mock}.
2052
2392
  * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
2053
2393
  * control how a matched request is handled. You can save this object for later use and invoke
2054
2394
  * `respond` or `passThrough` again in order to change how a matched request is handled.
@@ -2065,6 +2405,8 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
2065
2405
  * and returns true if the url matches the current definition.
2066
2406
  * @param {(string|RegExp)=} data HTTP request body.
2067
2407
  * @param {(Object|function(Object))=} headers HTTP headers.
2408
+ * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
2409
+ * {@link ngMock.$httpBackend $httpBackend mock}.
2068
2410
  * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
2069
2411
  * control how a matched request is handled. You can save this object for later use and invoke
2070
2412
  * `respond` or `passThrough` again in order to change how a matched request is handled.
@@ -2081,6 +2423,8 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
2081
2423
  * and returns true if the url matches the current definition.
2082
2424
  * @param {(string|RegExp)=} data HTTP request body.
2083
2425
  * @param {(Object|function(Object))=} headers HTTP headers.
2426
+ * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
2427
+ * {@link ngMock.$httpBackend $httpBackend mock}.
2084
2428
  * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
2085
2429
  * control how a matched request is handled. You can save this object for later use and invoke
2086
2430
  * `respond` or `passThrough` again in order to change how a matched request is handled.
@@ -2097,6 +2441,8 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
2097
2441
  * and returns true if the url matches the current definition.
2098
2442
  * @param {(string|RegExp)=} data HTTP request body.
2099
2443
  * @param {(Object|function(Object))=} headers HTTP headers.
2444
+ * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
2445
+ * {@link ngMock.$httpBackend $httpBackend mock}.
2100
2446
  * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
2101
2447
  * control how a matched request is handled. You can save this object for later use and invoke
2102
2448
  * `respond` or `passThrough` again in order to change how a matched request is handled.
@@ -2111,6 +2457,21 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
2111
2457
  *
2112
2458
  * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
2113
2459
  * and returns true if the url matches the current definition.
2460
+ * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
2461
+ * {@link ngMock.$httpBackend $httpBackend mock}.
2462
+ * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
2463
+ * control how a matched request is handled. You can save this object for later use and invoke
2464
+ * `respond` or `passThrough` again in order to change how a matched request is handled.
2465
+ */
2466
+ /**
2467
+ * @ngdoc method
2468
+ * @name $httpBackend#whenRoute
2469
+ * @module ngMockE2E
2470
+ * @description
2471
+ * Creates a new backend definition that compares only with the requested route.
2472
+ *
2473
+ * @param {string} method HTTP method.
2474
+ * @param {string} url HTTP url string that supports colon param matching.
2114
2475
  * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
2115
2476
  * control how a matched request is handled. You can save this object for later use and invoke
2116
2477
  * `respond` or `passThrough` again in order to change how a matched request is handled.
@@ -2243,10 +2604,12 @@ if (window.jasmine || window.mocha) {
2243
2604
 
2244
2605
  currentSpec.$injector = null;
2245
2606
  currentSpec.$modules = null;
2607
+ currentSpec.$providerInjector = null;
2246
2608
  currentSpec = null;
2247
2609
 
2248
2610
  if (injector) {
2249
2611
  injector.get('$rootElement').off();
2612
+ injector.get('$rootScope').$destroy();
2250
2613
  }
2251
2614
 
2252
2615
  // clean up jquery's fragment cache
@@ -2290,16 +2653,21 @@ if (window.jasmine || window.mocha) {
2290
2653
  if (currentSpec.$injector) {
2291
2654
  throw new Error('Injector already created, can not register a module!');
2292
2655
  } else {
2293
- var modules = currentSpec.$modules || (currentSpec.$modules = []);
2656
+ var fn, modules = currentSpec.$modules || (currentSpec.$modules = []);
2294
2657
  angular.forEach(moduleFns, function(module) {
2295
2658
  if (angular.isObject(module) && !angular.isArray(module)) {
2296
- modules.push(function($provide) {
2659
+ fn = function($provide) {
2297
2660
  angular.forEach(module, function(value, key) {
2298
2661
  $provide.value(key, value);
2299
2662
  });
2300
- });
2663
+ };
2301
2664
  } else {
2302
- modules.push(module);
2665
+ fn = module;
2666
+ }
2667
+ if (currentSpec.$providerInjector) {
2668
+ currentSpec.$providerInjector.invoke(fn);
2669
+ } else {
2670
+ modules.push(fn);
2303
2671
  }
2304
2672
  });
2305
2673
  }
@@ -2413,6 +2781,9 @@ if (window.jasmine || window.mocha) {
2413
2781
  function workFn() {
2414
2782
  var modules = currentSpec.$modules || [];
2415
2783
  var strictDi = !!currentSpec.$injectorStrict;
2784
+ modules.unshift(function($injector) {
2785
+ currentSpec.$providerInjector = $injector;
2786
+ });
2416
2787
  modules.unshift('ngMock');
2417
2788
  modules.unshift('ng');
2418
2789
  var injector = currentSpec.$injector;