angularjs-rails 1.4.8 → 1.5.0

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,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;