rolodex 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (131) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +25 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +25 -0
  6. data/Rakefile +1 -0
  7. data/karma.conf.js +87 -0
  8. data/lib/rolodex.rb +31 -0
  9. data/lib/rolodex/engine.rb +12 -0
  10. data/lib/rolodex/sass.rb +5 -0
  11. data/lib/rolodex/version.rb +3 -0
  12. data/package.json +33 -0
  13. data/rolodex.gemspec +29 -0
  14. data/spec/javascripts/rolodex_angular/modal.spec.coffee +13 -0
  15. data/spec/spec_helper.rb +0 -0
  16. data/spec/test_lib/angular-mocks.js +2173 -0
  17. data/spec/test_lib/angular.js +21883 -0
  18. data/spec/test_lib/jquery-2.1.1.js +9190 -0
  19. data/spec/test_lib/lodash.js +6785 -0
  20. data/vendor/assets/.keep +0 -0
  21. data/vendor/assets/images/alerts/icon-danger-dark.png +0 -0
  22. data/vendor/assets/images/alerts/icon-danger-dark.svg +9 -0
  23. data/vendor/assets/images/alerts/icon-danger-light.png +0 -0
  24. data/vendor/assets/images/alerts/icon-danger-light.svg +9 -0
  25. data/vendor/assets/images/alerts/icon-flop-dark.png +0 -0
  26. data/vendor/assets/images/alerts/icon-flop-dark.svg +11 -0
  27. data/vendor/assets/images/alerts/icon-flop-light.png +0 -0
  28. data/vendor/assets/images/alerts/icon-flop-light.svg +11 -0
  29. data/vendor/assets/images/alerts/icon-info-dark.png +0 -0
  30. data/vendor/assets/images/alerts/icon-info-dark.svg +9 -0
  31. data/vendor/assets/images/alerts/icon-info-light.png +0 -0
  32. data/vendor/assets/images/alerts/icon-info-light.svg +9 -0
  33. data/vendor/assets/images/alerts/icon-tick-dark.png +0 -0
  34. data/vendor/assets/images/alerts/icon-tick-dark.svg +9 -0
  35. data/vendor/assets/images/alerts/icon-tick-light.png +0 -0
  36. data/vendor/assets/images/alerts/icon-tick-light.svg +9 -0
  37. data/vendor/assets/images/forms/checkbox.png +0 -0
  38. data/vendor/assets/images/forms/checkbox.svg +13 -0
  39. data/vendor/assets/images/forms/icon-calendar.png +0 -0
  40. data/vendor/assets/images/forms/icon-calendar.svg +20 -0
  41. data/vendor/assets/images/forms/icon-search.png +0 -0
  42. data/vendor/assets/images/forms/icon-search.svg +13 -0
  43. data/vendor/assets/images/forms/icon-validating.gif +0 -0
  44. data/vendor/assets/images/forms/icon-validating.psd +0 -0
  45. data/vendor/assets/images/forms/radio-button.png +0 -0
  46. data/vendor/assets/images/forms/radio-button.svg +12 -0
  47. data/vendor/assets/images/icons/icon-caret.png +0 -0
  48. data/vendor/assets/images/icons/icon-caret.svg +24 -0
  49. data/vendor/assets/images/icons/icon-close-x.png +0 -0
  50. data/vendor/assets/images/icons/icon-close-x.svg +9 -0
  51. data/vendor/assets/images/icons/icon-expand-gray.png +0 -0
  52. data/vendor/assets/images/icons/icon-expand-gray.svg +11 -0
  53. data/vendor/assets/images/icons/icon-expand-white.png +0 -0
  54. data/vendor/assets/images/icons/icon-expand-white.svg +11 -0
  55. data/vendor/assets/images/icons/icon-image.png +0 -0
  56. data/vendor/assets/images/icons/icon-image.svg +12 -0
  57. data/vendor/assets/images/icons/icon-invalid.png +0 -0
  58. data/vendor/assets/images/icons/icon-invalid.svg +14 -0
  59. data/vendor/assets/images/icons/icon-plane.png +0 -0
  60. data/vendor/assets/images/icons/icon-plane.svg +15 -0
  61. data/vendor/assets/images/icons/icon-star-banner.png +0 -0
  62. data/vendor/assets/images/icons/icon-star-banner.svg +16 -0
  63. data/vendor/assets/images/icons/icon-suggest.png +0 -0
  64. data/vendor/assets/images/icons/icon-suggest.svg +15 -0
  65. data/vendor/assets/images/icons/icon-tick.png +0 -0
  66. data/vendor/assets/images/icons/icon-tick.svg +9 -0
  67. data/vendor/assets/images/icons/icon-valid.png +0 -0
  68. data/vendor/assets/images/icons/icon-valid.svg +9 -0
  69. data/vendor/assets/images/loading/loading.png +0 -0
  70. data/vendor/assets/images/loading/loading.svg +23 -0
  71. data/vendor/assets/images/logo/belly-logo.png +0 -0
  72. data/vendor/assets/images/logo/belly-logo.svg +19 -0
  73. data/vendor/assets/images/tooltips/tooltip-star.png +0 -0
  74. data/vendor/assets/images/tooltips/tooltip-star.svg +20 -0
  75. data/vendor/assets/javascripts/rolodex_angular.coffee +3 -0
  76. data/vendor/assets/javascripts/rolodex_angular/README.md +68 -0
  77. data/vendor/assets/javascripts/rolodex_angular/rolodex.coffee +11 -0
  78. data/vendor/assets/javascripts/rolodex_angular/src/accordion.coffee +100 -0
  79. data/vendor/assets/javascripts/rolodex_angular/src/alert.coffee +17 -0
  80. data/vendor/assets/javascripts/rolodex_angular/src/buttons.coffee +59 -0
  81. data/vendor/assets/javascripts/rolodex_angular/src/collapse.coffee +65 -0
  82. data/vendor/assets/javascripts/rolodex_angular/src/dateparser.coffee +125 -0
  83. data/vendor/assets/javascripts/rolodex_angular/src/datepicker.coffee +547 -0
  84. data/vendor/assets/javascripts/rolodex_angular/src/dropdown.coffee +143 -0
  85. data/vendor/assets/javascripts/rolodex_angular/src/modal.coffee +331 -0
  86. data/vendor/assets/javascripts/rolodex_angular/src/position.coffee +128 -0
  87. data/vendor/assets/javascripts/rolodex_angular/src/transition.coffee +73 -0
  88. data/vendor/assets/javascripts/rolodex_angular/template/accordion/accordion-group.ngt.haml +7 -0
  89. data/vendor/assets/javascripts/rolodex_angular/template/accordion/accordion.ngt.haml +1 -0
  90. data/vendor/assets/javascripts/rolodex_angular/template/alert/alert.ngt.haml +3 -0
  91. data/vendor/assets/javascripts/rolodex_angular/template/datepicker/datepicker.ngt.haml +4 -0
  92. data/vendor/assets/javascripts/rolodex_angular/template/datepicker/day.ngt.haml +20 -0
  93. data/vendor/assets/javascripts/rolodex_angular/template/datepicker/month.ngt.haml +17 -0
  94. data/vendor/assets/javascripts/rolodex_angular/template/datepicker/popup.ngt.haml +2 -0
  95. data/vendor/assets/javascripts/rolodex_angular/template/datepicker/year.ngt.haml +17 -0
  96. data/vendor/assets/javascripts/rolodex_angular/template/modal/window.ngt.haml +2 -0
  97. data/vendor/assets/javascripts/vendor/jquery-1.10.2.min.js +6 -0
  98. data/vendor/assets/javascripts/vendor/modernizr-2.6.2.min.js +4 -0
  99. data/vendor/assets/stylesheets/rolodex.css.sass +26 -0
  100. data/vendor/assets/stylesheets/rolodex/base/_layout.sass +58 -0
  101. data/vendor/assets/stylesheets/rolodex/base/_typography.sass +123 -0
  102. data/vendor/assets/stylesheets/rolodex/components/_alerts.sass +112 -0
  103. data/vendor/assets/stylesheets/rolodex/components/_buttons.sass +103 -0
  104. data/vendor/assets/stylesheets/rolodex/components/_dropdowns.sass +121 -0
  105. data/vendor/assets/stylesheets/rolodex/components/_forms.sass +326 -0
  106. data/vendor/assets/stylesheets/rolodex/components/_icons.sass +76 -0
  107. data/vendor/assets/stylesheets/rolodex/components/_labels.sass +29 -0
  108. data/vendor/assets/stylesheets/rolodex/components/_lists.sass +15 -0
  109. data/vendor/assets/stylesheets/rolodex/components/_loading.sass +32 -0
  110. data/vendor/assets/stylesheets/rolodex/components/_media.sass +14 -0
  111. data/vendor/assets/stylesheets/rolodex/components/_modals.sass +83 -0
  112. data/vendor/assets/stylesheets/rolodex/components/_tables.sass +9 -0
  113. data/vendor/assets/stylesheets/rolodex/components/_tooltips.sass +123 -0
  114. data/vendor/assets/stylesheets/rolodex/settings/_settings.sass +33 -0
  115. data/vendor/assets/stylesheets/rolodex/settings/mixins/_bump.sass +51 -0
  116. data/vendor/assets/stylesheets/rolodex/settings/mixins/_icons.sass +17 -0
  117. data/vendor/assets/stylesheets/rolodex/settings/mixins/_layout.sass +52 -0
  118. data/vendor/assets/stylesheets/rolodex/settings/mixins/_rems.sass +41 -0
  119. data/vendor/assets/stylesheets/rolodex/settings/mixins/_svg.sass +10 -0
  120. data/vendor/assets/stylesheets/rolodex/settings/mixins/_typography.sass +65 -0
  121. data/vendor/assets/stylesheets/rolodex/settings/utilities/_layout.sass +51 -0
  122. data/vendor/assets/stylesheets/rolodex/settings/utilities/_misc.sass +13 -0
  123. data/vendor/assets/stylesheets/rolodex/settings/utilities/_typography.sass +60 -0
  124. data/vendor/assets/stylesheets/rolodex/settings/variables/_alerts.scss +27 -0
  125. data/vendor/assets/stylesheets/rolodex/settings/variables/_buttons.scss +10 -0
  126. data/vendor/assets/stylesheets/rolodex/settings/variables/_colors.scss +46 -0
  127. data/vendor/assets/stylesheets/rolodex/settings/variables/_labels.scss +10 -0
  128. data/vendor/assets/stylesheets/rolodex/settings/variables/_layout.scss +22 -0
  129. data/vendor/assets/stylesheets/rolodex/settings/variables/_misc.scss +12 -0
  130. data/vendor/assets/stylesheets/rolodex/settings/variables/_typography.scss +34 -0
  131. metadata +263 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e37dca2d8c2c12e9f3fb77ce47a9e6e23cbc16b2
4
+ data.tar.gz: f23b84ad48a9e5e159e58f718ca50622547c37a3
5
+ SHA512:
6
+ metadata.gz: 41b0f200ef0df7f3f4e4c9384484f37f0b6319613f0e7bb52f18fba74f95146e970cf357092d0b49d3908f393b3623b017b525daf85ff42b53d4913d06bd4e84
7
+ data.tar.gz: 3dce60630089c31722f2708e10fe3b2c87459faf90bdb8141cbdc6732094739f018df5b3a007981fc2de2db05b9b1b6a17a83011fcb5fce6018f61d9bc847b41
@@ -0,0 +1,25 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+
19
+ # Ignore .DS_store file
20
+ .DS_Store
21
+
22
+ # Ignore Ruby version manager schtuff
23
+ .ruby-version
24
+ .ruby-gemset
25
+
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rolodex.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Darby Frey
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,25 @@
1
+ # Rolodex
2
+
3
+ Styleguide gem for Belly
4
+
5
+ ## Use with Middleman
6
+
7
+ Add to your `Gemfile`:
8
+
9
+ ```
10
+ gem 'rolodex', :git => 'https://github.com/bellycard/rolodex.git'
11
+ ```
12
+
13
+ Import modules to your Sass files:
14
+
15
+ ```
16
+ @import 'rolodex'
17
+ ```
18
+
19
+ ## Contributing
20
+
21
+ 1. Fork it
22
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
23
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
24
+ 4. Push to the branch (`git push origin my-new-feature`)
25
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,87 @@
1
+ // Karma configuration
2
+
3
+ module.exports = function(config) {
4
+ config.set({
5
+
6
+ // base path that will be used to resolve all patterns (eg. files, exclude)
7
+ basePath: '',
8
+
9
+
10
+ // frameworks to use
11
+ // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
12
+ frameworks: ['jasmine'],
13
+
14
+
15
+ // list of files / patterns to load in the browser
16
+ files: [
17
+ // Dependent libs for testing
18
+ 'spec/test_lib/jquery-2.1.1.js',
19
+ 'spec/test_lib/angular.js',
20
+ 'spec/test_lib/angular-mocks.js',
21
+ 'spec/test_lib/lodash.js',
22
+
23
+ // The actual apps/modules/templates
24
+ 'vendor/assets/javascripts/rolodex_angular/**/*.coffee',
25
+ 'vendor/assets/javascripts/rolodex_angular/**/*.html',
26
+
27
+ // Specs!
28
+ 'spec/javascripts/**/*.spec.coffee'
29
+ ],
30
+
31
+
32
+ // list of files to exclude
33
+ exclude: [
34
+ ],
35
+
36
+ ngHtml2JsPreprocessor: {
37
+ stripPrefix: 'source/',
38
+ moduleName: 'templates'
39
+ },
40
+
41
+ coverageReporter: {
42
+ type: 'html',
43
+ dir: 'spec/javascripts/rolodex_angular/coverage/'
44
+ },
45
+
46
+ // preprocess matching files before serving them to the browser
47
+ // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
48
+ preprocessors: {
49
+ '**/*.html': ['ng-html2js'],
50
+ 'vendor/assets/javascripts/rolodex_angular/**/*.coffee': ['coverage'],
51
+ 'spec/javascripts/rolodex_angular/**/*.coffee': ['coffee']
52
+ },
53
+
54
+
55
+ // test results reporter to use
56
+ // possible values: 'dots', 'progress'
57
+ // available reporters: https://npmjs.org/browse/keyword/karma-reporter
58
+ reporters: ['progress', 'coverage'],
59
+
60
+
61
+ // web server port
62
+ port: 9876,
63
+
64
+
65
+ // enable / disable colors in the output (reporters and logs)
66
+ colors: true,
67
+
68
+
69
+ // level of logging
70
+ // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
71
+ logLevel: config.LOG_INFO,
72
+
73
+
74
+ // enable / disable watching file and executing tests whenever any file changes
75
+ autoWatch: true,
76
+
77
+
78
+ // start these browsers
79
+ // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
80
+ browsers: ['Chrome', 'Firefox', 'Safari'],
81
+
82
+
83
+ // Continuous Integration mode
84
+ // if true, Karma captures browsers, runs the tests and exits
85
+ singleRun: false
86
+ });
87
+ };
@@ -0,0 +1,31 @@
1
+ require 'angular/html2js'
2
+ require 'rolodex/sass'
3
+
4
+ module Rolodex
5
+ class << self
6
+ # Inspired by bootstrap-sass
7
+ def load!
8
+ Angular::Html2js.configure do |config|
9
+ config.module_name = 'templates'
10
+ config.init_sprockets
11
+ end
12
+
13
+ if defined?(::Rails)
14
+ require 'rolodex/engine'
15
+ elsif defined?(:Sprockets)
16
+ Sprockets.append_path(File.join(asset_path, 'stylesheets'))
17
+ Sprockets.append_path(File.join(asset_path, 'javascripts'))
18
+ end
19
+ end
20
+
21
+ def gem_path
22
+ @gem_path ||= File.expand_path '..', File.dirname(__FILE__)
23
+ end
24
+
25
+ def asset_path
26
+ @asset_path ||= File.join gem_path, 'vendor', 'assets'
27
+ end
28
+ end
29
+ end
30
+
31
+ Rolodex.load!
@@ -0,0 +1,12 @@
1
+ module Rolodex
2
+ module Rails
3
+ class Engine < ::Rails::Engine
4
+ initializer 'rolodex.assets.precompile' do |app|
5
+ %w(stylesheets javascripts images).each do |sub|
6
+ app.config.assets.paths << root.join('vendor', 'assets', sub).to_s
7
+ end
8
+ app.config.assets.precompile << %r(\.(?:png|svg|gif)$)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,5 @@
1
+ require 'sass'
2
+
3
+ # Rounding numbers to the 8th decimal
4
+
5
+ Sass::Script::Number.precision = 8
@@ -0,0 +1,3 @@
1
+ module Rolodex
2
+ VERSION = "2.0.0"
3
+ end
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "rolodex",
3
+ "version": "0.0.1",
4
+ "description": "Style for Belly",
5
+ "main": "",
6
+ "dependencies": {},
7
+ "devDependencies": {
8
+ "karma": "^0.12.19",
9
+ "karma-chrome-launcher": "^0.1.4",
10
+ "karma-coffee-preprocessor": "^0.2.1",
11
+ "karma-coverage": "^0.2.5",
12
+ "karma-firefox-launcher": "^0.1.3",
13
+ "karma-jasmine": "^0.1.5",
14
+ "karma-ng-html2js-preprocessor": "^0.1.0",
15
+ "karma-safari-launcher": "^0.1.1"
16
+ },
17
+ "scripts": {
18
+ "test_angular": "./node_modules/karma/bin/karma start"
19
+ },
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "https://github.com/bellycard/rolodex.git"
23
+ },
24
+ "keywords": [
25
+ "rolodex"
26
+ ],
27
+ "author": "Belly",
28
+ "license": "ISC",
29
+ "bugs": {
30
+ "url": "https://github.com/bellycard/rolodex/issues"
31
+ },
32
+ "homepage": "https://github.com/bellycard/rolodex"
33
+ }
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'rolodex/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "rolodex"
8
+ spec.version = Rolodex::VERSION
9
+ spec.authors = ["AJ Self, Darby Frey, Shay Howe"]
10
+ spec.email = ["tech@bellycard.com"]
11
+ spec.description = %q{Rolodex}
12
+ spec.summary = %q{Rolodex}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency 'angular-html2js'
22
+ spec.add_dependency 'sass', '~> 3.4.3'
23
+ spec.add_dependency 'sprockets', '~> 2.12.2'
24
+
25
+ spec.add_development_dependency 'bundler', '~> 1.3'
26
+ spec.add_development_dependency 'rake'
27
+ spec.add_development_dependency 'pry'
28
+
29
+ end
@@ -0,0 +1,13 @@
1
+ describe 'roModal', ->
2
+
3
+ beforeEach module('rolodex.modal')
4
+
5
+ xdescribe 'basic modal with defaults', ->
6
+ xdescribe 'configuring options via provider', ->
7
+ xdescribe 'controller', ->
8
+ xdescribe 'resolve', ->
9
+ xdescribe 'scope', ->
10
+ xdescribe 'keyboard interaction', ->
11
+ xdescribe 'backdrop', ->
12
+ xdescribe 'window', ->
13
+ xdescribe 'sizing', ->
File without changes
@@ -0,0 +1,2173 @@
1
+ /**
2
+ * @license AngularJS v1.2.21
3
+ * (c) 2010-2014 Google, Inc. http://angularjs.org
4
+ * License: MIT
5
+ */
6
+ (function(window, angular, undefined) {
7
+
8
+ 'use strict';
9
+
10
+ /**
11
+ * @ngdoc object
12
+ * @name angular.mock
13
+ * @description
14
+ *
15
+ * Namespace from 'angular-mocks.js' which contains testing related code.
16
+ */
17
+ angular.mock = {};
18
+
19
+ /**
20
+ * ! This is a private undocumented service !
21
+ *
22
+ * @name $browser
23
+ *
24
+ * @description
25
+ * This service is a mock implementation of {@link ng.$browser}. It provides fake
26
+ * implementation for commonly used browser apis that are hard to test, e.g. setTimeout, xhr,
27
+ * cookies, etc...
28
+ *
29
+ * The api of this service is the same as that of the real {@link ng.$browser $browser}, except
30
+ * that there are several helper methods available which can be used in tests.
31
+ */
32
+ angular.mock.$BrowserProvider = function() {
33
+ this.$get = function() {
34
+ return new angular.mock.$Browser();
35
+ };
36
+ };
37
+
38
+ angular.mock.$Browser = function() {
39
+ var self = this;
40
+
41
+ this.isMock = true;
42
+ self.$$url = "http://server/";
43
+ self.$$lastUrl = self.$$url; // used by url polling fn
44
+ self.pollFns = [];
45
+
46
+ // TODO(vojta): remove this temporary api
47
+ self.$$completeOutstandingRequest = angular.noop;
48
+ self.$$incOutstandingRequestCount = angular.noop;
49
+
50
+
51
+ // register url polling fn
52
+
53
+ self.onUrlChange = function(listener) {
54
+ self.pollFns.push(
55
+ function() {
56
+ if (self.$$lastUrl != self.$$url) {
57
+ self.$$lastUrl = self.$$url;
58
+ listener(self.$$url);
59
+ }
60
+ }
61
+ );
62
+
63
+ return listener;
64
+ };
65
+
66
+ self.cookieHash = {};
67
+ self.lastCookieHash = {};
68
+ self.deferredFns = [];
69
+ self.deferredNextId = 0;
70
+
71
+ self.defer = function(fn, delay) {
72
+ delay = delay || 0;
73
+ self.deferredFns.push({time:(self.defer.now + delay), fn:fn, id: self.deferredNextId});
74
+ self.deferredFns.sort(function(a,b){ return a.time - b.time;});
75
+ return self.deferredNextId++;
76
+ };
77
+
78
+
79
+ /**
80
+ * @name $browser#defer.now
81
+ *
82
+ * @description
83
+ * Current milliseconds mock time.
84
+ */
85
+ self.defer.now = 0;
86
+
87
+
88
+ self.defer.cancel = function(deferId) {
89
+ var fnIndex;
90
+
91
+ angular.forEach(self.deferredFns, function(fn, index) {
92
+ if (fn.id === deferId) fnIndex = index;
93
+ });
94
+
95
+ if (fnIndex !== undefined) {
96
+ self.deferredFns.splice(fnIndex, 1);
97
+ return true;
98
+ }
99
+
100
+ return false;
101
+ };
102
+
103
+
104
+ /**
105
+ * @name $browser#defer.flush
106
+ *
107
+ * @description
108
+ * Flushes all pending requests and executes the defer callbacks.
109
+ *
110
+ * @param {number=} number of milliseconds to flush. See {@link #defer.now}
111
+ */
112
+ self.defer.flush = function(delay) {
113
+ if (angular.isDefined(delay)) {
114
+ self.defer.now += delay;
115
+ } else {
116
+ if (self.deferredFns.length) {
117
+ self.defer.now = self.deferredFns[self.deferredFns.length-1].time;
118
+ } else {
119
+ throw new Error('No deferred tasks to be flushed');
120
+ }
121
+ }
122
+
123
+ while (self.deferredFns.length && self.deferredFns[0].time <= self.defer.now) {
124
+ self.deferredFns.shift().fn();
125
+ }
126
+ };
127
+
128
+ self.$$baseHref = '';
129
+ self.baseHref = function() {
130
+ return this.$$baseHref;
131
+ };
132
+ };
133
+ angular.mock.$Browser.prototype = {
134
+
135
+ /**
136
+ * @name $browser#poll
137
+ *
138
+ * @description
139
+ * run all fns in pollFns
140
+ */
141
+ poll: function poll() {
142
+ angular.forEach(this.pollFns, function(pollFn){
143
+ pollFn();
144
+ });
145
+ },
146
+
147
+ addPollFn: function(pollFn) {
148
+ this.pollFns.push(pollFn);
149
+ return pollFn;
150
+ },
151
+
152
+ url: function(url, replace) {
153
+ if (url) {
154
+ this.$$url = url;
155
+ return this;
156
+ }
157
+
158
+ return this.$$url;
159
+ },
160
+
161
+ cookies: function(name, value) {
162
+ if (name) {
163
+ if (angular.isUndefined(value)) {
164
+ delete this.cookieHash[name];
165
+ } else {
166
+ if (angular.isString(value) && //strings only
167
+ value.length <= 4096) { //strict cookie storage limits
168
+ this.cookieHash[name] = value;
169
+ }
170
+ }
171
+ } else {
172
+ if (!angular.equals(this.cookieHash, this.lastCookieHash)) {
173
+ this.lastCookieHash = angular.copy(this.cookieHash);
174
+ this.cookieHash = angular.copy(this.cookieHash);
175
+ }
176
+ return this.cookieHash;
177
+ }
178
+ },
179
+
180
+ notifyWhenNoOutstandingRequests: function(fn) {
181
+ fn();
182
+ }
183
+ };
184
+
185
+
186
+ /**
187
+ * @ngdoc provider
188
+ * @name $exceptionHandlerProvider
189
+ *
190
+ * @description
191
+ * Configures the mock implementation of {@link ng.$exceptionHandler} to rethrow or to log errors
192
+ * passed into the `$exceptionHandler`.
193
+ */
194
+
195
+ /**
196
+ * @ngdoc service
197
+ * @name $exceptionHandler
198
+ *
199
+ * @description
200
+ * Mock implementation of {@link ng.$exceptionHandler} that rethrows or logs errors passed
201
+ * into it. See {@link ngMock.$exceptionHandlerProvider $exceptionHandlerProvider} for configuration
202
+ * information.
203
+ *
204
+ *
205
+ * ```js
206
+ * describe('$exceptionHandlerProvider', function() {
207
+ *
208
+ * it('should capture log messages and exceptions', function() {
209
+ *
210
+ * module(function($exceptionHandlerProvider) {
211
+ * $exceptionHandlerProvider.mode('log');
212
+ * });
213
+ *
214
+ * inject(function($log, $exceptionHandler, $timeout) {
215
+ * $timeout(function() { $log.log(1); });
216
+ * $timeout(function() { $log.log(2); throw 'banana peel'; });
217
+ * $timeout(function() { $log.log(3); });
218
+ * expect($exceptionHandler.errors).toEqual([]);
219
+ * expect($log.assertEmpty());
220
+ * $timeout.flush();
221
+ * expect($exceptionHandler.errors).toEqual(['banana peel']);
222
+ * expect($log.log.logs).toEqual([[1], [2], [3]]);
223
+ * });
224
+ * });
225
+ * });
226
+ * ```
227
+ */
228
+
229
+ angular.mock.$ExceptionHandlerProvider = function() {
230
+ var handler;
231
+
232
+ /**
233
+ * @ngdoc method
234
+ * @name $exceptionHandlerProvider#mode
235
+ *
236
+ * @description
237
+ * Sets the logging mode.
238
+ *
239
+ * @param {string} mode Mode of operation, defaults to `rethrow`.
240
+ *
241
+ * - `rethrow`: If any errors are passed into the handler in tests, it typically
242
+ * means that there is a bug in the application or test, so this mock will
243
+ * make these tests fail.
244
+ * - `log`: Sometimes it is desirable to test that an error is thrown, for this case the `log`
245
+ * mode stores an array of errors in `$exceptionHandler.errors`, to allow later
246
+ * assertion of them. See {@link ngMock.$log#assertEmpty assertEmpty()} and
247
+ * {@link ngMock.$log#reset reset()}
248
+ */
249
+ this.mode = function(mode) {
250
+ switch(mode) {
251
+ case 'rethrow':
252
+ handler = function(e) {
253
+ throw e;
254
+ };
255
+ break;
256
+ case 'log':
257
+ var errors = [];
258
+
259
+ handler = function(e) {
260
+ if (arguments.length == 1) {
261
+ errors.push(e);
262
+ } else {
263
+ errors.push([].slice.call(arguments, 0));
264
+ }
265
+ };
266
+
267
+ handler.errors = errors;
268
+ break;
269
+ default:
270
+ throw new Error("Unknown mode '" + mode + "', only 'log'/'rethrow' modes are allowed!");
271
+ }
272
+ };
273
+
274
+ this.$get = function() {
275
+ return handler;
276
+ };
277
+
278
+ this.mode('rethrow');
279
+ };
280
+
281
+
282
+ /**
283
+ * @ngdoc service
284
+ * @name $log
285
+ *
286
+ * @description
287
+ * Mock implementation of {@link ng.$log} that gathers all logged messages in arrays
288
+ * (one array per logging level). These arrays are exposed as `logs` property of each of the
289
+ * level-specific log function, e.g. for level `error` the array is exposed as `$log.error.logs`.
290
+ *
291
+ */
292
+ angular.mock.$LogProvider = function() {
293
+ var debug = true;
294
+
295
+ function concat(array1, array2, index) {
296
+ return array1.concat(Array.prototype.slice.call(array2, index));
297
+ }
298
+
299
+ this.debugEnabled = function(flag) {
300
+ if (angular.isDefined(flag)) {
301
+ debug = flag;
302
+ return this;
303
+ } else {
304
+ return debug;
305
+ }
306
+ };
307
+
308
+ this.$get = function () {
309
+ var $log = {
310
+ log: function() { $log.log.logs.push(concat([], arguments, 0)); },
311
+ warn: function() { $log.warn.logs.push(concat([], arguments, 0)); },
312
+ info: function() { $log.info.logs.push(concat([], arguments, 0)); },
313
+ error: function() { $log.error.logs.push(concat([], arguments, 0)); },
314
+ debug: function() {
315
+ if (debug) {
316
+ $log.debug.logs.push(concat([], arguments, 0));
317
+ }
318
+ }
319
+ };
320
+
321
+ /**
322
+ * @ngdoc method
323
+ * @name $log#reset
324
+ *
325
+ * @description
326
+ * Reset all of the logging arrays to empty.
327
+ */
328
+ $log.reset = function () {
329
+ /**
330
+ * @ngdoc property
331
+ * @name $log#log.logs
332
+ *
333
+ * @description
334
+ * Array of messages logged using {@link ngMock.$log#log}.
335
+ *
336
+ * @example
337
+ * ```js
338
+ * $log.log('Some Log');
339
+ * var first = $log.log.logs.unshift();
340
+ * ```
341
+ */
342
+ $log.log.logs = [];
343
+ /**
344
+ * @ngdoc property
345
+ * @name $log#info.logs
346
+ *
347
+ * @description
348
+ * Array of messages logged using {@link ngMock.$log#info}.
349
+ *
350
+ * @example
351
+ * ```js
352
+ * $log.info('Some Info');
353
+ * var first = $log.info.logs.unshift();
354
+ * ```
355
+ */
356
+ $log.info.logs = [];
357
+ /**
358
+ * @ngdoc property
359
+ * @name $log#warn.logs
360
+ *
361
+ * @description
362
+ * Array of messages logged using {@link ngMock.$log#warn}.
363
+ *
364
+ * @example
365
+ * ```js
366
+ * $log.warn('Some Warning');
367
+ * var first = $log.warn.logs.unshift();
368
+ * ```
369
+ */
370
+ $log.warn.logs = [];
371
+ /**
372
+ * @ngdoc property
373
+ * @name $log#error.logs
374
+ *
375
+ * @description
376
+ * Array of messages logged using {@link ngMock.$log#error}.
377
+ *
378
+ * @example
379
+ * ```js
380
+ * $log.error('Some Error');
381
+ * var first = $log.error.logs.unshift();
382
+ * ```
383
+ */
384
+ $log.error.logs = [];
385
+ /**
386
+ * @ngdoc property
387
+ * @name $log#debug.logs
388
+ *
389
+ * @description
390
+ * Array of messages logged using {@link ngMock.$log#debug}.
391
+ *
392
+ * @example
393
+ * ```js
394
+ * $log.debug('Some Error');
395
+ * var first = $log.debug.logs.unshift();
396
+ * ```
397
+ */
398
+ $log.debug.logs = [];
399
+ };
400
+
401
+ /**
402
+ * @ngdoc method
403
+ * @name $log#assertEmpty
404
+ *
405
+ * @description
406
+ * Assert that the all of the logging methods have no logged messages. If messages present, an
407
+ * exception is thrown.
408
+ */
409
+ $log.assertEmpty = function() {
410
+ var errors = [];
411
+ angular.forEach(['error', 'warn', 'info', 'log', 'debug'], function(logLevel) {
412
+ angular.forEach($log[logLevel].logs, function(log) {
413
+ angular.forEach(log, function (logItem) {
414
+ errors.push('MOCK $log (' + logLevel + '): ' + String(logItem) + '\n' +
415
+ (logItem.stack || ''));
416
+ });
417
+ });
418
+ });
419
+ if (errors.length) {
420
+ errors.unshift("Expected $log to be empty! Either a message was logged unexpectedly, or "+
421
+ "an expected log message was not checked and removed:");
422
+ errors.push('');
423
+ throw new Error(errors.join('\n---------\n'));
424
+ }
425
+ };
426
+
427
+ $log.reset();
428
+ return $log;
429
+ };
430
+ };
431
+
432
+
433
+ /**
434
+ * @ngdoc service
435
+ * @name $interval
436
+ *
437
+ * @description
438
+ * Mock implementation of the $interval service.
439
+ *
440
+ * Use {@link ngMock.$interval#flush `$interval.flush(millis)`} to
441
+ * move forward by `millis` milliseconds and trigger any functions scheduled to run in that
442
+ * time.
443
+ *
444
+ * @param {function()} fn A function that should be called repeatedly.
445
+ * @param {number} delay Number of milliseconds between each function call.
446
+ * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
447
+ * indefinitely.
448
+ * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
449
+ * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
450
+ * @returns {promise} A promise which will be notified on each iteration.
451
+ */
452
+ angular.mock.$IntervalProvider = function() {
453
+ this.$get = ['$rootScope', '$q',
454
+ function($rootScope, $q) {
455
+ var repeatFns = [],
456
+ nextRepeatId = 0,
457
+ now = 0;
458
+
459
+ var $interval = function(fn, delay, count, invokeApply) {
460
+ var deferred = $q.defer(),
461
+ promise = deferred.promise,
462
+ iteration = 0,
463
+ skipApply = (angular.isDefined(invokeApply) && !invokeApply);
464
+
465
+ count = (angular.isDefined(count)) ? count : 0;
466
+ promise.then(null, null, fn);
467
+
468
+ promise.$$intervalId = nextRepeatId;
469
+
470
+ function tick() {
471
+ deferred.notify(iteration++);
472
+
473
+ if (count > 0 && iteration >= count) {
474
+ var fnIndex;
475
+ deferred.resolve(iteration);
476
+
477
+ angular.forEach(repeatFns, function(fn, index) {
478
+ if (fn.id === promise.$$intervalId) fnIndex = index;
479
+ });
480
+
481
+ if (fnIndex !== undefined) {
482
+ repeatFns.splice(fnIndex, 1);
483
+ }
484
+ }
485
+
486
+ if (!skipApply) $rootScope.$apply();
487
+ }
488
+
489
+ repeatFns.push({
490
+ nextTime:(now + delay),
491
+ delay: delay,
492
+ fn: tick,
493
+ id: nextRepeatId,
494
+ deferred: deferred
495
+ });
496
+ repeatFns.sort(function(a,b){ return a.nextTime - b.nextTime;});
497
+
498
+ nextRepeatId++;
499
+ return promise;
500
+ };
501
+ /**
502
+ * @ngdoc method
503
+ * @name $interval#cancel
504
+ *
505
+ * @description
506
+ * Cancels a task associated with the `promise`.
507
+ *
508
+ * @param {promise} promise A promise from calling the `$interval` function.
509
+ * @returns {boolean} Returns `true` if the task was successfully cancelled.
510
+ */
511
+ $interval.cancel = function(promise) {
512
+ if(!promise) return false;
513
+ var fnIndex;
514
+
515
+ angular.forEach(repeatFns, function(fn, index) {
516
+ if (fn.id === promise.$$intervalId) fnIndex = index;
517
+ });
518
+
519
+ if (fnIndex !== undefined) {
520
+ repeatFns[fnIndex].deferred.reject('canceled');
521
+ repeatFns.splice(fnIndex, 1);
522
+ return true;
523
+ }
524
+
525
+ return false;
526
+ };
527
+
528
+ /**
529
+ * @ngdoc method
530
+ * @name $interval#flush
531
+ * @description
532
+ *
533
+ * Runs interval tasks scheduled to be run in the next `millis` milliseconds.
534
+ *
535
+ * @param {number=} millis maximum timeout amount to flush up until.
536
+ *
537
+ * @return {number} The amount of time moved forward.
538
+ */
539
+ $interval.flush = function(millis) {
540
+ now += millis;
541
+ while (repeatFns.length && repeatFns[0].nextTime <= now) {
542
+ var task = repeatFns[0];
543
+ task.fn();
544
+ task.nextTime += task.delay;
545
+ repeatFns.sort(function(a,b){ return a.nextTime - b.nextTime;});
546
+ }
547
+ return millis;
548
+ };
549
+
550
+ return $interval;
551
+ }];
552
+ };
553
+
554
+
555
+ /* jshint -W101 */
556
+ /* The R_ISO8061_STR regex is never going to fit into the 100 char limit!
557
+ * This directive should go inside the anonymous function but a bug in JSHint means that it would
558
+ * not be enacted early enough to prevent the warning.
559
+ */
560
+ var R_ISO8061_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?:\:?(\d\d)(?:\:?(\d\d)(?:\.(\d{3}))?)?)?(Z|([+-])(\d\d):?(\d\d)))?$/;
561
+
562
+ function jsonStringToDate(string) {
563
+ var match;
564
+ if (match = string.match(R_ISO8061_STR)) {
565
+ var date = new Date(0),
566
+ tzHour = 0,
567
+ tzMin = 0;
568
+ if (match[9]) {
569
+ tzHour = int(match[9] + match[10]);
570
+ tzMin = int(match[9] + match[11]);
571
+ }
572
+ date.setUTCFullYear(int(match[1]), int(match[2]) - 1, int(match[3]));
573
+ date.setUTCHours(int(match[4]||0) - tzHour,
574
+ int(match[5]||0) - tzMin,
575
+ int(match[6]||0),
576
+ int(match[7]||0));
577
+ return date;
578
+ }
579
+ return string;
580
+ }
581
+
582
+ function int(str) {
583
+ return parseInt(str, 10);
584
+ }
585
+
586
+ function padNumber(num, digits, trim) {
587
+ var neg = '';
588
+ if (num < 0) {
589
+ neg = '-';
590
+ num = -num;
591
+ }
592
+ num = '' + num;
593
+ while(num.length < digits) num = '0' + num;
594
+ if (trim)
595
+ num = num.substr(num.length - digits);
596
+ return neg + num;
597
+ }
598
+
599
+
600
+ /**
601
+ * @ngdoc type
602
+ * @name angular.mock.TzDate
603
+ * @description
604
+ *
605
+ * *NOTE*: this is not an injectable instance, just a globally available mock class of `Date`.
606
+ *
607
+ * Mock of the Date type which has its timezone specified via constructor arg.
608
+ *
609
+ * The main purpose is to create Date-like instances with timezone fixed to the specified timezone
610
+ * offset, so that we can test code that depends on local timezone settings without dependency on
611
+ * the time zone settings of the machine where the code is running.
612
+ *
613
+ * @param {number} offset Offset of the *desired* timezone in hours (fractions will be honored)
614
+ * @param {(number|string)} timestamp Timestamp representing the desired time in *UTC*
615
+ *
616
+ * @example
617
+ * !!!! WARNING !!!!!
618
+ * This is not a complete Date object so only methods that were implemented can be called safely.
619
+ * To make matters worse, TzDate instances inherit stuff from Date via a prototype.
620
+ *
621
+ * We do our best to intercept calls to "unimplemented" methods, but since the list of methods is
622
+ * incomplete we might be missing some non-standard methods. This can result in errors like:
623
+ * "Date.prototype.foo called on incompatible Object".
624
+ *
625
+ * ```js
626
+ * var newYearInBratislava = new TzDate(-1, '2009-12-31T23:00:00Z');
627
+ * newYearInBratislava.getTimezoneOffset() => -60;
628
+ * newYearInBratislava.getFullYear() => 2010;
629
+ * newYearInBratislava.getMonth() => 0;
630
+ * newYearInBratislava.getDate() => 1;
631
+ * newYearInBratislava.getHours() => 0;
632
+ * newYearInBratislava.getMinutes() => 0;
633
+ * newYearInBratislava.getSeconds() => 0;
634
+ * ```
635
+ *
636
+ */
637
+ angular.mock.TzDate = function (offset, timestamp) {
638
+ var self = new Date(0);
639
+ if (angular.isString(timestamp)) {
640
+ var tsStr = timestamp;
641
+
642
+ self.origDate = jsonStringToDate(timestamp);
643
+
644
+ timestamp = self.origDate.getTime();
645
+ if (isNaN(timestamp))
646
+ throw {
647
+ name: "Illegal Argument",
648
+ message: "Arg '" + tsStr + "' passed into TzDate constructor is not a valid date string"
649
+ };
650
+ } else {
651
+ self.origDate = new Date(timestamp);
652
+ }
653
+
654
+ var localOffset = new Date(timestamp).getTimezoneOffset();
655
+ self.offsetDiff = localOffset*60*1000 - offset*1000*60*60;
656
+ self.date = new Date(timestamp + self.offsetDiff);
657
+
658
+ self.getTime = function() {
659
+ return self.date.getTime() - self.offsetDiff;
660
+ };
661
+
662
+ self.toLocaleDateString = function() {
663
+ return self.date.toLocaleDateString();
664
+ };
665
+
666
+ self.getFullYear = function() {
667
+ return self.date.getFullYear();
668
+ };
669
+
670
+ self.getMonth = function() {
671
+ return self.date.getMonth();
672
+ };
673
+
674
+ self.getDate = function() {
675
+ return self.date.getDate();
676
+ };
677
+
678
+ self.getHours = function() {
679
+ return self.date.getHours();
680
+ };
681
+
682
+ self.getMinutes = function() {
683
+ return self.date.getMinutes();
684
+ };
685
+
686
+ self.getSeconds = function() {
687
+ return self.date.getSeconds();
688
+ };
689
+
690
+ self.getMilliseconds = function() {
691
+ return self.date.getMilliseconds();
692
+ };
693
+
694
+ self.getTimezoneOffset = function() {
695
+ return offset * 60;
696
+ };
697
+
698
+ self.getUTCFullYear = function() {
699
+ return self.origDate.getUTCFullYear();
700
+ };
701
+
702
+ self.getUTCMonth = function() {
703
+ return self.origDate.getUTCMonth();
704
+ };
705
+
706
+ self.getUTCDate = function() {
707
+ return self.origDate.getUTCDate();
708
+ };
709
+
710
+ self.getUTCHours = function() {
711
+ return self.origDate.getUTCHours();
712
+ };
713
+
714
+ self.getUTCMinutes = function() {
715
+ return self.origDate.getUTCMinutes();
716
+ };
717
+
718
+ self.getUTCSeconds = function() {
719
+ return self.origDate.getUTCSeconds();
720
+ };
721
+
722
+ self.getUTCMilliseconds = function() {
723
+ return self.origDate.getUTCMilliseconds();
724
+ };
725
+
726
+ self.getDay = function() {
727
+ return self.date.getDay();
728
+ };
729
+
730
+ // provide this method only on browsers that already have it
731
+ if (self.toISOString) {
732
+ self.toISOString = function() {
733
+ return padNumber(self.origDate.getUTCFullYear(), 4) + '-' +
734
+ padNumber(self.origDate.getUTCMonth() + 1, 2) + '-' +
735
+ padNumber(self.origDate.getUTCDate(), 2) + 'T' +
736
+ padNumber(self.origDate.getUTCHours(), 2) + ':' +
737
+ padNumber(self.origDate.getUTCMinutes(), 2) + ':' +
738
+ padNumber(self.origDate.getUTCSeconds(), 2) + '.' +
739
+ padNumber(self.origDate.getUTCMilliseconds(), 3) + 'Z';
740
+ };
741
+ }
742
+
743
+ //hide all methods not implemented in this mock that the Date prototype exposes
744
+ var unimplementedMethods = ['getUTCDay',
745
+ 'getYear', 'setDate', 'setFullYear', 'setHours', 'setMilliseconds',
746
+ 'setMinutes', 'setMonth', 'setSeconds', 'setTime', 'setUTCDate', 'setUTCFullYear',
747
+ 'setUTCHours', 'setUTCMilliseconds', 'setUTCMinutes', 'setUTCMonth', 'setUTCSeconds',
748
+ 'setYear', 'toDateString', 'toGMTString', 'toJSON', 'toLocaleFormat', 'toLocaleString',
749
+ 'toLocaleTimeString', 'toSource', 'toString', 'toTimeString', 'toUTCString', 'valueOf'];
750
+
751
+ angular.forEach(unimplementedMethods, function(methodName) {
752
+ self[methodName] = function() {
753
+ throw new Error("Method '" + methodName + "' is not implemented in the TzDate mock");
754
+ };
755
+ });
756
+
757
+ return self;
758
+ };
759
+
760
+ //make "tzDateInstance instanceof Date" return true
761
+ angular.mock.TzDate.prototype = Date.prototype;
762
+ /* jshint +W101 */
763
+
764
+ angular.mock.animate = angular.module('ngAnimateMock', ['ng'])
765
+
766
+ .config(['$provide', function($provide) {
767
+
768
+ var reflowQueue = [];
769
+ $provide.value('$$animateReflow', function(fn) {
770
+ var index = reflowQueue.length;
771
+ reflowQueue.push(fn);
772
+ return function cancel() {
773
+ reflowQueue.splice(index, 1);
774
+ };
775
+ });
776
+
777
+ $provide.decorator('$animate', function($delegate, $$asyncCallback) {
778
+ var animate = {
779
+ queue : [],
780
+ enabled : $delegate.enabled,
781
+ triggerCallbacks : function() {
782
+ $$asyncCallback.flush();
783
+ },
784
+ triggerReflow : function() {
785
+ angular.forEach(reflowQueue, function(fn) {
786
+ fn();
787
+ });
788
+ reflowQueue = [];
789
+ }
790
+ };
791
+
792
+ angular.forEach(
793
+ ['enter','leave','move','addClass','removeClass','setClass'], function(method) {
794
+ animate[method] = function() {
795
+ animate.queue.push({
796
+ event : method,
797
+ element : arguments[0],
798
+ args : arguments
799
+ });
800
+ $delegate[method].apply($delegate, arguments);
801
+ };
802
+ });
803
+
804
+ return animate;
805
+ });
806
+
807
+ }]);
808
+
809
+
810
+ /**
811
+ * @ngdoc function
812
+ * @name angular.mock.dump
813
+ * @description
814
+ *
815
+ * *NOTE*: this is not an injectable instance, just a globally available function.
816
+ *
817
+ * Method for serializing common angular objects (scope, elements, etc..) into strings, useful for
818
+ * debugging.
819
+ *
820
+ * This method is also available on window, where it can be used to display objects on debug
821
+ * console.
822
+ *
823
+ * @param {*} object - any object to turn into string.
824
+ * @return {string} a serialized string of the argument
825
+ */
826
+ angular.mock.dump = function(object) {
827
+ return serialize(object);
828
+
829
+ function serialize(object) {
830
+ var out;
831
+
832
+ if (angular.isElement(object)) {
833
+ object = angular.element(object);
834
+ out = angular.element('<div></div>');
835
+ angular.forEach(object, function(element) {
836
+ out.append(angular.element(element).clone());
837
+ });
838
+ out = out.html();
839
+ } else if (angular.isArray(object)) {
840
+ out = [];
841
+ angular.forEach(object, function(o) {
842
+ out.push(serialize(o));
843
+ });
844
+ out = '[ ' + out.join(', ') + ' ]';
845
+ } else if (angular.isObject(object)) {
846
+ if (angular.isFunction(object.$eval) && angular.isFunction(object.$apply)) {
847
+ out = serializeScope(object);
848
+ } else if (object instanceof Error) {
849
+ out = object.stack || ('' + object.name + ': ' + object.message);
850
+ } else {
851
+ // TODO(i): this prevents methods being logged,
852
+ // we should have a better way to serialize objects
853
+ out = angular.toJson(object, true);
854
+ }
855
+ } else {
856
+ out = String(object);
857
+ }
858
+
859
+ return out;
860
+ }
861
+
862
+ function serializeScope(scope, offset) {
863
+ offset = offset || ' ';
864
+ var log = [offset + 'Scope(' + scope.$id + '): {'];
865
+ for ( var key in scope ) {
866
+ if (Object.prototype.hasOwnProperty.call(scope, key) && !key.match(/^(\$|this)/)) {
867
+ log.push(' ' + key + ': ' + angular.toJson(scope[key]));
868
+ }
869
+ }
870
+ var child = scope.$$childHead;
871
+ while(child) {
872
+ log.push(serializeScope(child, offset + ' '));
873
+ child = child.$$nextSibling;
874
+ }
875
+ log.push('}');
876
+ return log.join('\n' + offset);
877
+ }
878
+ };
879
+
880
+ /**
881
+ * @ngdoc service
882
+ * @name $httpBackend
883
+ * @description
884
+ * Fake HTTP backend implementation suitable for unit testing applications that use the
885
+ * {@link ng.$http $http service}.
886
+ *
887
+ * *Note*: For fake HTTP backend implementation suitable for end-to-end testing or backend-less
888
+ * development please see {@link ngMockE2E.$httpBackend e2e $httpBackend mock}.
889
+ *
890
+ * During unit testing, we want our unit tests to run quickly and have no external dependencies so
891
+ * we don’t want to send [XHR](https://developer.mozilla.org/en/xmlhttprequest) or
892
+ * [JSONP](http://en.wikipedia.org/wiki/JSONP) requests to a real server. All we really need is
893
+ * to verify whether a certain request has been sent or not, or alternatively just let the
894
+ * application make requests, respond with pre-trained responses and assert that the end result is
895
+ * what we expect it to be.
896
+ *
897
+ * This mock implementation can be used to respond with static or dynamic responses via the
898
+ * `expect` and `when` apis and their shortcuts (`expectGET`, `whenPOST`, etc).
899
+ *
900
+ * When an Angular application needs some data from a server, it calls the $http service, which
901
+ * sends the request to a real server using $httpBackend service. With dependency injection, it is
902
+ * easy to inject $httpBackend mock (which has the same API as $httpBackend) and use it to verify
903
+ * the requests and respond with some testing data without sending a request to a real server.
904
+ *
905
+ * There are two ways to specify what test data should be returned as http responses by the mock
906
+ * backend when the code under test makes http requests:
907
+ *
908
+ * - `$httpBackend.expect` - specifies a request expectation
909
+ * - `$httpBackend.when` - specifies a backend definition
910
+ *
911
+ *
912
+ * # Request Expectations vs Backend Definitions
913
+ *
914
+ * Request expectations provide a way to make assertions about requests made by the application and
915
+ * to define responses for those requests. The test will fail if the expected requests are not made
916
+ * or they are made in the wrong order.
917
+ *
918
+ * Backend definitions allow you to define a fake backend for your application which doesn't assert
919
+ * if a particular request was made or not, it just returns a trained response if a request is made.
920
+ * The test will pass whether or not the request gets made during testing.
921
+ *
922
+ *
923
+ * <table class="table">
924
+ * <tr><th width="220px"></th><th>Request expectations</th><th>Backend definitions</th></tr>
925
+ * <tr>
926
+ * <th>Syntax</th>
927
+ * <td>.expect(...).respond(...)</td>
928
+ * <td>.when(...).respond(...)</td>
929
+ * </tr>
930
+ * <tr>
931
+ * <th>Typical usage</th>
932
+ * <td>strict unit tests</td>
933
+ * <td>loose (black-box) unit testing</td>
934
+ * </tr>
935
+ * <tr>
936
+ * <th>Fulfills multiple requests</th>
937
+ * <td>NO</td>
938
+ * <td>YES</td>
939
+ * </tr>
940
+ * <tr>
941
+ * <th>Order of requests matters</th>
942
+ * <td>YES</td>
943
+ * <td>NO</td>
944
+ * </tr>
945
+ * <tr>
946
+ * <th>Request required</th>
947
+ * <td>YES</td>
948
+ * <td>NO</td>
949
+ * </tr>
950
+ * <tr>
951
+ * <th>Response required</th>
952
+ * <td>optional (see below)</td>
953
+ * <td>YES</td>
954
+ * </tr>
955
+ * </table>
956
+ *
957
+ * In cases where both backend definitions and request expectations are specified during unit
958
+ * testing, the request expectations are evaluated first.
959
+ *
960
+ * If a request expectation has no response specified, the algorithm will search your backend
961
+ * definitions for an appropriate response.
962
+ *
963
+ * If a request didn't match any expectation or if the expectation doesn't have the response
964
+ * defined, the backend definitions are evaluated in sequential order to see if any of them match
965
+ * the request. The response from the first matched definition is returned.
966
+ *
967
+ *
968
+ * # Flushing HTTP requests
969
+ *
970
+ * The $httpBackend used in production always responds to requests asynchronously. If we preserved
971
+ * this behavior in unit testing, we'd have to create async unit tests, which are hard to write,
972
+ * to follow and to maintain. But neither can the testing mock respond synchronously; that would
973
+ * change the execution of the code under test. For this reason, the mock $httpBackend has a
974
+ * `flush()` method, which allows the test to explicitly flush pending requests. This preserves
975
+ * the async api of the backend, while allowing the test to execute synchronously.
976
+ *
977
+ *
978
+ * # Unit testing with mock $httpBackend
979
+ * The following code shows how to setup and use the mock backend when unit testing a controller.
980
+ * First we create the controller under test:
981
+ *
982
+ ```js
983
+ // The controller code
984
+ function MyController($scope, $http) {
985
+ var authToken;
986
+
987
+ $http.get('/auth.py').success(function(data, status, headers) {
988
+ authToken = headers('A-Token');
989
+ $scope.user = data;
990
+ });
991
+
992
+ $scope.saveMessage = function(message) {
993
+ var headers = { 'Authorization': authToken };
994
+ $scope.status = 'Saving...';
995
+
996
+ $http.post('/add-msg.py', message, { headers: headers } ).success(function(response) {
997
+ $scope.status = '';
998
+ }).error(function() {
999
+ $scope.status = 'ERROR!';
1000
+ });
1001
+ };
1002
+ }
1003
+ ```
1004
+ *
1005
+ * Now we setup the mock backend and create the test specs:
1006
+ *
1007
+ ```js
1008
+ // testing controller
1009
+ describe('MyController', function() {
1010
+ var $httpBackend, $rootScope, createController;
1011
+
1012
+ beforeEach(inject(function($injector) {
1013
+ // Set up the mock http service responses
1014
+ $httpBackend = $injector.get('$httpBackend');
1015
+ // backend definition common for all tests
1016
+ $httpBackend.when('GET', '/auth.py').respond({userId: 'userX'}, {'A-Token': 'xxx'});
1017
+
1018
+ // Get hold of a scope (i.e. the root scope)
1019
+ $rootScope = $injector.get('$rootScope');
1020
+ // The $controller service is used to create instances of controllers
1021
+ var $controller = $injector.get('$controller');
1022
+
1023
+ createController = function() {
1024
+ return $controller('MyController', {'$scope' : $rootScope });
1025
+ };
1026
+ }));
1027
+
1028
+
1029
+ afterEach(function() {
1030
+ $httpBackend.verifyNoOutstandingExpectation();
1031
+ $httpBackend.verifyNoOutstandingRequest();
1032
+ });
1033
+
1034
+
1035
+ it('should fetch authentication token', function() {
1036
+ $httpBackend.expectGET('/auth.py');
1037
+ var controller = createController();
1038
+ $httpBackend.flush();
1039
+ });
1040
+
1041
+
1042
+ it('should send msg to server', function() {
1043
+ var controller = createController();
1044
+ $httpBackend.flush();
1045
+
1046
+ // now you don’t care about the authentication, but
1047
+ // the controller will still send the request and
1048
+ // $httpBackend will respond without you having to
1049
+ // specify the expectation and response for this request
1050
+
1051
+ $httpBackend.expectPOST('/add-msg.py', 'message content').respond(201, '');
1052
+ $rootScope.saveMessage('message content');
1053
+ expect($rootScope.status).toBe('Saving...');
1054
+ $httpBackend.flush();
1055
+ expect($rootScope.status).toBe('');
1056
+ });
1057
+
1058
+
1059
+ it('should send auth header', function() {
1060
+ var controller = createController();
1061
+ $httpBackend.flush();
1062
+
1063
+ $httpBackend.expectPOST('/add-msg.py', undefined, function(headers) {
1064
+ // check if the header was send, if it wasn't the expectation won't
1065
+ // match the request and the test will fail
1066
+ return headers['Authorization'] == 'xxx';
1067
+ }).respond(201, '');
1068
+
1069
+ $rootScope.saveMessage('whatever');
1070
+ $httpBackend.flush();
1071
+ });
1072
+ });
1073
+ ```
1074
+ */
1075
+ angular.mock.$HttpBackendProvider = function() {
1076
+ this.$get = ['$rootScope', createHttpBackendMock];
1077
+ };
1078
+
1079
+ /**
1080
+ * General factory function for $httpBackend mock.
1081
+ * Returns instance for unit testing (when no arguments specified):
1082
+ * - passing through is disabled
1083
+ * - auto flushing is disabled
1084
+ *
1085
+ * Returns instance for e2e testing (when `$delegate` and `$browser` specified):
1086
+ * - passing through (delegating request to real backend) is enabled
1087
+ * - auto flushing is enabled
1088
+ *
1089
+ * @param {Object=} $delegate Real $httpBackend instance (allow passing through if specified)
1090
+ * @param {Object=} $browser Auto-flushing enabled if specified
1091
+ * @return {Object} Instance of $httpBackend mock
1092
+ */
1093
+ function createHttpBackendMock($rootScope, $delegate, $browser) {
1094
+ var definitions = [],
1095
+ expectations = [],
1096
+ responses = [],
1097
+ responsesPush = angular.bind(responses, responses.push),
1098
+ copy = angular.copy;
1099
+
1100
+ function createResponse(status, data, headers, statusText) {
1101
+ if (angular.isFunction(status)) return status;
1102
+
1103
+ return function() {
1104
+ return angular.isNumber(status)
1105
+ ? [status, data, headers, statusText]
1106
+ : [200, status, data];
1107
+ };
1108
+ }
1109
+
1110
+ // TODO(vojta): change params to: method, url, data, headers, callback
1111
+ function $httpBackend(method, url, data, callback, headers, timeout, withCredentials) {
1112
+ var xhr = new MockXhr(),
1113
+ expectation = expectations[0],
1114
+ wasExpected = false;
1115
+
1116
+ function prettyPrint(data) {
1117
+ return (angular.isString(data) || angular.isFunction(data) || data instanceof RegExp)
1118
+ ? data
1119
+ : angular.toJson(data);
1120
+ }
1121
+
1122
+ function wrapResponse(wrapped) {
1123
+ if (!$browser && timeout && timeout.then) timeout.then(handleTimeout);
1124
+
1125
+ return handleResponse;
1126
+
1127
+ function handleResponse() {
1128
+ var response = wrapped.response(method, url, data, headers);
1129
+ xhr.$$respHeaders = response[2];
1130
+ callback(copy(response[0]), copy(response[1]), xhr.getAllResponseHeaders(),
1131
+ copy(response[3] || ''));
1132
+ }
1133
+
1134
+ function handleTimeout() {
1135
+ for (var i = 0, ii = responses.length; i < ii; i++) {
1136
+ if (responses[i] === handleResponse) {
1137
+ responses.splice(i, 1);
1138
+ callback(-1, undefined, '');
1139
+ break;
1140
+ }
1141
+ }
1142
+ }
1143
+ }
1144
+
1145
+ if (expectation && expectation.match(method, url)) {
1146
+ if (!expectation.matchData(data))
1147
+ throw new Error('Expected ' + expectation + ' with different data\n' +
1148
+ 'EXPECTED: ' + prettyPrint(expectation.data) + '\nGOT: ' + data);
1149
+
1150
+ if (!expectation.matchHeaders(headers))
1151
+ throw new Error('Expected ' + expectation + ' with different headers\n' +
1152
+ 'EXPECTED: ' + prettyPrint(expectation.headers) + '\nGOT: ' +
1153
+ prettyPrint(headers));
1154
+
1155
+ expectations.shift();
1156
+
1157
+ if (expectation.response) {
1158
+ responses.push(wrapResponse(expectation));
1159
+ return;
1160
+ }
1161
+ wasExpected = true;
1162
+ }
1163
+
1164
+ var i = -1, definition;
1165
+ while ((definition = definitions[++i])) {
1166
+ if (definition.match(method, url, data, headers || {})) {
1167
+ if (definition.response) {
1168
+ // if $browser specified, we do auto flush all requests
1169
+ ($browser ? $browser.defer : responsesPush)(wrapResponse(definition));
1170
+ } else if (definition.passThrough) {
1171
+ $delegate(method, url, data, callback, headers, timeout, withCredentials);
1172
+ } else throw new Error('No response defined !');
1173
+ return;
1174
+ }
1175
+ }
1176
+ throw wasExpected ?
1177
+ new Error('No response defined !') :
1178
+ new Error('Unexpected request: ' + method + ' ' + url + '\n' +
1179
+ (expectation ? 'Expected ' + expectation : 'No more request expected'));
1180
+ }
1181
+
1182
+ /**
1183
+ * @ngdoc method
1184
+ * @name $httpBackend#when
1185
+ * @description
1186
+ * Creates a new backend definition.
1187
+ *
1188
+ * @param {string} method HTTP method.
1189
+ * @param {string|RegExp} url HTTP url.
1190
+ * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
1191
+ * data string and returns true if the data is as expected.
1192
+ * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
1193
+ * object and returns true if the headers match the current definition.
1194
+ * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1195
+ * request is handled.
1196
+ *
1197
+ * - respond –
1198
+ * `{function([status,] data[, headers, statusText])
1199
+ * | function(function(method, url, data, headers)}`
1200
+ * – The respond method takes a set of static data to be returned or a function that can
1201
+ * return an array containing response status (number), response data (string), response
1202
+ * headers (Object), and the text for the status (string).
1203
+ */
1204
+ $httpBackend.when = function(method, url, data, headers) {
1205
+ var definition = new MockHttpExpectation(method, url, data, headers),
1206
+ chain = {
1207
+ respond: function(status, data, headers, statusText) {
1208
+ definition.response = createResponse(status, data, headers, statusText);
1209
+ }
1210
+ };
1211
+
1212
+ if ($browser) {
1213
+ chain.passThrough = function() {
1214
+ definition.passThrough = true;
1215
+ };
1216
+ }
1217
+
1218
+ definitions.push(definition);
1219
+ return chain;
1220
+ };
1221
+
1222
+ /**
1223
+ * @ngdoc method
1224
+ * @name $httpBackend#whenGET
1225
+ * @description
1226
+ * Creates a new backend definition for GET requests. For more info see `when()`.
1227
+ *
1228
+ * @param {string|RegExp} url HTTP url.
1229
+ * @param {(Object|function(Object))=} headers HTTP headers.
1230
+ * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1231
+ * request is handled.
1232
+ */
1233
+
1234
+ /**
1235
+ * @ngdoc method
1236
+ * @name $httpBackend#whenHEAD
1237
+ * @description
1238
+ * Creates a new backend definition for HEAD requests. For more info see `when()`.
1239
+ *
1240
+ * @param {string|RegExp} url HTTP url.
1241
+ * @param {(Object|function(Object))=} headers HTTP headers.
1242
+ * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1243
+ * request is handled.
1244
+ */
1245
+
1246
+ /**
1247
+ * @ngdoc method
1248
+ * @name $httpBackend#whenDELETE
1249
+ * @description
1250
+ * Creates a new backend definition for DELETE requests. For more info see `when()`.
1251
+ *
1252
+ * @param {string|RegExp} url HTTP url.
1253
+ * @param {(Object|function(Object))=} headers HTTP headers.
1254
+ * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1255
+ * request is handled.
1256
+ */
1257
+
1258
+ /**
1259
+ * @ngdoc method
1260
+ * @name $httpBackend#whenPOST
1261
+ * @description
1262
+ * Creates a new backend definition for POST requests. For more info see `when()`.
1263
+ *
1264
+ * @param {string|RegExp} url HTTP url.
1265
+ * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
1266
+ * data string and returns true if the data is as expected.
1267
+ * @param {(Object|function(Object))=} headers HTTP headers.
1268
+ * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1269
+ * request is handled.
1270
+ */
1271
+
1272
+ /**
1273
+ * @ngdoc method
1274
+ * @name $httpBackend#whenPUT
1275
+ * @description
1276
+ * Creates a new backend definition for PUT requests. For more info see `when()`.
1277
+ *
1278
+ * @param {string|RegExp} url HTTP url.
1279
+ * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
1280
+ * data string and returns true if the data is as expected.
1281
+ * @param {(Object|function(Object))=} headers HTTP headers.
1282
+ * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1283
+ * request is handled.
1284
+ */
1285
+
1286
+ /**
1287
+ * @ngdoc method
1288
+ * @name $httpBackend#whenJSONP
1289
+ * @description
1290
+ * Creates a new backend definition for JSONP requests. For more info see `when()`.
1291
+ *
1292
+ * @param {string|RegExp} url HTTP url.
1293
+ * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1294
+ * request is handled.
1295
+ */
1296
+ createShortMethods('when');
1297
+
1298
+
1299
+ /**
1300
+ * @ngdoc method
1301
+ * @name $httpBackend#expect
1302
+ * @description
1303
+ * Creates a new request expectation.
1304
+ *
1305
+ * @param {string} method HTTP method.
1306
+ * @param {string|RegExp} url HTTP url.
1307
+ * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
1308
+ * receives data string and returns true if the data is as expected, or Object if request body
1309
+ * is in JSON format.
1310
+ * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
1311
+ * object and returns true if the headers match the current expectation.
1312
+ * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1313
+ * request is handled.
1314
+ *
1315
+ * - respond –
1316
+ * `{function([status,] data[, headers, statusText])
1317
+ * | function(function(method, url, data, headers)}`
1318
+ * – The respond method takes a set of static data to be returned or a function that can
1319
+ * return an array containing response status (number), response data (string), response
1320
+ * headers (Object), and the text for the status (string).
1321
+ */
1322
+ $httpBackend.expect = function(method, url, data, headers) {
1323
+ var expectation = new MockHttpExpectation(method, url, data, headers);
1324
+ expectations.push(expectation);
1325
+ return {
1326
+ respond: function (status, data, headers, statusText) {
1327
+ expectation.response = createResponse(status, data, headers, statusText);
1328
+ }
1329
+ };
1330
+ };
1331
+
1332
+
1333
+ /**
1334
+ * @ngdoc method
1335
+ * @name $httpBackend#expectGET
1336
+ * @description
1337
+ * Creates a new request expectation for GET requests. For more info see `expect()`.
1338
+ *
1339
+ * @param {string|RegExp} url HTTP url.
1340
+ * @param {Object=} headers HTTP headers.
1341
+ * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1342
+ * request is handled. See #expect for more info.
1343
+ */
1344
+
1345
+ /**
1346
+ * @ngdoc method
1347
+ * @name $httpBackend#expectHEAD
1348
+ * @description
1349
+ * Creates a new request expectation for HEAD requests. For more info see `expect()`.
1350
+ *
1351
+ * @param {string|RegExp} url HTTP url.
1352
+ * @param {Object=} headers HTTP headers.
1353
+ * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1354
+ * request is handled.
1355
+ */
1356
+
1357
+ /**
1358
+ * @ngdoc method
1359
+ * @name $httpBackend#expectDELETE
1360
+ * @description
1361
+ * Creates a new request expectation for DELETE requests. For more info see `expect()`.
1362
+ *
1363
+ * @param {string|RegExp} url HTTP url.
1364
+ * @param {Object=} headers HTTP headers.
1365
+ * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1366
+ * request is handled.
1367
+ */
1368
+
1369
+ /**
1370
+ * @ngdoc method
1371
+ * @name $httpBackend#expectPOST
1372
+ * @description
1373
+ * Creates a new request expectation for POST requests. For more info see `expect()`.
1374
+ *
1375
+ * @param {string|RegExp} url HTTP url.
1376
+ * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
1377
+ * receives data string and returns true if the data is as expected, or Object if request body
1378
+ * is in JSON format.
1379
+ * @param {Object=} headers HTTP headers.
1380
+ * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1381
+ * request is handled.
1382
+ */
1383
+
1384
+ /**
1385
+ * @ngdoc method
1386
+ * @name $httpBackend#expectPUT
1387
+ * @description
1388
+ * Creates a new request expectation for PUT requests. For more info see `expect()`.
1389
+ *
1390
+ * @param {string|RegExp} url HTTP url.
1391
+ * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
1392
+ * receives data string and returns true if the data is as expected, or Object if request body
1393
+ * is in JSON format.
1394
+ * @param {Object=} headers HTTP headers.
1395
+ * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1396
+ * request is handled.
1397
+ */
1398
+
1399
+ /**
1400
+ * @ngdoc method
1401
+ * @name $httpBackend#expectPATCH
1402
+ * @description
1403
+ * Creates a new request expectation for PATCH requests. For more info see `expect()`.
1404
+ *
1405
+ * @param {string|RegExp} url HTTP url.
1406
+ * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
1407
+ * receives data string and returns true if the data is as expected, or Object if request body
1408
+ * is in JSON format.
1409
+ * @param {Object=} headers HTTP headers.
1410
+ * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1411
+ * request is handled.
1412
+ */
1413
+
1414
+ /**
1415
+ * @ngdoc method
1416
+ * @name $httpBackend#expectJSONP
1417
+ * @description
1418
+ * Creates a new request expectation for JSONP requests. For more info see `expect()`.
1419
+ *
1420
+ * @param {string|RegExp} url HTTP url.
1421
+ * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1422
+ * request is handled.
1423
+ */
1424
+ createShortMethods('expect');
1425
+
1426
+
1427
+ /**
1428
+ * @ngdoc method
1429
+ * @name $httpBackend#flush
1430
+ * @description
1431
+ * Flushes all pending requests using the trained responses.
1432
+ *
1433
+ * @param {number=} count Number of responses to flush (in the order they arrived). If undefined,
1434
+ * all pending requests will be flushed. If there are no pending requests when the flush method
1435
+ * is called an exception is thrown (as this typically a sign of programming error).
1436
+ */
1437
+ $httpBackend.flush = function(count) {
1438
+ $rootScope.$digest();
1439
+ if (!responses.length) throw new Error('No pending request to flush !');
1440
+
1441
+ if (angular.isDefined(count)) {
1442
+ while (count--) {
1443
+ if (!responses.length) throw new Error('No more pending request to flush !');
1444
+ responses.shift()();
1445
+ }
1446
+ } else {
1447
+ while (responses.length) {
1448
+ responses.shift()();
1449
+ }
1450
+ }
1451
+ $httpBackend.verifyNoOutstandingExpectation();
1452
+ };
1453
+
1454
+
1455
+ /**
1456
+ * @ngdoc method
1457
+ * @name $httpBackend#verifyNoOutstandingExpectation
1458
+ * @description
1459
+ * Verifies that all of the requests defined via the `expect` api were made. If any of the
1460
+ * requests were not made, verifyNoOutstandingExpectation throws an exception.
1461
+ *
1462
+ * Typically, you would call this method following each test case that asserts requests using an
1463
+ * "afterEach" clause.
1464
+ *
1465
+ * ```js
1466
+ * afterEach($httpBackend.verifyNoOutstandingExpectation);
1467
+ * ```
1468
+ */
1469
+ $httpBackend.verifyNoOutstandingExpectation = function() {
1470
+ $rootScope.$digest();
1471
+ if (expectations.length) {
1472
+ throw new Error('Unsatisfied requests: ' + expectations.join(', '));
1473
+ }
1474
+ };
1475
+
1476
+
1477
+ /**
1478
+ * @ngdoc method
1479
+ * @name $httpBackend#verifyNoOutstandingRequest
1480
+ * @description
1481
+ * Verifies that there are no outstanding requests that need to be flushed.
1482
+ *
1483
+ * Typically, you would call this method following each test case that asserts requests using an
1484
+ * "afterEach" clause.
1485
+ *
1486
+ * ```js
1487
+ * afterEach($httpBackend.verifyNoOutstandingRequest);
1488
+ * ```
1489
+ */
1490
+ $httpBackend.verifyNoOutstandingRequest = function() {
1491
+ if (responses.length) {
1492
+ throw new Error('Unflushed requests: ' + responses.length);
1493
+ }
1494
+ };
1495
+
1496
+
1497
+ /**
1498
+ * @ngdoc method
1499
+ * @name $httpBackend#resetExpectations
1500
+ * @description
1501
+ * Resets all request expectations, but preserves all backend definitions. Typically, you would
1502
+ * call resetExpectations during a multiple-phase test when you want to reuse the same instance of
1503
+ * $httpBackend mock.
1504
+ */
1505
+ $httpBackend.resetExpectations = function() {
1506
+ expectations.length = 0;
1507
+ responses.length = 0;
1508
+ };
1509
+
1510
+ return $httpBackend;
1511
+
1512
+
1513
+ function createShortMethods(prefix) {
1514
+ angular.forEach(['GET', 'DELETE', 'JSONP'], function(method) {
1515
+ $httpBackend[prefix + method] = function(url, headers) {
1516
+ return $httpBackend[prefix](method, url, undefined, headers);
1517
+ };
1518
+ });
1519
+
1520
+ angular.forEach(['PUT', 'POST', 'PATCH'], function(method) {
1521
+ $httpBackend[prefix + method] = function(url, data, headers) {
1522
+ return $httpBackend[prefix](method, url, data, headers);
1523
+ };
1524
+ });
1525
+ }
1526
+ }
1527
+
1528
+ function MockHttpExpectation(method, url, data, headers) {
1529
+
1530
+ this.data = data;
1531
+ this.headers = headers;
1532
+
1533
+ this.match = function(m, u, d, h) {
1534
+ if (method != m) return false;
1535
+ if (!this.matchUrl(u)) return false;
1536
+ if (angular.isDefined(d) && !this.matchData(d)) return false;
1537
+ if (angular.isDefined(h) && !this.matchHeaders(h)) return false;
1538
+ return true;
1539
+ };
1540
+
1541
+ this.matchUrl = function(u) {
1542
+ if (!url) return true;
1543
+ if (angular.isFunction(url.test)) return url.test(u);
1544
+ return url == u;
1545
+ };
1546
+
1547
+ this.matchHeaders = function(h) {
1548
+ if (angular.isUndefined(headers)) return true;
1549
+ if (angular.isFunction(headers)) return headers(h);
1550
+ return angular.equals(headers, h);
1551
+ };
1552
+
1553
+ this.matchData = function(d) {
1554
+ if (angular.isUndefined(data)) return true;
1555
+ if (data && angular.isFunction(data.test)) return data.test(d);
1556
+ if (data && angular.isFunction(data)) return data(d);
1557
+ if (data && !angular.isString(data)) return angular.equals(data, angular.fromJson(d));
1558
+ return data == d;
1559
+ };
1560
+
1561
+ this.toString = function() {
1562
+ return method + ' ' + url;
1563
+ };
1564
+ }
1565
+
1566
+ function createMockXhr() {
1567
+ return new MockXhr();
1568
+ }
1569
+
1570
+ function MockXhr() {
1571
+
1572
+ // hack for testing $http, $httpBackend
1573
+ MockXhr.$$lastInstance = this;
1574
+
1575
+ this.open = function(method, url, async) {
1576
+ this.$$method = method;
1577
+ this.$$url = url;
1578
+ this.$$async = async;
1579
+ this.$$reqHeaders = {};
1580
+ this.$$respHeaders = {};
1581
+ };
1582
+
1583
+ this.send = function(data) {
1584
+ this.$$data = data;
1585
+ };
1586
+
1587
+ this.setRequestHeader = function(key, value) {
1588
+ this.$$reqHeaders[key] = value;
1589
+ };
1590
+
1591
+ this.getResponseHeader = function(name) {
1592
+ // the lookup must be case insensitive,
1593
+ // that's why we try two quick lookups first and full scan last
1594
+ var header = this.$$respHeaders[name];
1595
+ if (header) return header;
1596
+
1597
+ name = angular.lowercase(name);
1598
+ header = this.$$respHeaders[name];
1599
+ if (header) return header;
1600
+
1601
+ header = undefined;
1602
+ angular.forEach(this.$$respHeaders, function(headerVal, headerName) {
1603
+ if (!header && angular.lowercase(headerName) == name) header = headerVal;
1604
+ });
1605
+ return header;
1606
+ };
1607
+
1608
+ this.getAllResponseHeaders = function() {
1609
+ var lines = [];
1610
+
1611
+ angular.forEach(this.$$respHeaders, function(value, key) {
1612
+ lines.push(key + ': ' + value);
1613
+ });
1614
+ return lines.join('\n');
1615
+ };
1616
+
1617
+ this.abort = angular.noop;
1618
+ }
1619
+
1620
+
1621
+ /**
1622
+ * @ngdoc service
1623
+ * @name $timeout
1624
+ * @description
1625
+ *
1626
+ * This service is just a simple decorator for {@link ng.$timeout $timeout} service
1627
+ * that adds a "flush" and "verifyNoPendingTasks" methods.
1628
+ */
1629
+
1630
+ angular.mock.$TimeoutDecorator = function($delegate, $browser) {
1631
+
1632
+ /**
1633
+ * @ngdoc method
1634
+ * @name $timeout#flush
1635
+ * @description
1636
+ *
1637
+ * Flushes the queue of pending tasks.
1638
+ *
1639
+ * @param {number=} delay maximum timeout amount to flush up until
1640
+ */
1641
+ $delegate.flush = function(delay) {
1642
+ $browser.defer.flush(delay);
1643
+ };
1644
+
1645
+ /**
1646
+ * @ngdoc method
1647
+ * @name $timeout#verifyNoPendingTasks
1648
+ * @description
1649
+ *
1650
+ * Verifies that there are no pending tasks that need to be flushed.
1651
+ */
1652
+ $delegate.verifyNoPendingTasks = function() {
1653
+ if ($browser.deferredFns.length) {
1654
+ throw new Error('Deferred tasks to flush (' + $browser.deferredFns.length + '): ' +
1655
+ formatPendingTasksAsString($browser.deferredFns));
1656
+ }
1657
+ };
1658
+
1659
+ function formatPendingTasksAsString(tasks) {
1660
+ var result = [];
1661
+ angular.forEach(tasks, function(task) {
1662
+ result.push('{id: ' + task.id + ', ' + 'time: ' + task.time + '}');
1663
+ });
1664
+
1665
+ return result.join(', ');
1666
+ }
1667
+
1668
+ return $delegate;
1669
+ };
1670
+
1671
+ angular.mock.$RAFDecorator = function($delegate) {
1672
+ var queue = [];
1673
+ var rafFn = function(fn) {
1674
+ var index = queue.length;
1675
+ queue.push(fn);
1676
+ return function() {
1677
+ queue.splice(index, 1);
1678
+ };
1679
+ };
1680
+
1681
+ rafFn.supported = $delegate.supported;
1682
+
1683
+ rafFn.flush = function() {
1684
+ if(queue.length === 0) {
1685
+ throw new Error('No rAF callbacks present');
1686
+ }
1687
+
1688
+ var length = queue.length;
1689
+ for(var i=0;i<length;i++) {
1690
+ queue[i]();
1691
+ }
1692
+
1693
+ queue = [];
1694
+ };
1695
+
1696
+ return rafFn;
1697
+ };
1698
+
1699
+ angular.mock.$AsyncCallbackDecorator = function($delegate) {
1700
+ var callbacks = [];
1701
+ var addFn = function(fn) {
1702
+ callbacks.push(fn);
1703
+ };
1704
+ addFn.flush = function() {
1705
+ angular.forEach(callbacks, function(fn) {
1706
+ fn();
1707
+ });
1708
+ callbacks = [];
1709
+ };
1710
+ return addFn;
1711
+ };
1712
+
1713
+ /**
1714
+ *
1715
+ */
1716
+ angular.mock.$RootElementProvider = function() {
1717
+ this.$get = function() {
1718
+ return angular.element('<div ng-app></div>');
1719
+ };
1720
+ };
1721
+
1722
+ /**
1723
+ * @ngdoc module
1724
+ * @name ngMock
1725
+ * @packageName angular-mocks
1726
+ * @description
1727
+ *
1728
+ * # ngMock
1729
+ *
1730
+ * The `ngMock` module provides support to inject and mock Angular services into unit tests.
1731
+ * In addition, ngMock also extends various core ng services such that they can be
1732
+ * inspected and controlled in a synchronous manner within test code.
1733
+ *
1734
+ *
1735
+ * <div doc-module-components="ngMock"></div>
1736
+ *
1737
+ */
1738
+ angular.module('ngMock', ['ng']).provider({
1739
+ $browser: angular.mock.$BrowserProvider,
1740
+ $exceptionHandler: angular.mock.$ExceptionHandlerProvider,
1741
+ $log: angular.mock.$LogProvider,
1742
+ $interval: angular.mock.$IntervalProvider,
1743
+ $httpBackend: angular.mock.$HttpBackendProvider,
1744
+ $rootElement: angular.mock.$RootElementProvider
1745
+ }).config(['$provide', function($provide) {
1746
+ $provide.decorator('$timeout', angular.mock.$TimeoutDecorator);
1747
+ $provide.decorator('$$rAF', angular.mock.$RAFDecorator);
1748
+ $provide.decorator('$$asyncCallback', angular.mock.$AsyncCallbackDecorator);
1749
+ }]);
1750
+
1751
+ /**
1752
+ * @ngdoc module
1753
+ * @name ngMockE2E
1754
+ * @module ngMockE2E
1755
+ * @packageName angular-mocks
1756
+ * @description
1757
+ *
1758
+ * The `ngMockE2E` is an angular module which contains mocks suitable for end-to-end testing.
1759
+ * Currently there is only one mock present in this module -
1760
+ * the {@link ngMockE2E.$httpBackend e2e $httpBackend} mock.
1761
+ */
1762
+ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
1763
+ $provide.decorator('$httpBackend', angular.mock.e2e.$httpBackendDecorator);
1764
+ }]);
1765
+
1766
+ /**
1767
+ * @ngdoc service
1768
+ * @name $httpBackend
1769
+ * @module ngMockE2E
1770
+ * @description
1771
+ * Fake HTTP backend implementation suitable for end-to-end testing or backend-less development of
1772
+ * applications that use the {@link ng.$http $http service}.
1773
+ *
1774
+ * *Note*: For fake http backend implementation suitable for unit testing please see
1775
+ * {@link ngMock.$httpBackend unit-testing $httpBackend mock}.
1776
+ *
1777
+ * This implementation can be used to respond with static or dynamic responses via the `when` api
1778
+ * and its shortcuts (`whenGET`, `whenPOST`, etc) and optionally pass through requests to the
1779
+ * real $httpBackend for specific requests (e.g. to interact with certain remote apis or to fetch
1780
+ * templates from a webserver).
1781
+ *
1782
+ * As opposed to unit-testing, in an end-to-end testing scenario or in scenario when an application
1783
+ * is being developed with the real backend api replaced with a mock, it is often desirable for
1784
+ * certain category of requests to bypass the mock and issue a real http request (e.g. to fetch
1785
+ * templates or static files from the webserver). To configure the backend with this behavior
1786
+ * use the `passThrough` request handler of `when` instead of `respond`.
1787
+ *
1788
+ * Additionally, we don't want to manually have to flush mocked out requests like we do during unit
1789
+ * testing. For this reason the e2e $httpBackend automatically flushes mocked out requests
1790
+ * automatically, closely simulating the behavior of the XMLHttpRequest object.
1791
+ *
1792
+ * To setup the application to run with this http backend, you have to create a module that depends
1793
+ * on the `ngMockE2E` and your application modules and defines the fake backend:
1794
+ *
1795
+ * ```js
1796
+ * myAppDev = angular.module('myAppDev', ['myApp', 'ngMockE2E']);
1797
+ * myAppDev.run(function($httpBackend) {
1798
+ * phones = [{name: 'phone1'}, {name: 'phone2'}];
1799
+ *
1800
+ * // returns the current list of phones
1801
+ * $httpBackend.whenGET('/phones').respond(phones);
1802
+ *
1803
+ * // adds a new phone to the phones array
1804
+ * $httpBackend.whenPOST('/phones').respond(function(method, url, data) {
1805
+ * var phone = angular.fromJson(data);
1806
+ * phones.push(phone);
1807
+ * return [200, phone, {}];
1808
+ * });
1809
+ * $httpBackend.whenGET(/^\/templates\//).passThrough();
1810
+ * //...
1811
+ * });
1812
+ * ```
1813
+ *
1814
+ * Afterwards, bootstrap your app with this new module.
1815
+ */
1816
+
1817
+ /**
1818
+ * @ngdoc method
1819
+ * @name $httpBackend#when
1820
+ * @module ngMockE2E
1821
+ * @description
1822
+ * Creates a new backend definition.
1823
+ *
1824
+ * @param {string} method HTTP method.
1825
+ * @param {string|RegExp} url HTTP url.
1826
+ * @param {(string|RegExp)=} data HTTP request body.
1827
+ * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
1828
+ * object and returns true if the headers match the current definition.
1829
+ * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1830
+ * control how a matched request is handled.
1831
+ *
1832
+ * - respond –
1833
+ * `{function([status,] data[, headers, statusText])
1834
+ * | function(function(method, url, data, headers)}`
1835
+ * – The respond method takes a set of static data to be returned or a function that can return
1836
+ * an array containing response status (number), response data (string), response headers
1837
+ * (Object), and the text for the status (string).
1838
+ * - passThrough – `{function()}` – Any request matching a backend definition with
1839
+ * `passThrough` handler will be passed through to the real backend (an XHR request will be made
1840
+ * to the server.)
1841
+ */
1842
+
1843
+ /**
1844
+ * @ngdoc method
1845
+ * @name $httpBackend#whenGET
1846
+ * @module ngMockE2E
1847
+ * @description
1848
+ * Creates a new backend definition for GET requests. For more info see `when()`.
1849
+ *
1850
+ * @param {string|RegExp} url HTTP url.
1851
+ * @param {(Object|function(Object))=} headers HTTP headers.
1852
+ * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1853
+ * control how a matched request is handled.
1854
+ */
1855
+
1856
+ /**
1857
+ * @ngdoc method
1858
+ * @name $httpBackend#whenHEAD
1859
+ * @module ngMockE2E
1860
+ * @description
1861
+ * Creates a new backend definition for HEAD requests. For more info see `when()`.
1862
+ *
1863
+ * @param {string|RegExp} url HTTP url.
1864
+ * @param {(Object|function(Object))=} headers HTTP headers.
1865
+ * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1866
+ * control how a matched request is handled.
1867
+ */
1868
+
1869
+ /**
1870
+ * @ngdoc method
1871
+ * @name $httpBackend#whenDELETE
1872
+ * @module ngMockE2E
1873
+ * @description
1874
+ * Creates a new backend definition for DELETE requests. For more info see `when()`.
1875
+ *
1876
+ * @param {string|RegExp} url HTTP url.
1877
+ * @param {(Object|function(Object))=} headers HTTP headers.
1878
+ * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1879
+ * control how a matched request is handled.
1880
+ */
1881
+
1882
+ /**
1883
+ * @ngdoc method
1884
+ * @name $httpBackend#whenPOST
1885
+ * @module ngMockE2E
1886
+ * @description
1887
+ * Creates a new backend definition for POST requests. For more info see `when()`.
1888
+ *
1889
+ * @param {string|RegExp} url HTTP url.
1890
+ * @param {(string|RegExp)=} data HTTP request body.
1891
+ * @param {(Object|function(Object))=} headers HTTP headers.
1892
+ * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1893
+ * control how a matched request is handled.
1894
+ */
1895
+
1896
+ /**
1897
+ * @ngdoc method
1898
+ * @name $httpBackend#whenPUT
1899
+ * @module ngMockE2E
1900
+ * @description
1901
+ * Creates a new backend definition for PUT requests. For more info see `when()`.
1902
+ *
1903
+ * @param {string|RegExp} url HTTP url.
1904
+ * @param {(string|RegExp)=} data HTTP request body.
1905
+ * @param {(Object|function(Object))=} headers HTTP headers.
1906
+ * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1907
+ * control how a matched request is handled.
1908
+ */
1909
+
1910
+ /**
1911
+ * @ngdoc method
1912
+ * @name $httpBackend#whenPATCH
1913
+ * @module ngMockE2E
1914
+ * @description
1915
+ * Creates a new backend definition for PATCH requests. For more info see `when()`.
1916
+ *
1917
+ * @param {string|RegExp} url HTTP url.
1918
+ * @param {(string|RegExp)=} data HTTP request body.
1919
+ * @param {(Object|function(Object))=} headers HTTP headers.
1920
+ * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1921
+ * control how a matched request is handled.
1922
+ */
1923
+
1924
+ /**
1925
+ * @ngdoc method
1926
+ * @name $httpBackend#whenJSONP
1927
+ * @module ngMockE2E
1928
+ * @description
1929
+ * Creates a new backend definition for JSONP requests. For more info see `when()`.
1930
+ *
1931
+ * @param {string|RegExp} url HTTP url.
1932
+ * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1933
+ * control how a matched request is handled.
1934
+ */
1935
+ angular.mock.e2e = {};
1936
+ angular.mock.e2e.$httpBackendDecorator =
1937
+ ['$rootScope', '$delegate', '$browser', createHttpBackendMock];
1938
+
1939
+
1940
+ angular.mock.clearDataCache = function() {
1941
+ var key,
1942
+ cache = angular.element.cache;
1943
+
1944
+ for(key in cache) {
1945
+ if (Object.prototype.hasOwnProperty.call(cache,key)) {
1946
+ var handle = cache[key].handle;
1947
+
1948
+ handle && angular.element(handle.elem).off();
1949
+ delete cache[key];
1950
+ }
1951
+ }
1952
+ };
1953
+
1954
+
1955
+ if(window.jasmine || window.mocha) {
1956
+
1957
+ var currentSpec = null,
1958
+ isSpecRunning = function() {
1959
+ return !!currentSpec;
1960
+ };
1961
+
1962
+
1963
+ (window.beforeEach || window.setup)(function() {
1964
+ currentSpec = this;
1965
+ });
1966
+
1967
+ (window.afterEach || window.teardown)(function() {
1968
+ var injector = currentSpec.$injector;
1969
+
1970
+ angular.forEach(currentSpec.$modules, function(module) {
1971
+ if (module && module.$$hashKey) {
1972
+ module.$$hashKey = undefined;
1973
+ }
1974
+ });
1975
+
1976
+ currentSpec.$injector = null;
1977
+ currentSpec.$modules = null;
1978
+ currentSpec = null;
1979
+
1980
+ if (injector) {
1981
+ injector.get('$rootElement').off();
1982
+ injector.get('$browser').pollFns.length = 0;
1983
+ }
1984
+
1985
+ angular.mock.clearDataCache();
1986
+
1987
+ // clean up jquery's fragment cache
1988
+ angular.forEach(angular.element.fragments, function(val, key) {
1989
+ delete angular.element.fragments[key];
1990
+ });
1991
+
1992
+ MockXhr.$$lastInstance = null;
1993
+
1994
+ angular.forEach(angular.callbacks, function(val, key) {
1995
+ delete angular.callbacks[key];
1996
+ });
1997
+ angular.callbacks.counter = 0;
1998
+ });
1999
+
2000
+ /**
2001
+ * @ngdoc function
2002
+ * @name angular.mock.module
2003
+ * @description
2004
+ *
2005
+ * *NOTE*: This function is also published on window for easy access.<br>
2006
+ *
2007
+ * This function registers a module configuration code. It collects the configuration information
2008
+ * which will be used when the injector is created by {@link angular.mock.inject inject}.
2009
+ *
2010
+ * See {@link angular.mock.inject inject} for usage example
2011
+ *
2012
+ * @param {...(string|Function|Object)} fns any number of modules which are represented as string
2013
+ * aliases or as anonymous module initialization functions. The modules are used to
2014
+ * configure the injector. The 'ng' and 'ngMock' modules are automatically loaded. If an
2015
+ * object literal is passed they will be registered as values in the module, the key being
2016
+ * the module name and the value being what is returned.
2017
+ */
2018
+ window.module = angular.mock.module = function() {
2019
+ var moduleFns = Array.prototype.slice.call(arguments, 0);
2020
+ return isSpecRunning() ? workFn() : workFn;
2021
+ /////////////////////
2022
+ function workFn() {
2023
+ if (currentSpec.$injector) {
2024
+ throw new Error('Injector already created, can not register a module!');
2025
+ } else {
2026
+ var modules = currentSpec.$modules || (currentSpec.$modules = []);
2027
+ angular.forEach(moduleFns, function(module) {
2028
+ if (angular.isObject(module) && !angular.isArray(module)) {
2029
+ modules.push(function($provide) {
2030
+ angular.forEach(module, function(value, key) {
2031
+ $provide.value(key, value);
2032
+ });
2033
+ });
2034
+ } else {
2035
+ modules.push(module);
2036
+ }
2037
+ });
2038
+ }
2039
+ }
2040
+ };
2041
+
2042
+ /**
2043
+ * @ngdoc function
2044
+ * @name angular.mock.inject
2045
+ * @description
2046
+ *
2047
+ * *NOTE*: This function is also published on window for easy access.<br>
2048
+ *
2049
+ * The inject function wraps a function into an injectable function. The inject() creates new
2050
+ * instance of {@link auto.$injector $injector} per test, which is then used for
2051
+ * resolving references.
2052
+ *
2053
+ *
2054
+ * ## Resolving References (Underscore Wrapping)
2055
+ * Often, we would like to inject a reference once, in a `beforeEach()` block and reuse this
2056
+ * in multiple `it()` clauses. To be able to do this we must assign the reference to a variable
2057
+ * that is declared in the scope of the `describe()` block. Since we would, most likely, want
2058
+ * the variable to have the same name of the reference we have a problem, since the parameter
2059
+ * to the `inject()` function would hide the outer variable.
2060
+ *
2061
+ * To help with this, the injected parameters can, optionally, be enclosed with underscores.
2062
+ * These are ignored by the injector when the reference name is resolved.
2063
+ *
2064
+ * For example, the parameter `_myService_` would be resolved as the reference `myService`.
2065
+ * Since it is available in the function body as _myService_, we can then assign it to a variable
2066
+ * defined in an outer scope.
2067
+ *
2068
+ * ```
2069
+ * // Defined out reference variable outside
2070
+ * var myService;
2071
+ *
2072
+ * // Wrap the parameter in underscores
2073
+ * beforeEach( inject( function(_myService_){
2074
+ * myService = _myService_;
2075
+ * }));
2076
+ *
2077
+ * // Use myService in a series of tests.
2078
+ * it('makes use of myService', function() {
2079
+ * myService.doStuff();
2080
+ * });
2081
+ *
2082
+ * ```
2083
+ *
2084
+ * See also {@link angular.mock.module angular.mock.module}
2085
+ *
2086
+ * ## Example
2087
+ * Example of what a typical jasmine tests looks like with the inject method.
2088
+ * ```js
2089
+ *
2090
+ * angular.module('myApplicationModule', [])
2091
+ * .value('mode', 'app')
2092
+ * .value('version', 'v1.0.1');
2093
+ *
2094
+ *
2095
+ * describe('MyApp', function() {
2096
+ *
2097
+ * // You need to load modules that you want to test,
2098
+ * // it loads only the "ng" module by default.
2099
+ * beforeEach(module('myApplicationModule'));
2100
+ *
2101
+ *
2102
+ * // inject() is used to inject arguments of all given functions
2103
+ * it('should provide a version', inject(function(mode, version) {
2104
+ * expect(version).toEqual('v1.0.1');
2105
+ * expect(mode).toEqual('app');
2106
+ * }));
2107
+ *
2108
+ *
2109
+ * // The inject and module method can also be used inside of the it or beforeEach
2110
+ * it('should override a version and test the new version is injected', function() {
2111
+ * // module() takes functions or strings (module aliases)
2112
+ * module(function($provide) {
2113
+ * $provide.value('version', 'overridden'); // override version here
2114
+ * });
2115
+ *
2116
+ * inject(function(version) {
2117
+ * expect(version).toEqual('overridden');
2118
+ * });
2119
+ * });
2120
+ * });
2121
+ *
2122
+ * ```
2123
+ *
2124
+ * @param {...Function} fns any number of functions which will be injected using the injector.
2125
+ */
2126
+
2127
+
2128
+
2129
+ var ErrorAddingDeclarationLocationStack = function(e, errorForStack) {
2130
+ this.message = e.message;
2131
+ this.name = e.name;
2132
+ if (e.line) this.line = e.line;
2133
+ if (e.sourceId) this.sourceId = e.sourceId;
2134
+ if (e.stack && errorForStack)
2135
+ this.stack = e.stack + '\n' + errorForStack.stack;
2136
+ if (e.stackArray) this.stackArray = e.stackArray;
2137
+ };
2138
+ ErrorAddingDeclarationLocationStack.prototype.toString = Error.prototype.toString;
2139
+
2140
+ window.inject = angular.mock.inject = function() {
2141
+ var blockFns = Array.prototype.slice.call(arguments, 0);
2142
+ var errorForStack = new Error('Declaration Location');
2143
+ return isSpecRunning() ? workFn.call(currentSpec) : workFn;
2144
+ /////////////////////
2145
+ function workFn() {
2146
+ var modules = currentSpec.$modules || [];
2147
+
2148
+ modules.unshift('ngMock');
2149
+ modules.unshift('ng');
2150
+ var injector = currentSpec.$injector;
2151
+ if (!injector) {
2152
+ injector = currentSpec.$injector = angular.injector(modules);
2153
+ }
2154
+ for(var i = 0, ii = blockFns.length; i < ii; i++) {
2155
+ try {
2156
+ /* jshint -W040 *//* Jasmine explicitly provides a `this` object when calling functions */
2157
+ injector.invoke(blockFns[i] || angular.noop, this);
2158
+ /* jshint +W040 */
2159
+ } catch (e) {
2160
+ if (e.stack && errorForStack) {
2161
+ throw new ErrorAddingDeclarationLocationStack(e, errorForStack);
2162
+ }
2163
+ throw e;
2164
+ } finally {
2165
+ errorForStack = null;
2166
+ }
2167
+ }
2168
+ }
2169
+ };
2170
+ }
2171
+
2172
+
2173
+ })(window, window.angular);