tablo_connect 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (239) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +22 -0
  3. data/README.md +43 -0
  4. data/Rakefile +26 -0
  5. data/app/assets/fonts/tablo_connect/bootstrap/glyphicons-halflings-regular.eot +0 -0
  6. data/app/assets/fonts/tablo_connect/bootstrap/glyphicons-halflings-regular.svg +288 -0
  7. data/app/assets/fonts/tablo_connect/bootstrap/glyphicons-halflings-regular.ttf +0 -0
  8. data/app/assets/fonts/tablo_connect/bootstrap/glyphicons-halflings-regular.woff +0 -0
  9. data/app/assets/fonts/tablo_connect/bootstrap/glyphicons-halflings-regular.woff2 +0 -0
  10. data/app/assets/javascripts/tablo_connect/angular/angular-animate.js +3928 -0
  11. data/app/assets/javascripts/tablo_connect/angular/angular-animate.min.js +56 -0
  12. data/app/assets/javascripts/tablo_connect/angular/angular-animate.min.js.map +8 -0
  13. data/app/assets/javascripts/tablo_connect/angular/angular-aria.js +397 -0
  14. data/app/assets/javascripts/tablo_connect/angular/angular-aria.min.js +14 -0
  15. data/app/assets/javascripts/tablo_connect/angular/angular-aria.min.js.map +8 -0
  16. data/app/assets/javascripts/tablo_connect/angular/angular-cookies.js +321 -0
  17. data/app/assets/javascripts/tablo_connect/angular/angular-cookies.min.js +9 -0
  18. data/app/assets/javascripts/tablo_connect/angular/angular-cookies.min.js.map +8 -0
  19. data/app/assets/javascripts/tablo_connect/angular/angular-csp.css +21 -0
  20. data/app/assets/javascripts/tablo_connect/angular/angular-loader.js +443 -0
  21. data/app/assets/javascripts/tablo_connect/angular/angular-loader.min.js +10 -0
  22. data/app/assets/javascripts/tablo_connect/angular/angular-loader.min.js.map +8 -0
  23. data/app/assets/javascripts/tablo_connect/angular/angular-message-format.js +980 -0
  24. data/app/assets/javascripts/tablo_connect/angular/angular-message-format.min.js +26 -0
  25. data/app/assets/javascripts/tablo_connect/angular/angular-message-format.min.js.map +8 -0
  26. data/app/assets/javascripts/tablo_connect/angular/angular-messages.js +685 -0
  27. data/app/assets/javascripts/tablo_connect/angular/angular-messages.min.js +12 -0
  28. data/app/assets/javascripts/tablo_connect/angular/angular-messages.min.js.map +8 -0
  29. data/app/assets/javascripts/tablo_connect/angular/angular-mocks.js +2470 -0
  30. data/app/assets/javascripts/tablo_connect/angular/angular-resource.js +675 -0
  31. data/app/assets/javascripts/tablo_connect/angular/angular-resource.min.js +13 -0
  32. data/app/assets/javascripts/tablo_connect/angular/angular-resource.min.js.map +8 -0
  33. data/app/assets/javascripts/tablo_connect/angular/angular-route.js +991 -0
  34. data/app/assets/javascripts/tablo_connect/angular/angular-route.min.js +15 -0
  35. data/app/assets/javascripts/tablo_connect/angular/angular-route.min.js.map +8 -0
  36. data/app/assets/javascripts/tablo_connect/angular/angular-sanitize.js +683 -0
  37. data/app/assets/javascripts/tablo_connect/angular/angular-sanitize.min.js +16 -0
  38. data/app/assets/javascripts/tablo_connect/angular/angular-sanitize.min.js.map +8 -0
  39. data/app/assets/javascripts/tablo_connect/angular/angular-scenario.js +40324 -0
  40. data/app/assets/javascripts/tablo_connect/angular/angular-touch.js +628 -0
  41. data/app/assets/javascripts/tablo_connect/angular/angular-touch.min.js +13 -0
  42. data/app/assets/javascripts/tablo_connect/angular/angular-touch.min.js.map +8 -0
  43. data/app/assets/javascripts/tablo_connect/angular/angular-ui-router.js +4370 -0
  44. data/app/assets/javascripts/tablo_connect/angular/angular.js +28904 -0
  45. data/app/assets/javascripts/tablo_connect/angular/angular.min.js +294 -0
  46. data/app/assets/javascripts/tablo_connect/angular/angular.min.js.map +8 -0
  47. data/app/assets/javascripts/tablo_connect/angular/errors.json +1 -0
  48. data/app/assets/javascripts/tablo_connect/angular/version.json +1 -0
  49. data/app/assets/javascripts/tablo_connect/angular/version.txt +1 -0
  50. data/app/assets/javascripts/tablo_connect/application.js +21 -0
  51. data/app/assets/javascripts/tablo_connect/bootstrap/affix.js +162 -0
  52. data/app/assets/javascripts/tablo_connect/bootstrap/alert.js +94 -0
  53. data/app/assets/javascripts/tablo_connect/bootstrap/button.js +120 -0
  54. data/app/assets/javascripts/tablo_connect/bootstrap/carousel.js +237 -0
  55. data/app/assets/javascripts/tablo_connect/bootstrap/collapse.js +211 -0
  56. data/app/assets/javascripts/tablo_connect/bootstrap/dropdown.js +165 -0
  57. data/app/assets/javascripts/tablo_connect/bootstrap/modal.js +337 -0
  58. data/app/assets/javascripts/tablo_connect/bootstrap/popover.js +108 -0
  59. data/app/assets/javascripts/tablo_connect/bootstrap/scrollspy.js +172 -0
  60. data/app/assets/javascripts/tablo_connect/bootstrap/tab.js +155 -0
  61. data/app/assets/javascripts/tablo_connect/bootstrap/tooltip.js +514 -0
  62. data/app/assets/javascripts/tablo_connect/bootstrap/transition.js +59 -0
  63. data/app/assets/javascripts/tablo_connect/jquery/jquery.js +9210 -0
  64. data/app/assets/javascripts/tablo_connect/ng_app/app.js +39 -0
  65. data/app/assets/javascripts/tablo_connect/ng_app/controllers/episodes_controller.js +17 -0
  66. data/app/assets/javascripts/tablo_connect/ng_app/controllers/home_controller.js +9 -0
  67. data/app/assets/javascripts/tablo_connect/ng_app/controllers/movies_controller.js +17 -0
  68. data/app/assets/javascripts/tablo_connect/ng_app/controllers/shows_controller.js +9 -0
  69. data/app/assets/javascripts/tablo_connect/ng_app/filters/titleize_filter.js +17 -0
  70. data/app/assets/javascripts/tablo_connect/ng_app/filters/url_filter.js +19 -0
  71. data/app/assets/javascripts/tablo_connect/ng_app/modules/alerts/alerts.js +9 -0
  72. data/app/assets/javascripts/tablo_connect/ng_app/modules/alerts/directives/alerts_directive.js +19 -0
  73. data/app/assets/javascripts/tablo_connect/ng_app/modules/alerts/services/alerts_service.js +22 -0
  74. data/app/assets/javascripts/tablo_connect/ng_app/modules/alerts/templates/alerts.html +6 -0
  75. data/app/assets/javascripts/tablo_connect/ng_app/modules/tablo_sync/directives/tablo_sync_directive.js +31 -0
  76. data/app/assets/javascripts/tablo_connect/ng_app/modules/tablo_sync/services/tablo_sync_service.js +20 -0
  77. data/app/assets/javascripts/tablo_connect/ng_app/modules/tablo_sync/tablo_sync.js +9 -0
  78. data/app/assets/javascripts/tablo_connect/ng_app/modules/tablo_sync/templates/tablo_sync.html +4 -0
  79. data/app/assets/javascripts/tablo_connect/ng_app/services/tablo_service.js +98 -0
  80. data/app/assets/javascripts/tablo_connect/ng_app/templates/episodes.html +36 -0
  81. data/app/assets/javascripts/tablo_connect/ng_app/templates/index.html +3 -0
  82. data/app/assets/javascripts/tablo_connect/ng_app/templates/movies.html +33 -0
  83. data/app/assets/javascripts/tablo_connect/ng_app/templates/shows.html +13 -0
  84. data/app/assets/stylesheets/tablo_connect/_bootstrap.scss +56 -0
  85. data/app/assets/stylesheets/tablo_connect/_global.scss +19 -0
  86. data/app/assets/stylesheets/tablo_connect/_movies.scss +0 -0
  87. data/app/assets/stylesheets/tablo_connect/_shows.scss +30 -0
  88. data/app/assets/stylesheets/tablo_connect/application.scss +18 -0
  89. data/app/assets/stylesheets/tablo_connect/bootstrap/_alerts.scss +73 -0
  90. data/app/assets/stylesheets/tablo_connect/bootstrap/_badges.scss +68 -0
  91. data/app/assets/stylesheets/tablo_connect/bootstrap/_breadcrumbs.scss +26 -0
  92. data/app/assets/stylesheets/tablo_connect/bootstrap/_button-groups.scss +244 -0
  93. data/app/assets/stylesheets/tablo_connect/bootstrap/_buttons.scss +168 -0
  94. data/app/assets/stylesheets/tablo_connect/bootstrap/_carousel.scss +269 -0
  95. data/app/assets/stylesheets/tablo_connect/bootstrap/_close.scss +36 -0
  96. data/app/assets/stylesheets/tablo_connect/bootstrap/_code.scss +69 -0
  97. data/app/assets/stylesheets/tablo_connect/bootstrap/_component-animations.scss +37 -0
  98. data/app/assets/stylesheets/tablo_connect/bootstrap/_dropdowns.scss +216 -0
  99. data/app/assets/stylesheets/tablo_connect/bootstrap/_forms.scss +611 -0
  100. data/app/assets/stylesheets/tablo_connect/bootstrap/_glyphicons.scss +307 -0
  101. data/app/assets/stylesheets/tablo_connect/bootstrap/_grid.scss +84 -0
  102. data/app/assets/stylesheets/tablo_connect/bootstrap/_input-groups.scss +167 -0
  103. data/app/assets/stylesheets/tablo_connect/bootstrap/_jumbotron.scss +52 -0
  104. data/app/assets/stylesheets/tablo_connect/bootstrap/_labels.scss +66 -0
  105. data/app/assets/stylesheets/tablo_connect/bootstrap/_list-group.scss +130 -0
  106. data/app/assets/stylesheets/tablo_connect/bootstrap/_media.scss +66 -0
  107. data/app/assets/stylesheets/tablo_connect/bootstrap/_mixins.scss +40 -0
  108. data/app/assets/stylesheets/tablo_connect/bootstrap/_modals.scss +150 -0
  109. data/app/assets/stylesheets/tablo_connect/bootstrap/_navbar.scss +662 -0
  110. data/app/assets/stylesheets/tablo_connect/bootstrap/_navs.scss +242 -0
  111. data/app/assets/stylesheets/tablo_connect/bootstrap/_normalize.scss +424 -0
  112. data/app/assets/stylesheets/tablo_connect/bootstrap/_pager.scss +54 -0
  113. data/app/assets/stylesheets/tablo_connect/bootstrap/_pagination.scss +89 -0
  114. data/app/assets/stylesheets/tablo_connect/bootstrap/_panels.scss +271 -0
  115. data/app/assets/stylesheets/tablo_connect/bootstrap/_popovers.scss +131 -0
  116. data/app/assets/stylesheets/tablo_connect/bootstrap/_print.scss +101 -0
  117. data/app/assets/stylesheets/tablo_connect/bootstrap/_progress-bars.scss +87 -0
  118. data/app/assets/stylesheets/tablo_connect/bootstrap/_responsive-embed.scss +35 -0
  119. data/app/assets/stylesheets/tablo_connect/bootstrap/_responsive-utilities.scss +179 -0
  120. data/app/assets/stylesheets/tablo_connect/bootstrap/_scaffolding.scss +161 -0
  121. data/app/assets/stylesheets/tablo_connect/bootstrap/_tables.scss +234 -0
  122. data/app/assets/stylesheets/tablo_connect/bootstrap/_theme.scss +291 -0
  123. data/app/assets/stylesheets/tablo_connect/bootstrap/_thumbnails.scss +38 -0
  124. data/app/assets/stylesheets/tablo_connect/bootstrap/_tooltip.scss +101 -0
  125. data/app/assets/stylesheets/tablo_connect/bootstrap/_type.scss +298 -0
  126. data/app/assets/stylesheets/tablo_connect/bootstrap/_utilities.scss +55 -0
  127. data/app/assets/stylesheets/tablo_connect/bootstrap/_variables.scss +872 -0
  128. data/app/assets/stylesheets/tablo_connect/bootstrap/_wells.scss +29 -0
  129. data/app/assets/stylesheets/tablo_connect/bootstrap/mixins/_alerts.scss +14 -0
  130. data/app/assets/stylesheets/tablo_connect/bootstrap/mixins/_background-variant.scss +12 -0
  131. data/app/assets/stylesheets/tablo_connect/bootstrap/mixins/_border-radius.scss +18 -0
  132. data/app/assets/stylesheets/tablo_connect/bootstrap/mixins/_buttons.scss +68 -0
  133. data/app/assets/stylesheets/tablo_connect/bootstrap/mixins/_center-block.scss +7 -0
  134. data/app/assets/stylesheets/tablo_connect/bootstrap/mixins/_clearfix.scss +22 -0
  135. data/app/assets/stylesheets/tablo_connect/bootstrap/mixins/_forms.scss +88 -0
  136. data/app/assets/stylesheets/tablo_connect/bootstrap/mixins/_gradients.scss +58 -0
  137. data/app/assets/stylesheets/tablo_connect/bootstrap/mixins/_grid-framework.scss +81 -0
  138. data/app/assets/stylesheets/tablo_connect/bootstrap/mixins/_grid.scss +122 -0
  139. data/app/assets/stylesheets/tablo_connect/bootstrap/mixins/_hide-text.scss +21 -0
  140. data/app/assets/stylesheets/tablo_connect/bootstrap/mixins/_image.scss +33 -0
  141. data/app/assets/stylesheets/tablo_connect/bootstrap/mixins/_labels.scss +12 -0
  142. data/app/assets/stylesheets/tablo_connect/bootstrap/mixins/_list-group.scss +32 -0
  143. data/app/assets/stylesheets/tablo_connect/bootstrap/mixins/_nav-divider.scss +10 -0
  144. data/app/assets/stylesheets/tablo_connect/bootstrap/mixins/_nav-vertical-align.scss +9 -0
  145. data/app/assets/stylesheets/tablo_connect/bootstrap/mixins/_opacity.scss +8 -0
  146. data/app/assets/stylesheets/tablo_connect/bootstrap/mixins/_pagination.scss +24 -0
  147. data/app/assets/stylesheets/tablo_connect/bootstrap/mixins/_panels.scss +24 -0
  148. data/app/assets/stylesheets/tablo_connect/bootstrap/mixins/_progress-bar.scss +10 -0
  149. data/app/assets/stylesheets/tablo_connect/bootstrap/mixins/_reset-filter.scss +8 -0
  150. data/app/assets/stylesheets/tablo_connect/bootstrap/mixins/_reset-text.scss +18 -0
  151. data/app/assets/stylesheets/tablo_connect/bootstrap/mixins/_resize.scss +6 -0
  152. data/app/assets/stylesheets/tablo_connect/bootstrap/mixins/_responsive-visibility.scss +21 -0
  153. data/app/assets/stylesheets/tablo_connect/bootstrap/mixins/_size.scss +10 -0
  154. data/app/assets/stylesheets/tablo_connect/bootstrap/mixins/_tab-focus.scss +9 -0
  155. data/app/assets/stylesheets/tablo_connect/bootstrap/mixins/_table-row.scss +28 -0
  156. data/app/assets/stylesheets/tablo_connect/bootstrap/mixins/_text-emphasis.scss +12 -0
  157. data/app/assets/stylesheets/tablo_connect/bootstrap/mixins/_text-overflow.scss +8 -0
  158. data/app/assets/stylesheets/tablo_connect/bootstrap/mixins/_vendor-prefixes.scss +222 -0
  159. data/app/controllers/tablo_connect/application_controller.rb +4 -0
  160. data/app/controllers/tablo_connect/copy_controller.rb +75 -0
  161. data/app/controllers/tablo_connect/home_controller.rb +6 -0
  162. data/app/controllers/tablo_connect/movies_controller.rb +9 -0
  163. data/app/controllers/tablo_connect/shows_controller.rb +11 -0
  164. data/app/controllers/tablo_connect/sync_controller.rb +73 -0
  165. data/app/helpers/tablo_connect/application_helper.rb +4 -0
  166. data/app/helpers/tablo_connect/movies_helper.rb +4 -0
  167. data/app/helpers/tablo_connect/shows_helper.rb +4 -0
  168. data/app/models/tablo_connect/movie.rb +14 -0
  169. data/app/models/tablo_connect/show.rb +23 -0
  170. data/app/views/layouts/tablo_connect/_header.html.erb +20 -0
  171. data/app/views/layouts/tablo_connect/application.html.erb +19 -0
  172. data/app/views/tablo_connect/home/index.html.erb +1 -0
  173. data/config/routes.rb +10 -0
  174. data/db/migrate/20151101164611_create_tablo_connect_movies.rb +18 -0
  175. data/db/migrate/20151101165020_create_tablo_connect_shows.rb +21 -0
  176. data/lib/tablo_connect.rb +7 -0
  177. data/lib/tablo_connect/engine.rb +28 -0
  178. data/lib/tablo_connect/version.rb +3 -0
  179. data/lib/tasks/tablo_connect_tasks.rake +4 -0
  180. data/spec/controllers/tablo_connect/copy_controller_spec.rb +244 -0
  181. data/spec/controllers/tablo_connect/movies_controller_spec.rb +27 -0
  182. data/spec/controllers/tablo_connect/shows_controller_spec.rb +45 -0
  183. data/spec/controllers/tablo_connect/sync_controller_spec.rb +209 -0
  184. data/spec/factories/tablo_connect_movies.rb +11 -0
  185. data/spec/factories/tablo_connect_shows.rb +15 -0
  186. data/spec/javascript/angular-environment.js +1 -0
  187. data/spec/javascript/karma.conf.js +48 -0
  188. data/spec/javascript/unit/controllers/episodes_controller_spec.js +49 -0
  189. data/spec/javascript/unit/controllers/home_controller_spec.js +34 -0
  190. data/spec/javascript/unit/controllers/movies_controller_spec.js +55 -0
  191. data/spec/javascript/unit/controllers/shows_controller_spec.js +33 -0
  192. data/spec/javascript/unit/filters/titleize_filter_spec.js +25 -0
  193. data/spec/javascript/unit/filters/url_filter_spec.js +33 -0
  194. data/spec/javascript/unit/modules/alerts/directives/alerts_directive_spec.js +39 -0
  195. data/spec/javascript/unit/modules/alerts/services/alerts_service_spec.js +51 -0
  196. data/spec/javascript/unit/modules/tablo_sync/directives/tablo_sync_directive_spec.js +68 -0
  197. data/spec/javascript/unit/modules/tablo_sync/services/tablo_sync_service_spec.js +26 -0
  198. data/spec/javascript/unit/services/tablo_service_spec.js +28 -0
  199. data/spec/models/tablo_connect/movie_spec.rb +32 -0
  200. data/spec/models/tablo_connect/show_spec.rb +65 -0
  201. data/spec/spec_helper.rb +86 -0
  202. data/spec/test_app/README.rdoc +28 -0
  203. data/spec/test_app/Rakefile +6 -0
  204. data/spec/test_app/app/assets/javascripts/application.js +13 -0
  205. data/spec/test_app/app/assets/stylesheets/application.css +15 -0
  206. data/spec/test_app/app/controllers/application_controller.rb +5 -0
  207. data/spec/test_app/app/helpers/application_helper.rb +2 -0
  208. data/spec/test_app/app/views/layouts/application.html.erb +14 -0
  209. data/spec/test_app/bin/bundle +3 -0
  210. data/spec/test_app/bin/rails +4 -0
  211. data/spec/test_app/bin/rake +4 -0
  212. data/spec/test_app/bin/setup +29 -0
  213. data/spec/test_app/config.ru +4 -0
  214. data/spec/test_app/config/application.rb +32 -0
  215. data/spec/test_app/config/boot.rb +5 -0
  216. data/spec/test_app/config/database.yml +25 -0
  217. data/spec/test_app/config/environment.rb +5 -0
  218. data/spec/test_app/config/environments/development.rb +41 -0
  219. data/spec/test_app/config/environments/production.rb +79 -0
  220. data/spec/test_app/config/environments/test.rb +42 -0
  221. data/spec/test_app/config/initializers/assets.rb +11 -0
  222. data/spec/test_app/config/initializers/backtrace_silencers.rb +7 -0
  223. data/spec/test_app/config/initializers/cookies_serializer.rb +3 -0
  224. data/spec/test_app/config/initializers/filter_parameter_logging.rb +4 -0
  225. data/spec/test_app/config/initializers/inflections.rb +16 -0
  226. data/spec/test_app/config/initializers/mime_types.rb +4 -0
  227. data/spec/test_app/config/initializers/session_store.rb +3 -0
  228. data/spec/test_app/config/initializers/wrap_parameters.rb +14 -0
  229. data/spec/test_app/config/locales/en.yml +23 -0
  230. data/spec/test_app/config/routes.rb +4 -0
  231. data/spec/test_app/config/secrets.yml +22 -0
  232. data/spec/test_app/db/schema.rb +49 -0
  233. data/spec/test_app/db/test.sqlite3 +0 -0
  234. data/spec/test_app/log/test.log +5561 -0
  235. data/spec/test_app/public/404.html +67 -0
  236. data/spec/test_app/public/422.html +67 -0
  237. data/spec/test_app/public/500.html +66 -0
  238. data/spec/test_app/public/favicon.ico +0 -0
  239. metadata +550 -0
@@ -0,0 +1,12 @@
1
+ /*
2
+ AngularJS v1.4.7
3
+ (c) 2010-2015 Google, Inc. http://angularjs.org
4
+ License: MIT
5
+ */
6
+ (function(z,h,A){'use strict';function m(h){return["$animate",function(r){return{restrict:"AE",transclude:"element",terminal:!0,require:"^^ngMessages",link:function(n,f,a,g,l){var c=f[0],p,h=a.ngMessage||a.when;a=a.ngMessageExp||a.whenExp;var k=function(b){p=b?v(b)?b:b.split(/[\s,]+/):null;g.reRender()};a?(k(n.$eval(a)),n.$watchCollection(a,k)):k(h);var e,q;g.register(c,q={test:function(b){var a=p;b=a?v(a)?0<=a.indexOf(b):a.hasOwnProperty(b):void 0;return b},attach:function(){e||l(n,function(b){r.enter(b,
7
+ null,f);e=b;var a=e.$$attachId=g.getAttachId();e.on("$destroy",function(){e&&e.$$attachId===a&&(g.deregister(c),q.detach())})})},detach:function(){if(e){var b=e;e=null;r.leave(b)}}})}}}]}var v=h.isArray,w=h.forEach,x=h.isString,y=h.element;h.module("ngMessages",[]).directive("ngMessages",["$animate",function(h){function r(f,a){return x(a)&&0===a.length||n(f.$eval(a))}function n(f){return x(f)?f.length:!!f}return{require:"ngMessages",restrict:"AE",controller:["$element","$scope","$attrs",function(f,
8
+ a,g){function l(b,a){for(var d=a,f=[];d&&d!==b;){var c=d.$$ngMessageNode;if(c&&c.length)return k[c];d.childNodes.length&&-1==f.indexOf(d)?(f.push(d),d=d.childNodes[d.childNodes.length-1]):d=d.previousSibling||d.parentNode}}var c=this,p=0,m=0;this.getAttachId=function(){return m++};var k=this.messages={},e,q;this.render=function(b){b=b||{};e=!1;q=b;for(var p=r(a,g.ngMessagesMultiple)||r(a,g.multiple),d=[],k={},s=c.head,l=!1,m=0;null!=s;){m++;var t=s.message,u=!1;l||w(b,function(a,b){!u&&n(a)&&t.test(b)&&
9
+ !k[b]&&(u=k[b]=!0,t.attach())});u?l=!p:d.push(t);s=s.next}w(d,function(b){b.detach()});d.length!==m?h.setClass(f,"ng-active","ng-inactive"):h.setClass(f,"ng-inactive","ng-active")};a.$watchCollection(g.ngMessages||g["for"],c.render);this.reRender=function(){e||(e=!0,a.$evalAsync(function(){e&&q&&c.render(q)}))};this.register=function(b,a){var d=p.toString();k[d]={message:a};var e=f[0],g=k[d];c.head?(e=l(e,b))?(g.next=e.next,e.next=g):(g.next=c.head,c.head=g):c.head=g;b.$$ngMessageNode=d;p++;c.reRender()};
10
+ this.deregister=function(b){var a=b.$$ngMessageNode;delete b.$$ngMessageNode;var d=k[a];(b=l(f[0],b))?b.next=d.next:c.head=d.next;delete k[a];c.reRender()}}]}}]).directive("ngMessagesInclude",["$templateRequest","$document","$compile",function(h,m,n){return{restrict:"AE",require:"^^ngMessages",link:function(f,a,g){var l=g.ngMessagesInclude||g.src;h(l).then(function(c){n(c)(f,function(c){a.after(c);c=y(m[0].createComment(" ngMessagesInclude: "+l+" "));a.after(c);a.remove()})})}}}]).directive("ngMessage",
11
+ m("AE")).directive("ngMessageExp",m("A"))})(window,window.angular);
12
+ //# sourceMappingURL=angular-messages.min.js.map
@@ -0,0 +1,8 @@
1
+ {
2
+ "version":3,
3
+ "file":"angular-messages.min.js",
4
+ "lineCount":11,
5
+ "mappings":"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",
6
+ "sources":["angular-messages.js"],
7
+ "names":["window","angular","undefined","ngMessageDirectiveFactory","restrict","$animate","transclude","terminal","require","link","scope","element","attrs","ngMessagesCtrl","$transclude","commentNode","records","staticExp","ngMessage","when","dynamicExp","ngMessageExp","whenExp","assignRecords","items","isArray","split","reRender","$eval","$watchCollection","currentElement","messageCtrl","register","test","name","collection","indexOf","hasOwnProperty","attach","elm","enter","$$attachId","getAttachId","on","deregister","detach","leave","forEach","isString","jqLite","module","directive","isAttrTruthy","attr","length","truthy","val","controller","$element","$scope","$attrs","findPreviousMessage","parent","comment","prevNode","parentLookup","prevKey","$$ngMessageNode","messages","childNodes","push","previousSibling","parentNode","ctrl","latestKey","nextAttachId","this.getAttachId","renderLater","cachedCollection","render","this.render","multiple","ngMessagesMultiple","unmatchedMessages","matchedKeys","messageItem","head","messageFound","totalMessages","message","messageUsed","value","key","next","setClass","ACTIVE_CLASS","INACTIVE_CLASS","ngMessages","this.reRender","$evalAsync","this.register","nextKey","toString","messageNode","match","this.deregister","$templateRequest","$document","$compile","src","ngMessagesInclude","then","html","contents","after","anchor","createComment","remove"]
8
+ }
@@ -0,0 +1,2470 @@
1
+ /**
2
+ * @license AngularJS v1.4.7
3
+ * (c) 2010-2015 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 || self.$$state !== self.$$lastState) {
57
+ self.$$lastUrl = self.$$url;
58
+ self.$$lastState = self.$$state;
59
+ listener(self.$$url, self.$$state);
60
+ }
61
+ }
62
+ );
63
+
64
+ return listener;
65
+ };
66
+
67
+ self.$$applicationDestroyed = angular.noop;
68
+ self.$$checkUrlChange = angular.noop;
69
+
70
+ self.deferredFns = [];
71
+ self.deferredNextId = 0;
72
+
73
+ self.defer = function(fn, delay) {
74
+ delay = delay || 0;
75
+ self.deferredFns.push({time:(self.defer.now + delay), fn:fn, id: self.deferredNextId});
76
+ self.deferredFns.sort(function(a, b) { return a.time - b.time;});
77
+ return self.deferredNextId++;
78
+ };
79
+
80
+
81
+ /**
82
+ * @name $browser#defer.now
83
+ *
84
+ * @description
85
+ * Current milliseconds mock time.
86
+ */
87
+ self.defer.now = 0;
88
+
89
+
90
+ self.defer.cancel = function(deferId) {
91
+ var fnIndex;
92
+
93
+ angular.forEach(self.deferredFns, function(fn, index) {
94
+ if (fn.id === deferId) fnIndex = index;
95
+ });
96
+
97
+ if (angular.isDefined(fnIndex)) {
98
+ self.deferredFns.splice(fnIndex, 1);
99
+ return true;
100
+ }
101
+
102
+ return false;
103
+ };
104
+
105
+
106
+ /**
107
+ * @name $browser#defer.flush
108
+ *
109
+ * @description
110
+ * Flushes all pending requests and executes the defer callbacks.
111
+ *
112
+ * @param {number=} number of milliseconds to flush. See {@link #defer.now}
113
+ */
114
+ self.defer.flush = function(delay) {
115
+ if (angular.isDefined(delay)) {
116
+ self.defer.now += delay;
117
+ } else {
118
+ if (self.deferredFns.length) {
119
+ self.defer.now = self.deferredFns[self.deferredFns.length - 1].time;
120
+ } else {
121
+ throw new Error('No deferred tasks to be flushed');
122
+ }
123
+ }
124
+
125
+ while (self.deferredFns.length && self.deferredFns[0].time <= self.defer.now) {
126
+ self.deferredFns.shift().fn();
127
+ }
128
+ };
129
+
130
+ self.$$baseHref = '/';
131
+ self.baseHref = function() {
132
+ return this.$$baseHref;
133
+ };
134
+ };
135
+ angular.mock.$Browser.prototype = {
136
+
137
+ /**
138
+ * @name $browser#poll
139
+ *
140
+ * @description
141
+ * run all fns in pollFns
142
+ */
143
+ poll: function poll() {
144
+ angular.forEach(this.pollFns, function(pollFn) {
145
+ pollFn();
146
+ });
147
+ },
148
+
149
+ url: function(url, replace, state) {
150
+ if (angular.isUndefined(state)) {
151
+ state = null;
152
+ }
153
+ if (url) {
154
+ this.$$url = url;
155
+ // Native pushState serializes & copies the object; simulate it.
156
+ this.$$state = angular.copy(state);
157
+ return this;
158
+ }
159
+
160
+ return this.$$url;
161
+ },
162
+
163
+ state: function() {
164
+ return this.$$state;
165
+ },
166
+
167
+ notifyWhenNoOutstandingRequests: function(fn) {
168
+ fn();
169
+ }
170
+ };
171
+
172
+
173
+ /**
174
+ * @ngdoc provider
175
+ * @name $exceptionHandlerProvider
176
+ *
177
+ * @description
178
+ * Configures the mock implementation of {@link ng.$exceptionHandler} to rethrow or to log errors
179
+ * passed to the `$exceptionHandler`.
180
+ */
181
+
182
+ /**
183
+ * @ngdoc service
184
+ * @name $exceptionHandler
185
+ *
186
+ * @description
187
+ * Mock implementation of {@link ng.$exceptionHandler} that rethrows or logs errors passed
188
+ * to it. See {@link ngMock.$exceptionHandlerProvider $exceptionHandlerProvider} for configuration
189
+ * information.
190
+ *
191
+ *
192
+ * ```js
193
+ * describe('$exceptionHandlerProvider', function() {
194
+ *
195
+ * it('should capture log messages and exceptions', function() {
196
+ *
197
+ * module(function($exceptionHandlerProvider) {
198
+ * $exceptionHandlerProvider.mode('log');
199
+ * });
200
+ *
201
+ * inject(function($log, $exceptionHandler, $timeout) {
202
+ * $timeout(function() { $log.log(1); });
203
+ * $timeout(function() { $log.log(2); throw 'banana peel'; });
204
+ * $timeout(function() { $log.log(3); });
205
+ * expect($exceptionHandler.errors).toEqual([]);
206
+ * expect($log.assertEmpty());
207
+ * $timeout.flush();
208
+ * expect($exceptionHandler.errors).toEqual(['banana peel']);
209
+ * expect($log.log.logs).toEqual([[1], [2], [3]]);
210
+ * });
211
+ * });
212
+ * });
213
+ * ```
214
+ */
215
+
216
+ angular.mock.$ExceptionHandlerProvider = function() {
217
+ var handler;
218
+
219
+ /**
220
+ * @ngdoc method
221
+ * @name $exceptionHandlerProvider#mode
222
+ *
223
+ * @description
224
+ * Sets the logging mode.
225
+ *
226
+ * @param {string} mode Mode of operation, defaults to `rethrow`.
227
+ *
228
+ * - `log`: Sometimes it is desirable to test that an error is thrown, for this case the `log`
229
+ * mode stores an array of errors in `$exceptionHandler.errors`, to allow later
230
+ * assertion of them. See {@link ngMock.$log#assertEmpty assertEmpty()} and
231
+ * {@link ngMock.$log#reset reset()}
232
+ * - `rethrow`: If any errors are passed to the handler in tests, it typically means that there
233
+ * is a bug in the application or test, so this mock will make these tests fail.
234
+ * For any implementations that expect exceptions to be thrown, the `rethrow` mode
235
+ * will also maintain a log of thrown errors.
236
+ */
237
+ this.mode = function(mode) {
238
+
239
+ switch (mode) {
240
+ case 'log':
241
+ case 'rethrow':
242
+ var errors = [];
243
+ handler = function(e) {
244
+ if (arguments.length == 1) {
245
+ errors.push(e);
246
+ } else {
247
+ errors.push([].slice.call(arguments, 0));
248
+ }
249
+ if (mode === "rethrow") {
250
+ throw e;
251
+ }
252
+ };
253
+ handler.errors = errors;
254
+ break;
255
+ default:
256
+ throw new Error("Unknown mode '" + mode + "', only 'log'/'rethrow' modes are allowed!");
257
+ }
258
+ };
259
+
260
+ this.$get = function() {
261
+ return handler;
262
+ };
263
+
264
+ this.mode('rethrow');
265
+ };
266
+
267
+
268
+ /**
269
+ * @ngdoc service
270
+ * @name $log
271
+ *
272
+ * @description
273
+ * Mock implementation of {@link ng.$log} that gathers all logged messages in arrays
274
+ * (one array per logging level). These arrays are exposed as `logs` property of each of the
275
+ * level-specific log function, e.g. for level `error` the array is exposed as `$log.error.logs`.
276
+ *
277
+ */
278
+ angular.mock.$LogProvider = function() {
279
+ var debug = true;
280
+
281
+ function concat(array1, array2, index) {
282
+ return array1.concat(Array.prototype.slice.call(array2, index));
283
+ }
284
+
285
+ this.debugEnabled = function(flag) {
286
+ if (angular.isDefined(flag)) {
287
+ debug = flag;
288
+ return this;
289
+ } else {
290
+ return debug;
291
+ }
292
+ };
293
+
294
+ this.$get = function() {
295
+ var $log = {
296
+ log: function() { $log.log.logs.push(concat([], arguments, 0)); },
297
+ warn: function() { $log.warn.logs.push(concat([], arguments, 0)); },
298
+ info: function() { $log.info.logs.push(concat([], arguments, 0)); },
299
+ error: function() { $log.error.logs.push(concat([], arguments, 0)); },
300
+ debug: function() {
301
+ if (debug) {
302
+ $log.debug.logs.push(concat([], arguments, 0));
303
+ }
304
+ }
305
+ };
306
+
307
+ /**
308
+ * @ngdoc method
309
+ * @name $log#reset
310
+ *
311
+ * @description
312
+ * Reset all of the logging arrays to empty.
313
+ */
314
+ $log.reset = function() {
315
+ /**
316
+ * @ngdoc property
317
+ * @name $log#log.logs
318
+ *
319
+ * @description
320
+ * Array of messages logged using {@link ng.$log#log `log()`}.
321
+ *
322
+ * @example
323
+ * ```js
324
+ * $log.log('Some Log');
325
+ * var first = $log.log.logs.unshift();
326
+ * ```
327
+ */
328
+ $log.log.logs = [];
329
+ /**
330
+ * @ngdoc property
331
+ * @name $log#info.logs
332
+ *
333
+ * @description
334
+ * Array of messages logged using {@link ng.$log#info `info()`}.
335
+ *
336
+ * @example
337
+ * ```js
338
+ * $log.info('Some Info');
339
+ * var first = $log.info.logs.unshift();
340
+ * ```
341
+ */
342
+ $log.info.logs = [];
343
+ /**
344
+ * @ngdoc property
345
+ * @name $log#warn.logs
346
+ *
347
+ * @description
348
+ * Array of messages logged using {@link ng.$log#warn `warn()`}.
349
+ *
350
+ * @example
351
+ * ```js
352
+ * $log.warn('Some Warning');
353
+ * var first = $log.warn.logs.unshift();
354
+ * ```
355
+ */
356
+ $log.warn.logs = [];
357
+ /**
358
+ * @ngdoc property
359
+ * @name $log#error.logs
360
+ *
361
+ * @description
362
+ * Array of messages logged using {@link ng.$log#error `error()`}.
363
+ *
364
+ * @example
365
+ * ```js
366
+ * $log.error('Some Error');
367
+ * var first = $log.error.logs.unshift();
368
+ * ```
369
+ */
370
+ $log.error.logs = [];
371
+ /**
372
+ * @ngdoc property
373
+ * @name $log#debug.logs
374
+ *
375
+ * @description
376
+ * Array of messages logged using {@link ng.$log#debug `debug()`}.
377
+ *
378
+ * @example
379
+ * ```js
380
+ * $log.debug('Some Error');
381
+ * var first = $log.debug.logs.unshift();
382
+ * ```
383
+ */
384
+ $log.debug.logs = [];
385
+ };
386
+
387
+ /**
388
+ * @ngdoc method
389
+ * @name $log#assertEmpty
390
+ *
391
+ * @description
392
+ * Assert that all of the logging methods have no logged messages. If any messages are present,
393
+ * an exception is thrown.
394
+ */
395
+ $log.assertEmpty = function() {
396
+ var errors = [];
397
+ angular.forEach(['error', 'warn', 'info', 'log', 'debug'], function(logLevel) {
398
+ angular.forEach($log[logLevel].logs, function(log) {
399
+ angular.forEach(log, function(logItem) {
400
+ errors.push('MOCK $log (' + logLevel + '): ' + String(logItem) + '\n' +
401
+ (logItem.stack || ''));
402
+ });
403
+ });
404
+ });
405
+ if (errors.length) {
406
+ errors.unshift("Expected $log to be empty! Either a message was logged unexpectedly, or " +
407
+ "an expected log message was not checked and removed:");
408
+ errors.push('');
409
+ throw new Error(errors.join('\n---------\n'));
410
+ }
411
+ };
412
+
413
+ $log.reset();
414
+ return $log;
415
+ };
416
+ };
417
+
418
+
419
+ /**
420
+ * @ngdoc service
421
+ * @name $interval
422
+ *
423
+ * @description
424
+ * Mock implementation of the $interval service.
425
+ *
426
+ * Use {@link ngMock.$interval#flush `$interval.flush(millis)`} to
427
+ * move forward by `millis` milliseconds and trigger any functions scheduled to run in that
428
+ * time.
429
+ *
430
+ * @param {function()} fn A function that should be called repeatedly.
431
+ * @param {number} delay Number of milliseconds between each function call.
432
+ * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
433
+ * indefinitely.
434
+ * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
435
+ * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
436
+ * @param {...*=} Pass additional parameters to the executed function.
437
+ * @returns {promise} A promise which will be notified on each iteration.
438
+ */
439
+ angular.mock.$IntervalProvider = function() {
440
+ this.$get = ['$browser', '$rootScope', '$q', '$$q',
441
+ function($browser, $rootScope, $q, $$q) {
442
+ var repeatFns = [],
443
+ nextRepeatId = 0,
444
+ now = 0;
445
+
446
+ var $interval = function(fn, delay, count, invokeApply) {
447
+ var hasParams = arguments.length > 4,
448
+ args = hasParams ? Array.prototype.slice.call(arguments, 4) : [],
449
+ iteration = 0,
450
+ skipApply = (angular.isDefined(invokeApply) && !invokeApply),
451
+ deferred = (skipApply ? $$q : $q).defer(),
452
+ promise = deferred.promise;
453
+
454
+ count = (angular.isDefined(count)) ? count : 0;
455
+ promise.then(null, null, (!hasParams) ? fn : function() {
456
+ fn.apply(null, args);
457
+ });
458
+
459
+ promise.$$intervalId = nextRepeatId;
460
+
461
+ function tick() {
462
+ deferred.notify(iteration++);
463
+
464
+ if (count > 0 && iteration >= count) {
465
+ var fnIndex;
466
+ deferred.resolve(iteration);
467
+
468
+ angular.forEach(repeatFns, function(fn, index) {
469
+ if (fn.id === promise.$$intervalId) fnIndex = index;
470
+ });
471
+
472
+ if (angular.isDefined(fnIndex)) {
473
+ repeatFns.splice(fnIndex, 1);
474
+ }
475
+ }
476
+
477
+ if (skipApply) {
478
+ $browser.defer.flush();
479
+ } else {
480
+ $rootScope.$apply();
481
+ }
482
+ }
483
+
484
+ repeatFns.push({
485
+ nextTime:(now + delay),
486
+ delay: delay,
487
+ fn: tick,
488
+ id: nextRepeatId,
489
+ deferred: deferred
490
+ });
491
+ repeatFns.sort(function(a, b) { return a.nextTime - b.nextTime;});
492
+
493
+ nextRepeatId++;
494
+ return promise;
495
+ };
496
+ /**
497
+ * @ngdoc method
498
+ * @name $interval#cancel
499
+ *
500
+ * @description
501
+ * Cancels a task associated with the `promise`.
502
+ *
503
+ * @param {promise} promise A promise from calling the `$interval` function.
504
+ * @returns {boolean} Returns `true` if the task was successfully cancelled.
505
+ */
506
+ $interval.cancel = function(promise) {
507
+ if (!promise) return false;
508
+ var fnIndex;
509
+
510
+ angular.forEach(repeatFns, function(fn, index) {
511
+ if (fn.id === promise.$$intervalId) fnIndex = index;
512
+ });
513
+
514
+ if (angular.isDefined(fnIndex)) {
515
+ repeatFns[fnIndex].deferred.reject('canceled');
516
+ repeatFns.splice(fnIndex, 1);
517
+ return true;
518
+ }
519
+
520
+ return false;
521
+ };
522
+
523
+ /**
524
+ * @ngdoc method
525
+ * @name $interval#flush
526
+ * @description
527
+ *
528
+ * Runs interval tasks scheduled to be run in the next `millis` milliseconds.
529
+ *
530
+ * @param {number=} millis maximum timeout amount to flush up until.
531
+ *
532
+ * @return {number} The amount of time moved forward.
533
+ */
534
+ $interval.flush = function(millis) {
535
+ now += millis;
536
+ while (repeatFns.length && repeatFns[0].nextTime <= now) {
537
+ var task = repeatFns[0];
538
+ task.fn();
539
+ task.nextTime += task.delay;
540
+ repeatFns.sort(function(a, b) { return a.nextTime - b.nextTime;});
541
+ }
542
+ return millis;
543
+ };
544
+
545
+ return $interval;
546
+ }];
547
+ };
548
+
549
+
550
+ /* jshint -W101 */
551
+ /* The R_ISO8061_STR regex is never going to fit into the 100 char limit!
552
+ * This directive should go inside the anonymous function but a bug in JSHint means that it would
553
+ * not be enacted early enough to prevent the warning.
554
+ */
555
+ var R_ISO8061_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?:\:?(\d\d)(?:\:?(\d\d)(?:\.(\d{3}))?)?)?(Z|([+-])(\d\d):?(\d\d)))?$/;
556
+
557
+ function jsonStringToDate(string) {
558
+ var match;
559
+ if (match = string.match(R_ISO8061_STR)) {
560
+ var date = new Date(0),
561
+ tzHour = 0,
562
+ tzMin = 0;
563
+ if (match[9]) {
564
+ tzHour = toInt(match[9] + match[10]);
565
+ tzMin = toInt(match[9] + match[11]);
566
+ }
567
+ date.setUTCFullYear(toInt(match[1]), toInt(match[2]) - 1, toInt(match[3]));
568
+ date.setUTCHours(toInt(match[4] || 0) - tzHour,
569
+ toInt(match[5] || 0) - tzMin,
570
+ toInt(match[6] || 0),
571
+ toInt(match[7] || 0));
572
+ return date;
573
+ }
574
+ return string;
575
+ }
576
+
577
+ function toInt(str) {
578
+ return parseInt(str, 10);
579
+ }
580
+
581
+ function padNumber(num, digits, trim) {
582
+ var neg = '';
583
+ if (num < 0) {
584
+ neg = '-';
585
+ num = -num;
586
+ }
587
+ num = '' + num;
588
+ while (num.length < digits) num = '0' + num;
589
+ if (trim) {
590
+ num = num.substr(num.length - digits);
591
+ }
592
+ return neg + num;
593
+ }
594
+
595
+
596
+ /**
597
+ * @ngdoc type
598
+ * @name angular.mock.TzDate
599
+ * @description
600
+ *
601
+ * *NOTE*: this is not an injectable instance, just a globally available mock class of `Date`.
602
+ *
603
+ * Mock of the Date type which has its timezone specified via constructor arg.
604
+ *
605
+ * The main purpose is to create Date-like instances with timezone fixed to the specified timezone
606
+ * offset, so that we can test code that depends on local timezone settings without dependency on
607
+ * the time zone settings of the machine where the code is running.
608
+ *
609
+ * @param {number} offset Offset of the *desired* timezone in hours (fractions will be honored)
610
+ * @param {(number|string)} timestamp Timestamp representing the desired time in *UTC*
611
+ *
612
+ * @example
613
+ * !!!! WARNING !!!!!
614
+ * This is not a complete Date object so only methods that were implemented can be called safely.
615
+ * To make matters worse, TzDate instances inherit stuff from Date via a prototype.
616
+ *
617
+ * We do our best to intercept calls to "unimplemented" methods, but since the list of methods is
618
+ * incomplete we might be missing some non-standard methods. This can result in errors like:
619
+ * "Date.prototype.foo called on incompatible Object".
620
+ *
621
+ * ```js
622
+ * var newYearInBratislava = new TzDate(-1, '2009-12-31T23:00:00Z');
623
+ * newYearInBratislava.getTimezoneOffset() => -60;
624
+ * newYearInBratislava.getFullYear() => 2010;
625
+ * newYearInBratislava.getMonth() => 0;
626
+ * newYearInBratislava.getDate() => 1;
627
+ * newYearInBratislava.getHours() => 0;
628
+ * newYearInBratislava.getMinutes() => 0;
629
+ * newYearInBratislava.getSeconds() => 0;
630
+ * ```
631
+ *
632
+ */
633
+ angular.mock.TzDate = function(offset, timestamp) {
634
+ var self = new Date(0);
635
+ if (angular.isString(timestamp)) {
636
+ var tsStr = timestamp;
637
+
638
+ self.origDate = jsonStringToDate(timestamp);
639
+
640
+ timestamp = self.origDate.getTime();
641
+ if (isNaN(timestamp)) {
642
+ throw {
643
+ name: "Illegal Argument",
644
+ message: "Arg '" + tsStr + "' passed into TzDate constructor is not a valid date string"
645
+ };
646
+ }
647
+ } else {
648
+ self.origDate = new Date(timestamp);
649
+ }
650
+
651
+ var localOffset = new Date(timestamp).getTimezoneOffset();
652
+ self.offsetDiff = localOffset * 60 * 1000 - offset * 1000 * 60 * 60;
653
+ self.date = new Date(timestamp + self.offsetDiff);
654
+
655
+ self.getTime = function() {
656
+ return self.date.getTime() - self.offsetDiff;
657
+ };
658
+
659
+ self.toLocaleDateString = function() {
660
+ return self.date.toLocaleDateString();
661
+ };
662
+
663
+ self.getFullYear = function() {
664
+ return self.date.getFullYear();
665
+ };
666
+
667
+ self.getMonth = function() {
668
+ return self.date.getMonth();
669
+ };
670
+
671
+ self.getDate = function() {
672
+ return self.date.getDate();
673
+ };
674
+
675
+ self.getHours = function() {
676
+ return self.date.getHours();
677
+ };
678
+
679
+ self.getMinutes = function() {
680
+ return self.date.getMinutes();
681
+ };
682
+
683
+ self.getSeconds = function() {
684
+ return self.date.getSeconds();
685
+ };
686
+
687
+ self.getMilliseconds = function() {
688
+ return self.date.getMilliseconds();
689
+ };
690
+
691
+ self.getTimezoneOffset = function() {
692
+ return offset * 60;
693
+ };
694
+
695
+ self.getUTCFullYear = function() {
696
+ return self.origDate.getUTCFullYear();
697
+ };
698
+
699
+ self.getUTCMonth = function() {
700
+ return self.origDate.getUTCMonth();
701
+ };
702
+
703
+ self.getUTCDate = function() {
704
+ return self.origDate.getUTCDate();
705
+ };
706
+
707
+ self.getUTCHours = function() {
708
+ return self.origDate.getUTCHours();
709
+ };
710
+
711
+ self.getUTCMinutes = function() {
712
+ return self.origDate.getUTCMinutes();
713
+ };
714
+
715
+ self.getUTCSeconds = function() {
716
+ return self.origDate.getUTCSeconds();
717
+ };
718
+
719
+ self.getUTCMilliseconds = function() {
720
+ return self.origDate.getUTCMilliseconds();
721
+ };
722
+
723
+ self.getDay = function() {
724
+ return self.date.getDay();
725
+ };
726
+
727
+ // provide this method only on browsers that already have it
728
+ if (self.toISOString) {
729
+ self.toISOString = function() {
730
+ return padNumber(self.origDate.getUTCFullYear(), 4) + '-' +
731
+ padNumber(self.origDate.getUTCMonth() + 1, 2) + '-' +
732
+ padNumber(self.origDate.getUTCDate(), 2) + 'T' +
733
+ padNumber(self.origDate.getUTCHours(), 2) + ':' +
734
+ padNumber(self.origDate.getUTCMinutes(), 2) + ':' +
735
+ padNumber(self.origDate.getUTCSeconds(), 2) + '.' +
736
+ padNumber(self.origDate.getUTCMilliseconds(), 3) + 'Z';
737
+ };
738
+ }
739
+
740
+ //hide all methods not implemented in this mock that the Date prototype exposes
741
+ var unimplementedMethods = ['getUTCDay',
742
+ 'getYear', 'setDate', 'setFullYear', 'setHours', 'setMilliseconds',
743
+ 'setMinutes', 'setMonth', 'setSeconds', 'setTime', 'setUTCDate', 'setUTCFullYear',
744
+ 'setUTCHours', 'setUTCMilliseconds', 'setUTCMinutes', 'setUTCMonth', 'setUTCSeconds',
745
+ 'setYear', 'toDateString', 'toGMTString', 'toJSON', 'toLocaleFormat', 'toLocaleString',
746
+ 'toLocaleTimeString', 'toSource', 'toString', 'toTimeString', 'toUTCString', 'valueOf'];
747
+
748
+ angular.forEach(unimplementedMethods, function(methodName) {
749
+ self[methodName] = function() {
750
+ throw new Error("Method '" + methodName + "' is not implemented in the TzDate mock");
751
+ };
752
+ });
753
+
754
+ return self;
755
+ };
756
+
757
+ //make "tzDateInstance instanceof Date" return true
758
+ angular.mock.TzDate.prototype = Date.prototype;
759
+ /* jshint +W101 */
760
+
761
+ angular.mock.animate = angular.module('ngAnimateMock', ['ng'])
762
+
763
+ .config(['$provide', function($provide) {
764
+
765
+ $provide.factory('$$forceReflow', function() {
766
+ function reflowFn() {
767
+ reflowFn.totalReflows++;
768
+ }
769
+ reflowFn.totalReflows = 0;
770
+ return reflowFn;
771
+ });
772
+
773
+ $provide.factory('$$animateAsyncRun', function() {
774
+ var queue = [];
775
+ var queueFn = function() {
776
+ return function(fn) {
777
+ queue.push(fn);
778
+ };
779
+ };
780
+ queueFn.flush = function() {
781
+ if (queue.length === 0) return false;
782
+
783
+ for (var i = 0; i < queue.length; i++) {
784
+ queue[i]();
785
+ }
786
+ queue = [];
787
+
788
+ return true;
789
+ };
790
+ return queueFn;
791
+ });
792
+
793
+ $provide.decorator('$animate', ['$delegate', '$timeout', '$browser', '$$rAF',
794
+ '$$forceReflow', '$$animateAsyncRun', '$rootScope',
795
+ function($delegate, $timeout, $browser, $$rAF,
796
+ $$forceReflow, $$animateAsyncRun, $rootScope) {
797
+ var animate = {
798
+ queue: [],
799
+ cancel: $delegate.cancel,
800
+ on: $delegate.on,
801
+ off: $delegate.off,
802
+ pin: $delegate.pin,
803
+ get reflows() {
804
+ return $$forceReflow.totalReflows;
805
+ },
806
+ enabled: $delegate.enabled,
807
+ flush: function() {
808
+ $rootScope.$digest();
809
+
810
+ var doNextRun, somethingFlushed = false;
811
+ do {
812
+ doNextRun = false;
813
+
814
+ if ($$rAF.queue.length) {
815
+ $$rAF.flush();
816
+ doNextRun = somethingFlushed = true;
817
+ }
818
+
819
+ if ($$animateAsyncRun.flush()) {
820
+ doNextRun = somethingFlushed = true;
821
+ }
822
+ } while (doNextRun);
823
+
824
+ if (!somethingFlushed) {
825
+ throw new Error('No pending animations ready to be closed or flushed');
826
+ }
827
+
828
+ $rootScope.$digest();
829
+ }
830
+ };
831
+
832
+ angular.forEach(
833
+ ['animate','enter','leave','move','addClass','removeClass','setClass'], function(method) {
834
+ animate[method] = function() {
835
+ animate.queue.push({
836
+ event: method,
837
+ element: arguments[0],
838
+ options: arguments[arguments.length - 1],
839
+ args: arguments
840
+ });
841
+ return $delegate[method].apply($delegate, arguments);
842
+ };
843
+ });
844
+
845
+ return animate;
846
+ }]);
847
+
848
+ }]);
849
+
850
+
851
+ /**
852
+ * @ngdoc function
853
+ * @name angular.mock.dump
854
+ * @description
855
+ *
856
+ * *NOTE*: this is not an injectable instance, just a globally available function.
857
+ *
858
+ * Method for serializing common angular objects (scope, elements, etc..) into strings, useful for
859
+ * debugging.
860
+ *
861
+ * This method is also available on window, where it can be used to display objects on debug
862
+ * console.
863
+ *
864
+ * @param {*} object - any object to turn into string.
865
+ * @return {string} a serialized string of the argument
866
+ */
867
+ angular.mock.dump = function(object) {
868
+ return serialize(object);
869
+
870
+ function serialize(object) {
871
+ var out;
872
+
873
+ if (angular.isElement(object)) {
874
+ object = angular.element(object);
875
+ out = angular.element('<div></div>');
876
+ angular.forEach(object, function(element) {
877
+ out.append(angular.element(element).clone());
878
+ });
879
+ out = out.html();
880
+ } else if (angular.isArray(object)) {
881
+ out = [];
882
+ angular.forEach(object, function(o) {
883
+ out.push(serialize(o));
884
+ });
885
+ out = '[ ' + out.join(', ') + ' ]';
886
+ } else if (angular.isObject(object)) {
887
+ if (angular.isFunction(object.$eval) && angular.isFunction(object.$apply)) {
888
+ out = serializeScope(object);
889
+ } else if (object instanceof Error) {
890
+ out = object.stack || ('' + object.name + ': ' + object.message);
891
+ } else {
892
+ // TODO(i): this prevents methods being logged,
893
+ // we should have a better way to serialize objects
894
+ out = angular.toJson(object, true);
895
+ }
896
+ } else {
897
+ out = String(object);
898
+ }
899
+
900
+ return out;
901
+ }
902
+
903
+ function serializeScope(scope, offset) {
904
+ offset = offset || ' ';
905
+ var log = [offset + 'Scope(' + scope.$id + '): {'];
906
+ for (var key in scope) {
907
+ if (Object.prototype.hasOwnProperty.call(scope, key) && !key.match(/^(\$|this)/)) {
908
+ log.push(' ' + key + ': ' + angular.toJson(scope[key]));
909
+ }
910
+ }
911
+ var child = scope.$$childHead;
912
+ while (child) {
913
+ log.push(serializeScope(child, offset + ' '));
914
+ child = child.$$nextSibling;
915
+ }
916
+ log.push('}');
917
+ return log.join('\n' + offset);
918
+ }
919
+ };
920
+
921
+ /**
922
+ * @ngdoc service
923
+ * @name $httpBackend
924
+ * @description
925
+ * Fake HTTP backend implementation suitable for unit testing applications that use the
926
+ * {@link ng.$http $http service}.
927
+ *
928
+ * *Note*: For fake HTTP backend implementation suitable for end-to-end testing or backend-less
929
+ * development please see {@link ngMockE2E.$httpBackend e2e $httpBackend mock}.
930
+ *
931
+ * During unit testing, we want our unit tests to run quickly and have no external dependencies so
932
+ * we don’t want to send [XHR](https://developer.mozilla.org/en/xmlhttprequest) or
933
+ * [JSONP](http://en.wikipedia.org/wiki/JSONP) requests to a real server. All we really need is
934
+ * to verify whether a certain request has been sent or not, or alternatively just let the
935
+ * application make requests, respond with pre-trained responses and assert that the end result is
936
+ * what we expect it to be.
937
+ *
938
+ * This mock implementation can be used to respond with static or dynamic responses via the
939
+ * `expect` and `when` apis and their shortcuts (`expectGET`, `whenPOST`, etc).
940
+ *
941
+ * When an Angular application needs some data from a server, it calls the $http service, which
942
+ * sends the request to a real server using $httpBackend service. With dependency injection, it is
943
+ * easy to inject $httpBackend mock (which has the same API as $httpBackend) and use it to verify
944
+ * the requests and respond with some testing data without sending a request to a real server.
945
+ *
946
+ * There are two ways to specify what test data should be returned as http responses by the mock
947
+ * backend when the code under test makes http requests:
948
+ *
949
+ * - `$httpBackend.expect` - specifies a request expectation
950
+ * - `$httpBackend.when` - specifies a backend definition
951
+ *
952
+ *
953
+ * # Request Expectations vs Backend Definitions
954
+ *
955
+ * Request expectations provide a way to make assertions about requests made by the application and
956
+ * to define responses for those requests. The test will fail if the expected requests are not made
957
+ * or they are made in the wrong order.
958
+ *
959
+ * Backend definitions allow you to define a fake backend for your application which doesn't assert
960
+ * if a particular request was made or not, it just returns a trained response if a request is made.
961
+ * The test will pass whether or not the request gets made during testing.
962
+ *
963
+ *
964
+ * <table class="table">
965
+ * <tr><th width="220px"></th><th>Request expectations</th><th>Backend definitions</th></tr>
966
+ * <tr>
967
+ * <th>Syntax</th>
968
+ * <td>.expect(...).respond(...)</td>
969
+ * <td>.when(...).respond(...)</td>
970
+ * </tr>
971
+ * <tr>
972
+ * <th>Typical usage</th>
973
+ * <td>strict unit tests</td>
974
+ * <td>loose (black-box) unit testing</td>
975
+ * </tr>
976
+ * <tr>
977
+ * <th>Fulfills multiple requests</th>
978
+ * <td>NO</td>
979
+ * <td>YES</td>
980
+ * </tr>
981
+ * <tr>
982
+ * <th>Order of requests matters</th>
983
+ * <td>YES</td>
984
+ * <td>NO</td>
985
+ * </tr>
986
+ * <tr>
987
+ * <th>Request required</th>
988
+ * <td>YES</td>
989
+ * <td>NO</td>
990
+ * </tr>
991
+ * <tr>
992
+ * <th>Response required</th>
993
+ * <td>optional (see below)</td>
994
+ * <td>YES</td>
995
+ * </tr>
996
+ * </table>
997
+ *
998
+ * In cases where both backend definitions and request expectations are specified during unit
999
+ * testing, the request expectations are evaluated first.
1000
+ *
1001
+ * If a request expectation has no response specified, the algorithm will search your backend
1002
+ * definitions for an appropriate response.
1003
+ *
1004
+ * If a request didn't match any expectation or if the expectation doesn't have the response
1005
+ * defined, the backend definitions are evaluated in sequential order to see if any of them match
1006
+ * the request. The response from the first matched definition is returned.
1007
+ *
1008
+ *
1009
+ * # Flushing HTTP requests
1010
+ *
1011
+ * The $httpBackend used in production always responds to requests asynchronously. If we preserved
1012
+ * this behavior in unit testing, we'd have to create async unit tests, which are hard to write,
1013
+ * to follow and to maintain. But neither can the testing mock respond synchronously; that would
1014
+ * change the execution of the code under test. For this reason, the mock $httpBackend has a
1015
+ * `flush()` method, which allows the test to explicitly flush pending requests. This preserves
1016
+ * the async api of the backend, while allowing the test to execute synchronously.
1017
+ *
1018
+ *
1019
+ * # Unit testing with mock $httpBackend
1020
+ * The following code shows how to setup and use the mock backend when unit testing a controller.
1021
+ * First we create the controller under test:
1022
+ *
1023
+ ```js
1024
+ // The module code
1025
+ angular
1026
+ .module('MyApp', [])
1027
+ .controller('MyController', MyController);
1028
+
1029
+ // The controller code
1030
+ function MyController($scope, $http) {
1031
+ var authToken;
1032
+
1033
+ $http.get('/auth.py').success(function(data, status, headers) {
1034
+ authToken = headers('A-Token');
1035
+ $scope.user = data;
1036
+ });
1037
+
1038
+ $scope.saveMessage = function(message) {
1039
+ var headers = { 'Authorization': authToken };
1040
+ $scope.status = 'Saving...';
1041
+
1042
+ $http.post('/add-msg.py', message, { headers: headers } ).success(function(response) {
1043
+ $scope.status = '';
1044
+ }).error(function() {
1045
+ $scope.status = 'Failed...';
1046
+ });
1047
+ };
1048
+ }
1049
+ ```
1050
+ *
1051
+ * Now we setup the mock backend and create the test specs:
1052
+ *
1053
+ ```js
1054
+ // testing controller
1055
+ describe('MyController', function() {
1056
+ var $httpBackend, $rootScope, createController, authRequestHandler;
1057
+
1058
+ // Set up the module
1059
+ beforeEach(module('MyApp'));
1060
+
1061
+ beforeEach(inject(function($injector) {
1062
+ // Set up the mock http service responses
1063
+ $httpBackend = $injector.get('$httpBackend');
1064
+ // backend definition common for all tests
1065
+ authRequestHandler = $httpBackend.when('GET', '/auth.py')
1066
+ .respond({userId: 'userX'}, {'A-Token': 'xxx'});
1067
+
1068
+ // Get hold of a scope (i.e. the root scope)
1069
+ $rootScope = $injector.get('$rootScope');
1070
+ // The $controller service is used to create instances of controllers
1071
+ var $controller = $injector.get('$controller');
1072
+
1073
+ createController = function() {
1074
+ return $controller('MyController', {'$scope' : $rootScope });
1075
+ };
1076
+ }));
1077
+
1078
+
1079
+ afterEach(function() {
1080
+ $httpBackend.verifyNoOutstandingExpectation();
1081
+ $httpBackend.verifyNoOutstandingRequest();
1082
+ });
1083
+
1084
+
1085
+ it('should fetch authentication token', function() {
1086
+ $httpBackend.expectGET('/auth.py');
1087
+ var controller = createController();
1088
+ $httpBackend.flush();
1089
+ });
1090
+
1091
+
1092
+ it('should fail authentication', function() {
1093
+
1094
+ // Notice how you can change the response even after it was set
1095
+ authRequestHandler.respond(401, '');
1096
+
1097
+ $httpBackend.expectGET('/auth.py');
1098
+ var controller = createController();
1099
+ $httpBackend.flush();
1100
+ expect($rootScope.status).toBe('Failed...');
1101
+ });
1102
+
1103
+
1104
+ it('should send msg to server', function() {
1105
+ var controller = createController();
1106
+ $httpBackend.flush();
1107
+
1108
+ // now you don’t care about the authentication, but
1109
+ // the controller will still send the request and
1110
+ // $httpBackend will respond without you having to
1111
+ // specify the expectation and response for this request
1112
+
1113
+ $httpBackend.expectPOST('/add-msg.py', 'message content').respond(201, '');
1114
+ $rootScope.saveMessage('message content');
1115
+ expect($rootScope.status).toBe('Saving...');
1116
+ $httpBackend.flush();
1117
+ expect($rootScope.status).toBe('');
1118
+ });
1119
+
1120
+
1121
+ it('should send auth header', function() {
1122
+ var controller = createController();
1123
+ $httpBackend.flush();
1124
+
1125
+ $httpBackend.expectPOST('/add-msg.py', undefined, function(headers) {
1126
+ // check if the header was sent, if it wasn't the expectation won't
1127
+ // match the request and the test will fail
1128
+ return headers['Authorization'] == 'xxx';
1129
+ }).respond(201, '');
1130
+
1131
+ $rootScope.saveMessage('whatever');
1132
+ $httpBackend.flush();
1133
+ });
1134
+ });
1135
+ ```
1136
+ */
1137
+ angular.mock.$HttpBackendProvider = function() {
1138
+ this.$get = ['$rootScope', '$timeout', createHttpBackendMock];
1139
+ };
1140
+
1141
+ /**
1142
+ * General factory function for $httpBackend mock.
1143
+ * Returns instance for unit testing (when no arguments specified):
1144
+ * - passing through is disabled
1145
+ * - auto flushing is disabled
1146
+ *
1147
+ * Returns instance for e2e testing (when `$delegate` and `$browser` specified):
1148
+ * - passing through (delegating request to real backend) is enabled
1149
+ * - auto flushing is enabled
1150
+ *
1151
+ * @param {Object=} $delegate Real $httpBackend instance (allow passing through if specified)
1152
+ * @param {Object=} $browser Auto-flushing enabled if specified
1153
+ * @return {Object} Instance of $httpBackend mock
1154
+ */
1155
+ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
1156
+ var definitions = [],
1157
+ expectations = [],
1158
+ responses = [],
1159
+ responsesPush = angular.bind(responses, responses.push),
1160
+ copy = angular.copy;
1161
+
1162
+ function createResponse(status, data, headers, statusText) {
1163
+ if (angular.isFunction(status)) return status;
1164
+
1165
+ return function() {
1166
+ return angular.isNumber(status)
1167
+ ? [status, data, headers, statusText]
1168
+ : [200, status, data, headers];
1169
+ };
1170
+ }
1171
+
1172
+ // TODO(vojta): change params to: method, url, data, headers, callback
1173
+ function $httpBackend(method, url, data, callback, headers, timeout, withCredentials) {
1174
+ var xhr = new MockXhr(),
1175
+ expectation = expectations[0],
1176
+ wasExpected = false;
1177
+
1178
+ function prettyPrint(data) {
1179
+ return (angular.isString(data) || angular.isFunction(data) || data instanceof RegExp)
1180
+ ? data
1181
+ : angular.toJson(data);
1182
+ }
1183
+
1184
+ function wrapResponse(wrapped) {
1185
+ if (!$browser && timeout) {
1186
+ timeout.then ? timeout.then(handleTimeout) : $timeout(handleTimeout, timeout);
1187
+ }
1188
+
1189
+ return handleResponse;
1190
+
1191
+ function handleResponse() {
1192
+ var response = wrapped.response(method, url, data, headers);
1193
+ xhr.$$respHeaders = response[2];
1194
+ callback(copy(response[0]), copy(response[1]), xhr.getAllResponseHeaders(),
1195
+ copy(response[3] || ''));
1196
+ }
1197
+
1198
+ function handleTimeout() {
1199
+ for (var i = 0, ii = responses.length; i < ii; i++) {
1200
+ if (responses[i] === handleResponse) {
1201
+ responses.splice(i, 1);
1202
+ callback(-1, undefined, '');
1203
+ break;
1204
+ }
1205
+ }
1206
+ }
1207
+ }
1208
+
1209
+ if (expectation && expectation.match(method, url)) {
1210
+ if (!expectation.matchData(data)) {
1211
+ throw new Error('Expected ' + expectation + ' with different data\n' +
1212
+ 'EXPECTED: ' + prettyPrint(expectation.data) + '\nGOT: ' + data);
1213
+ }
1214
+
1215
+ if (!expectation.matchHeaders(headers)) {
1216
+ throw new Error('Expected ' + expectation + ' with different headers\n' +
1217
+ 'EXPECTED: ' + prettyPrint(expectation.headers) + '\nGOT: ' +
1218
+ prettyPrint(headers));
1219
+ }
1220
+
1221
+ expectations.shift();
1222
+
1223
+ if (expectation.response) {
1224
+ responses.push(wrapResponse(expectation));
1225
+ return;
1226
+ }
1227
+ wasExpected = true;
1228
+ }
1229
+
1230
+ var i = -1, definition;
1231
+ while ((definition = definitions[++i])) {
1232
+ if (definition.match(method, url, data, headers || {})) {
1233
+ if (definition.response) {
1234
+ // if $browser specified, we do auto flush all requests
1235
+ ($browser ? $browser.defer : responsesPush)(wrapResponse(definition));
1236
+ } else if (definition.passThrough) {
1237
+ $delegate(method, url, data, callback, headers, timeout, withCredentials);
1238
+ } else throw new Error('No response defined !');
1239
+ return;
1240
+ }
1241
+ }
1242
+ throw wasExpected ?
1243
+ new Error('No response defined !') :
1244
+ new Error('Unexpected request: ' + method + ' ' + url + '\n' +
1245
+ (expectation ? 'Expected ' + expectation : 'No more request expected'));
1246
+ }
1247
+
1248
+ /**
1249
+ * @ngdoc method
1250
+ * @name $httpBackend#when
1251
+ * @description
1252
+ * Creates a new backend definition.
1253
+ *
1254
+ * @param {string} method HTTP method.
1255
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1256
+ * and returns true if the url matches the current definition.
1257
+ * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
1258
+ * data string and returns true if the data is as expected.
1259
+ * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
1260
+ * object and returns true if the headers match the current definition.
1261
+ * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1262
+ * request is handled. You can save this object for later use and invoke `respond` again in
1263
+ * order to change how a matched request is handled.
1264
+ *
1265
+ * - respond –
1266
+ * `{function([status,] data[, headers, statusText])
1267
+ * | function(function(method, url, data, headers)}`
1268
+ * – The respond method takes a set of static data to be returned or a function that can
1269
+ * return an array containing response status (number), response data (string), response
1270
+ * headers (Object), and the text for the status (string). The respond method returns the
1271
+ * `requestHandler` object for possible overrides.
1272
+ */
1273
+ $httpBackend.when = function(method, url, data, headers) {
1274
+ var definition = new MockHttpExpectation(method, url, data, headers),
1275
+ chain = {
1276
+ respond: function(status, data, headers, statusText) {
1277
+ definition.passThrough = undefined;
1278
+ definition.response = createResponse(status, data, headers, statusText);
1279
+ return chain;
1280
+ }
1281
+ };
1282
+
1283
+ if ($browser) {
1284
+ chain.passThrough = function() {
1285
+ definition.response = undefined;
1286
+ definition.passThrough = true;
1287
+ return chain;
1288
+ };
1289
+ }
1290
+
1291
+ definitions.push(definition);
1292
+ return chain;
1293
+ };
1294
+
1295
+ /**
1296
+ * @ngdoc method
1297
+ * @name $httpBackend#whenGET
1298
+ * @description
1299
+ * Creates a new backend definition for GET requests. For more info see `when()`.
1300
+ *
1301
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1302
+ * and returns true if the url matches the current definition.
1303
+ * @param {(Object|function(Object))=} headers HTTP headers.
1304
+ * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1305
+ * request is handled. You can save this object for later use and invoke `respond` again in
1306
+ * order to change how a matched request is handled.
1307
+ */
1308
+
1309
+ /**
1310
+ * @ngdoc method
1311
+ * @name $httpBackend#whenHEAD
1312
+ * @description
1313
+ * Creates a new backend definition for HEAD requests. For more info see `when()`.
1314
+ *
1315
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1316
+ * and returns true if the url matches the current definition.
1317
+ * @param {(Object|function(Object))=} headers HTTP headers.
1318
+ * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1319
+ * request is handled. You can save this object for later use and invoke `respond` again in
1320
+ * order to change how a matched request is handled.
1321
+ */
1322
+
1323
+ /**
1324
+ * @ngdoc method
1325
+ * @name $httpBackend#whenDELETE
1326
+ * @description
1327
+ * Creates a new backend definition for DELETE requests. For more info see `when()`.
1328
+ *
1329
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1330
+ * and returns true if the url matches the current definition.
1331
+ * @param {(Object|function(Object))=} headers HTTP headers.
1332
+ * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1333
+ * request is handled. You can save this object for later use and invoke `respond` again in
1334
+ * order to change how a matched request is handled.
1335
+ */
1336
+
1337
+ /**
1338
+ * @ngdoc method
1339
+ * @name $httpBackend#whenPOST
1340
+ * @description
1341
+ * Creates a new backend definition for POST requests. For more info see `when()`.
1342
+ *
1343
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1344
+ * and returns true if the url matches the current definition.
1345
+ * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
1346
+ * data string and returns true if the data is as expected.
1347
+ * @param {(Object|function(Object))=} headers HTTP headers.
1348
+ * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1349
+ * request is handled. You can save this object for later use and invoke `respond` again in
1350
+ * order to change how a matched request is handled.
1351
+ */
1352
+
1353
+ /**
1354
+ * @ngdoc method
1355
+ * @name $httpBackend#whenPUT
1356
+ * @description
1357
+ * Creates a new backend definition for PUT requests. For more info see `when()`.
1358
+ *
1359
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1360
+ * and returns true if the url matches the current definition.
1361
+ * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
1362
+ * data string and returns true if the data is as expected.
1363
+ * @param {(Object|function(Object))=} headers HTTP headers.
1364
+ * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1365
+ * request is handled. You can save this object for later use and invoke `respond` again in
1366
+ * order to change how a matched request is handled.
1367
+ */
1368
+
1369
+ /**
1370
+ * @ngdoc method
1371
+ * @name $httpBackend#whenJSONP
1372
+ * @description
1373
+ * Creates a new backend definition for JSONP requests. For more info see `when()`.
1374
+ *
1375
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1376
+ * and returns true if the url matches the current definition.
1377
+ * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1378
+ * request is handled. You can save this object for later use and invoke `respond` again in
1379
+ * order to change how a matched request is handled.
1380
+ */
1381
+ createShortMethods('when');
1382
+
1383
+
1384
+ /**
1385
+ * @ngdoc method
1386
+ * @name $httpBackend#expect
1387
+ * @description
1388
+ * Creates a new request expectation.
1389
+ *
1390
+ * @param {string} method HTTP method.
1391
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1392
+ * and returns true if the url matches the current definition.
1393
+ * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
1394
+ * receives data string and returns true if the data is as expected, or Object if request body
1395
+ * is in JSON format.
1396
+ * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
1397
+ * object and returns true if the headers match the current expectation.
1398
+ * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1399
+ * request is handled. You can save this object for later use and invoke `respond` again in
1400
+ * order to change how a matched request is handled.
1401
+ *
1402
+ * - respond –
1403
+ * `{function([status,] data[, headers, statusText])
1404
+ * | function(function(method, url, data, headers)}`
1405
+ * – The respond method takes a set of static data to be returned or a function that can
1406
+ * return an array containing response status (number), response data (string), response
1407
+ * headers (Object), and the text for the status (string). The respond method returns the
1408
+ * `requestHandler` object for possible overrides.
1409
+ */
1410
+ $httpBackend.expect = function(method, url, data, headers) {
1411
+ var expectation = new MockHttpExpectation(method, url, data, headers),
1412
+ chain = {
1413
+ respond: function(status, data, headers, statusText) {
1414
+ expectation.response = createResponse(status, data, headers, statusText);
1415
+ return chain;
1416
+ }
1417
+ };
1418
+
1419
+ expectations.push(expectation);
1420
+ return chain;
1421
+ };
1422
+
1423
+
1424
+ /**
1425
+ * @ngdoc method
1426
+ * @name $httpBackend#expectGET
1427
+ * @description
1428
+ * Creates a new request expectation for GET requests. For more info see `expect()`.
1429
+ *
1430
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1431
+ * and returns true if the url matches the current definition.
1432
+ * @param {Object=} headers HTTP headers.
1433
+ * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1434
+ * request is handled. You can save this object for later use and invoke `respond` again in
1435
+ * order to change how a matched request is handled. See #expect for more info.
1436
+ */
1437
+
1438
+ /**
1439
+ * @ngdoc method
1440
+ * @name $httpBackend#expectHEAD
1441
+ * @description
1442
+ * Creates a new request expectation for HEAD requests. For more info see `expect()`.
1443
+ *
1444
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1445
+ * and returns true if the url matches the current definition.
1446
+ * @param {Object=} headers HTTP headers.
1447
+ * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1448
+ * request is handled. You can save this object for later use and invoke `respond` again in
1449
+ * order to change how a matched request is handled.
1450
+ */
1451
+
1452
+ /**
1453
+ * @ngdoc method
1454
+ * @name $httpBackend#expectDELETE
1455
+ * @description
1456
+ * Creates a new request expectation for DELETE requests. For more info see `expect()`.
1457
+ *
1458
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1459
+ * and returns true if the url matches the current definition.
1460
+ * @param {Object=} headers HTTP headers.
1461
+ * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1462
+ * request is handled. You can save this object for later use and invoke `respond` again in
1463
+ * order to change how a matched request is handled.
1464
+ */
1465
+
1466
+ /**
1467
+ * @ngdoc method
1468
+ * @name $httpBackend#expectPOST
1469
+ * @description
1470
+ * Creates a new request expectation for POST requests. For more info see `expect()`.
1471
+ *
1472
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1473
+ * and returns true if the url matches the current definition.
1474
+ * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
1475
+ * receives data string and returns true if the data is as expected, or Object if request body
1476
+ * is in JSON format.
1477
+ * @param {Object=} headers HTTP headers.
1478
+ * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1479
+ * request is handled. You can save this object for later use and invoke `respond` again in
1480
+ * order to change how a matched request is handled.
1481
+ */
1482
+
1483
+ /**
1484
+ * @ngdoc method
1485
+ * @name $httpBackend#expectPUT
1486
+ * @description
1487
+ * Creates a new request expectation for PUT requests. For more info see `expect()`.
1488
+ *
1489
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1490
+ * and returns true if the url matches the current definition.
1491
+ * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
1492
+ * receives data string and returns true if the data is as expected, or Object if request body
1493
+ * is in JSON format.
1494
+ * @param {Object=} headers HTTP headers.
1495
+ * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1496
+ * request is handled. You can save this object for later use and invoke `respond` again in
1497
+ * order to change how a matched request is handled.
1498
+ */
1499
+
1500
+ /**
1501
+ * @ngdoc method
1502
+ * @name $httpBackend#expectPATCH
1503
+ * @description
1504
+ * Creates a new request expectation for PATCH requests. For more info see `expect()`.
1505
+ *
1506
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1507
+ * and returns true if the url matches the current definition.
1508
+ * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
1509
+ * receives data string and returns true if the data is as expected, or Object if request body
1510
+ * is in JSON format.
1511
+ * @param {Object=} headers HTTP headers.
1512
+ * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1513
+ * request is handled. You can save this object for later use and invoke `respond` again in
1514
+ * order to change how a matched request is handled.
1515
+ */
1516
+
1517
+ /**
1518
+ * @ngdoc method
1519
+ * @name $httpBackend#expectJSONP
1520
+ * @description
1521
+ * Creates a new request expectation for JSONP requests. For more info see `expect()`.
1522
+ *
1523
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives an url
1524
+ * and returns true if the url matches the current definition.
1525
+ * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1526
+ * request is handled. You can save this object for later use and invoke `respond` again in
1527
+ * order to change how a matched request is handled.
1528
+ */
1529
+ createShortMethods('expect');
1530
+
1531
+
1532
+ /**
1533
+ * @ngdoc method
1534
+ * @name $httpBackend#flush
1535
+ * @description
1536
+ * Flushes all pending requests using the trained responses.
1537
+ *
1538
+ * @param {number=} count Number of responses to flush (in the order they arrived). If undefined,
1539
+ * all pending requests will be flushed. If there are no pending requests when the flush method
1540
+ * is called an exception is thrown (as this typically a sign of programming error).
1541
+ */
1542
+ $httpBackend.flush = function(count, digest) {
1543
+ if (digest !== false) $rootScope.$digest();
1544
+ if (!responses.length) throw new Error('No pending request to flush !');
1545
+
1546
+ if (angular.isDefined(count) && count !== null) {
1547
+ while (count--) {
1548
+ if (!responses.length) throw new Error('No more pending request to flush !');
1549
+ responses.shift()();
1550
+ }
1551
+ } else {
1552
+ while (responses.length) {
1553
+ responses.shift()();
1554
+ }
1555
+ }
1556
+ $httpBackend.verifyNoOutstandingExpectation(digest);
1557
+ };
1558
+
1559
+
1560
+ /**
1561
+ * @ngdoc method
1562
+ * @name $httpBackend#verifyNoOutstandingExpectation
1563
+ * @description
1564
+ * Verifies that all of the requests defined via the `expect` api were made. If any of the
1565
+ * requests were not made, verifyNoOutstandingExpectation throws an exception.
1566
+ *
1567
+ * Typically, you would call this method following each test case that asserts requests using an
1568
+ * "afterEach" clause.
1569
+ *
1570
+ * ```js
1571
+ * afterEach($httpBackend.verifyNoOutstandingExpectation);
1572
+ * ```
1573
+ */
1574
+ $httpBackend.verifyNoOutstandingExpectation = function(digest) {
1575
+ if (digest !== false) $rootScope.$digest();
1576
+ if (expectations.length) {
1577
+ throw new Error('Unsatisfied requests: ' + expectations.join(', '));
1578
+ }
1579
+ };
1580
+
1581
+
1582
+ /**
1583
+ * @ngdoc method
1584
+ * @name $httpBackend#verifyNoOutstandingRequest
1585
+ * @description
1586
+ * Verifies that there are no outstanding requests that need to be flushed.
1587
+ *
1588
+ * Typically, you would call this method following each test case that asserts requests using an
1589
+ * "afterEach" clause.
1590
+ *
1591
+ * ```js
1592
+ * afterEach($httpBackend.verifyNoOutstandingRequest);
1593
+ * ```
1594
+ */
1595
+ $httpBackend.verifyNoOutstandingRequest = function() {
1596
+ if (responses.length) {
1597
+ throw new Error('Unflushed requests: ' + responses.length);
1598
+ }
1599
+ };
1600
+
1601
+
1602
+ /**
1603
+ * @ngdoc method
1604
+ * @name $httpBackend#resetExpectations
1605
+ * @description
1606
+ * Resets all request expectations, but preserves all backend definitions. Typically, you would
1607
+ * call resetExpectations during a multiple-phase test when you want to reuse the same instance of
1608
+ * $httpBackend mock.
1609
+ */
1610
+ $httpBackend.resetExpectations = function() {
1611
+ expectations.length = 0;
1612
+ responses.length = 0;
1613
+ };
1614
+
1615
+ return $httpBackend;
1616
+
1617
+
1618
+ function createShortMethods(prefix) {
1619
+ angular.forEach(['GET', 'DELETE', 'JSONP', 'HEAD'], function(method) {
1620
+ $httpBackend[prefix + method] = function(url, headers) {
1621
+ return $httpBackend[prefix](method, url, undefined, headers);
1622
+ };
1623
+ });
1624
+
1625
+ angular.forEach(['PUT', 'POST', 'PATCH'], function(method) {
1626
+ $httpBackend[prefix + method] = function(url, data, headers) {
1627
+ return $httpBackend[prefix](method, url, data, headers);
1628
+ };
1629
+ });
1630
+ }
1631
+ }
1632
+
1633
+ function MockHttpExpectation(method, url, data, headers) {
1634
+
1635
+ this.data = data;
1636
+ this.headers = headers;
1637
+
1638
+ this.match = function(m, u, d, h) {
1639
+ if (method != m) return false;
1640
+ if (!this.matchUrl(u)) return false;
1641
+ if (angular.isDefined(d) && !this.matchData(d)) return false;
1642
+ if (angular.isDefined(h) && !this.matchHeaders(h)) return false;
1643
+ return true;
1644
+ };
1645
+
1646
+ this.matchUrl = function(u) {
1647
+ if (!url) return true;
1648
+ if (angular.isFunction(url.test)) return url.test(u);
1649
+ if (angular.isFunction(url)) return url(u);
1650
+ return url == u;
1651
+ };
1652
+
1653
+ this.matchHeaders = function(h) {
1654
+ if (angular.isUndefined(headers)) return true;
1655
+ if (angular.isFunction(headers)) return headers(h);
1656
+ return angular.equals(headers, h);
1657
+ };
1658
+
1659
+ this.matchData = function(d) {
1660
+ if (angular.isUndefined(data)) return true;
1661
+ if (data && angular.isFunction(data.test)) return data.test(d);
1662
+ if (data && angular.isFunction(data)) return data(d);
1663
+ if (data && !angular.isString(data)) {
1664
+ return angular.equals(angular.fromJson(angular.toJson(data)), angular.fromJson(d));
1665
+ }
1666
+ return data == d;
1667
+ };
1668
+
1669
+ this.toString = function() {
1670
+ return method + ' ' + url;
1671
+ };
1672
+ }
1673
+
1674
+ function createMockXhr() {
1675
+ return new MockXhr();
1676
+ }
1677
+
1678
+ function MockXhr() {
1679
+
1680
+ // hack for testing $http, $httpBackend
1681
+ MockXhr.$$lastInstance = this;
1682
+
1683
+ this.open = function(method, url, async) {
1684
+ this.$$method = method;
1685
+ this.$$url = url;
1686
+ this.$$async = async;
1687
+ this.$$reqHeaders = {};
1688
+ this.$$respHeaders = {};
1689
+ };
1690
+
1691
+ this.send = function(data) {
1692
+ this.$$data = data;
1693
+ };
1694
+
1695
+ this.setRequestHeader = function(key, value) {
1696
+ this.$$reqHeaders[key] = value;
1697
+ };
1698
+
1699
+ this.getResponseHeader = function(name) {
1700
+ // the lookup must be case insensitive,
1701
+ // that's why we try two quick lookups first and full scan last
1702
+ var header = this.$$respHeaders[name];
1703
+ if (header) return header;
1704
+
1705
+ name = angular.lowercase(name);
1706
+ header = this.$$respHeaders[name];
1707
+ if (header) return header;
1708
+
1709
+ header = undefined;
1710
+ angular.forEach(this.$$respHeaders, function(headerVal, headerName) {
1711
+ if (!header && angular.lowercase(headerName) == name) header = headerVal;
1712
+ });
1713
+ return header;
1714
+ };
1715
+
1716
+ this.getAllResponseHeaders = function() {
1717
+ var lines = [];
1718
+
1719
+ angular.forEach(this.$$respHeaders, function(value, key) {
1720
+ lines.push(key + ': ' + value);
1721
+ });
1722
+ return lines.join('\n');
1723
+ };
1724
+
1725
+ this.abort = angular.noop;
1726
+ }
1727
+
1728
+
1729
+ /**
1730
+ * @ngdoc service
1731
+ * @name $timeout
1732
+ * @description
1733
+ *
1734
+ * This service is just a simple decorator for {@link ng.$timeout $timeout} service
1735
+ * that adds a "flush" and "verifyNoPendingTasks" methods.
1736
+ */
1737
+
1738
+ angular.mock.$TimeoutDecorator = ['$delegate', '$browser', function($delegate, $browser) {
1739
+
1740
+ /**
1741
+ * @ngdoc method
1742
+ * @name $timeout#flush
1743
+ * @description
1744
+ *
1745
+ * Flushes the queue of pending tasks.
1746
+ *
1747
+ * @param {number=} delay maximum timeout amount to flush up until
1748
+ */
1749
+ $delegate.flush = function(delay) {
1750
+ $browser.defer.flush(delay);
1751
+ };
1752
+
1753
+ /**
1754
+ * @ngdoc method
1755
+ * @name $timeout#verifyNoPendingTasks
1756
+ * @description
1757
+ *
1758
+ * Verifies that there are no pending tasks that need to be flushed.
1759
+ */
1760
+ $delegate.verifyNoPendingTasks = function() {
1761
+ if ($browser.deferredFns.length) {
1762
+ throw new Error('Deferred tasks to flush (' + $browser.deferredFns.length + '): ' +
1763
+ formatPendingTasksAsString($browser.deferredFns));
1764
+ }
1765
+ };
1766
+
1767
+ function formatPendingTasksAsString(tasks) {
1768
+ var result = [];
1769
+ angular.forEach(tasks, function(task) {
1770
+ result.push('{id: ' + task.id + ', ' + 'time: ' + task.time + '}');
1771
+ });
1772
+
1773
+ return result.join(', ');
1774
+ }
1775
+
1776
+ return $delegate;
1777
+ }];
1778
+
1779
+ angular.mock.$RAFDecorator = ['$delegate', function($delegate) {
1780
+ var rafFn = function(fn) {
1781
+ var index = rafFn.queue.length;
1782
+ rafFn.queue.push(fn);
1783
+ return function() {
1784
+ rafFn.queue.splice(index, 1);
1785
+ };
1786
+ };
1787
+
1788
+ rafFn.queue = [];
1789
+ rafFn.supported = $delegate.supported;
1790
+
1791
+ rafFn.flush = function() {
1792
+ if (rafFn.queue.length === 0) {
1793
+ throw new Error('No rAF callbacks present');
1794
+ }
1795
+
1796
+ var length = rafFn.queue.length;
1797
+ for (var i = 0; i < length; i++) {
1798
+ rafFn.queue[i]();
1799
+ }
1800
+
1801
+ rafFn.queue = rafFn.queue.slice(i);
1802
+ };
1803
+
1804
+ return rafFn;
1805
+ }];
1806
+
1807
+ /**
1808
+ *
1809
+ */
1810
+ angular.mock.$RootElementProvider = function() {
1811
+ this.$get = function() {
1812
+ return angular.element('<div ng-app></div>');
1813
+ };
1814
+ };
1815
+
1816
+ /**
1817
+ * @ngdoc service
1818
+ * @name $controller
1819
+ * @description
1820
+ * A decorator for {@link ng.$controller} with additional `bindings` parameter, useful when testing
1821
+ * controllers of directives that use {@link $compile#-bindtocontroller- `bindToController`}.
1822
+ *
1823
+ *
1824
+ * ## Example
1825
+ *
1826
+ * ```js
1827
+ *
1828
+ * // Directive definition ...
1829
+ *
1830
+ * myMod.directive('myDirective', {
1831
+ * controller: 'MyDirectiveController',
1832
+ * bindToController: {
1833
+ * name: '@'
1834
+ * }
1835
+ * });
1836
+ *
1837
+ *
1838
+ * // Controller definition ...
1839
+ *
1840
+ * myMod.controller('MyDirectiveController', ['log', function($log) {
1841
+ * $log.info(this.name);
1842
+ * })];
1843
+ *
1844
+ *
1845
+ * // In a test ...
1846
+ *
1847
+ * describe('myDirectiveController', function() {
1848
+ * it('should write the bound name to the log', inject(function($controller, $log) {
1849
+ * var ctrl = $controller('MyDirectiveController', { /* no locals &#42;/ }, { name: 'Clark Kent' });
1850
+ * expect(ctrl.name).toEqual('Clark Kent');
1851
+ * expect($log.info.logs).toEqual(['Clark Kent']);
1852
+ * });
1853
+ * });
1854
+ *
1855
+ * ```
1856
+ *
1857
+ * @param {Function|string} constructor If called with a function then it's considered to be the
1858
+ * controller constructor function. Otherwise it's considered to be a string which is used
1859
+ * to retrieve the controller constructor using the following steps:
1860
+ *
1861
+ * * check if a controller with given name is registered via `$controllerProvider`
1862
+ * * check if evaluating the string on the current scope returns a constructor
1863
+ * * if $controllerProvider#allowGlobals, check `window[constructor]` on the global
1864
+ * `window` object (not recommended)
1865
+ *
1866
+ * The string can use the `controller as property` syntax, where the controller instance is published
1867
+ * as the specified property on the `scope`; the `scope` must be injected into `locals` param for this
1868
+ * to work correctly.
1869
+ *
1870
+ * @param {Object} locals Injection locals for Controller.
1871
+ * @param {Object=} bindings Properties to add to the controller before invoking the constructor. This is used
1872
+ * to simulate the `bindToController` feature and simplify certain kinds of tests.
1873
+ * @return {Object} Instance of given controller.
1874
+ */
1875
+ angular.mock.$ControllerDecorator = ['$delegate', function($delegate) {
1876
+ return function(expression, locals, later, ident) {
1877
+ if (later && typeof later === 'object') {
1878
+ var create = $delegate(expression, locals, true, ident);
1879
+ angular.extend(create.instance, later);
1880
+ return create();
1881
+ }
1882
+ return $delegate(expression, locals, later, ident);
1883
+ };
1884
+ }];
1885
+
1886
+
1887
+ /**
1888
+ * @ngdoc module
1889
+ * @name ngMock
1890
+ * @packageName angular-mocks
1891
+ * @description
1892
+ *
1893
+ * # ngMock
1894
+ *
1895
+ * The `ngMock` module provides support to inject and mock Angular services into unit tests.
1896
+ * In addition, ngMock also extends various core ng services such that they can be
1897
+ * inspected and controlled in a synchronous manner within test code.
1898
+ *
1899
+ *
1900
+ * <div doc-module-components="ngMock"></div>
1901
+ *
1902
+ */
1903
+ angular.module('ngMock', ['ng']).provider({
1904
+ $browser: angular.mock.$BrowserProvider,
1905
+ $exceptionHandler: angular.mock.$ExceptionHandlerProvider,
1906
+ $log: angular.mock.$LogProvider,
1907
+ $interval: angular.mock.$IntervalProvider,
1908
+ $httpBackend: angular.mock.$HttpBackendProvider,
1909
+ $rootElement: angular.mock.$RootElementProvider
1910
+ }).config(['$provide', function($provide) {
1911
+ $provide.decorator('$timeout', angular.mock.$TimeoutDecorator);
1912
+ $provide.decorator('$$rAF', angular.mock.$RAFDecorator);
1913
+ $provide.decorator('$rootScope', angular.mock.$RootScopeDecorator);
1914
+ $provide.decorator('$controller', angular.mock.$ControllerDecorator);
1915
+ }]);
1916
+
1917
+ /**
1918
+ * @ngdoc module
1919
+ * @name ngMockE2E
1920
+ * @module ngMockE2E
1921
+ * @packageName angular-mocks
1922
+ * @description
1923
+ *
1924
+ * The `ngMockE2E` is an angular module which contains mocks suitable for end-to-end testing.
1925
+ * Currently there is only one mock present in this module -
1926
+ * the {@link ngMockE2E.$httpBackend e2e $httpBackend} mock.
1927
+ */
1928
+ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
1929
+ $provide.decorator('$httpBackend', angular.mock.e2e.$httpBackendDecorator);
1930
+ }]);
1931
+
1932
+ /**
1933
+ * @ngdoc service
1934
+ * @name $httpBackend
1935
+ * @module ngMockE2E
1936
+ * @description
1937
+ * Fake HTTP backend implementation suitable for end-to-end testing or backend-less development of
1938
+ * applications that use the {@link ng.$http $http service}.
1939
+ *
1940
+ * *Note*: For fake http backend implementation suitable for unit testing please see
1941
+ * {@link ngMock.$httpBackend unit-testing $httpBackend mock}.
1942
+ *
1943
+ * This implementation can be used to respond with static or dynamic responses via the `when` api
1944
+ * and its shortcuts (`whenGET`, `whenPOST`, etc) and optionally pass through requests to the
1945
+ * real $httpBackend for specific requests (e.g. to interact with certain remote apis or to fetch
1946
+ * templates from a webserver).
1947
+ *
1948
+ * As opposed to unit-testing, in an end-to-end testing scenario or in scenario when an application
1949
+ * is being developed with the real backend api replaced with a mock, it is often desirable for
1950
+ * certain category of requests to bypass the mock and issue a real http request (e.g. to fetch
1951
+ * templates or static files from the webserver). To configure the backend with this behavior
1952
+ * use the `passThrough` request handler of `when` instead of `respond`.
1953
+ *
1954
+ * Additionally, we don't want to manually have to flush mocked out requests like we do during unit
1955
+ * testing. For this reason the e2e $httpBackend flushes mocked out requests
1956
+ * automatically, closely simulating the behavior of the XMLHttpRequest object.
1957
+ *
1958
+ * To setup the application to run with this http backend, you have to create a module that depends
1959
+ * on the `ngMockE2E` and your application modules and defines the fake backend:
1960
+ *
1961
+ * ```js
1962
+ * myAppDev = angular.module('myAppDev', ['myApp', 'ngMockE2E']);
1963
+ * myAppDev.run(function($httpBackend) {
1964
+ * phones = [{name: 'phone1'}, {name: 'phone2'}];
1965
+ *
1966
+ * // returns the current list of phones
1967
+ * $httpBackend.whenGET('/phones').respond(phones);
1968
+ *
1969
+ * // adds a new phone to the phones array
1970
+ * $httpBackend.whenPOST('/phones').respond(function(method, url, data) {
1971
+ * var phone = angular.fromJson(data);
1972
+ * phones.push(phone);
1973
+ * return [200, phone, {}];
1974
+ * });
1975
+ * $httpBackend.whenGET(/^\/templates\//).passThrough();
1976
+ * //...
1977
+ * });
1978
+ * ```
1979
+ *
1980
+ * Afterwards, bootstrap your app with this new module.
1981
+ */
1982
+
1983
+ /**
1984
+ * @ngdoc method
1985
+ * @name $httpBackend#when
1986
+ * @module ngMockE2E
1987
+ * @description
1988
+ * Creates a new backend definition.
1989
+ *
1990
+ * @param {string} method HTTP method.
1991
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
1992
+ * and returns true if the url matches the current definition.
1993
+ * @param {(string|RegExp)=} data HTTP request body.
1994
+ * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
1995
+ * object and returns true if the headers match the current definition.
1996
+ * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1997
+ * control how a matched request is handled. You can save this object for later use and invoke
1998
+ * `respond` or `passThrough` again in order to change how a matched request is handled.
1999
+ *
2000
+ * - respond –
2001
+ * `{function([status,] data[, headers, statusText])
2002
+ * | function(function(method, url, data, headers)}`
2003
+ * – The respond method takes a set of static data to be returned or a function that can return
2004
+ * an array containing response status (number), response data (string), response headers
2005
+ * (Object), and the text for the status (string).
2006
+ * - passThrough – `{function()}` – Any request matching a backend definition with
2007
+ * `passThrough` handler will be passed through to the real backend (an XHR request will be made
2008
+ * to the server.)
2009
+ * - Both methods return the `requestHandler` object for possible overrides.
2010
+ */
2011
+
2012
+ /**
2013
+ * @ngdoc method
2014
+ * @name $httpBackend#whenGET
2015
+ * @module ngMockE2E
2016
+ * @description
2017
+ * Creates a new backend definition for GET requests. For more info see `when()`.
2018
+ *
2019
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
2020
+ * and returns true if the url matches the current definition.
2021
+ * @param {(Object|function(Object))=} headers HTTP headers.
2022
+ * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
2023
+ * control how a matched request is handled. You can save this object for later use and invoke
2024
+ * `respond` or `passThrough` again in order to change how a matched request is handled.
2025
+ */
2026
+
2027
+ /**
2028
+ * @ngdoc method
2029
+ * @name $httpBackend#whenHEAD
2030
+ * @module ngMockE2E
2031
+ * @description
2032
+ * Creates a new backend definition for HEAD requests. For more info see `when()`.
2033
+ *
2034
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
2035
+ * and returns true if the url matches the current definition.
2036
+ * @param {(Object|function(Object))=} headers HTTP headers.
2037
+ * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
2038
+ * control how a matched request is handled. You can save this object for later use and invoke
2039
+ * `respond` or `passThrough` again in order to change how a matched request is handled.
2040
+ */
2041
+
2042
+ /**
2043
+ * @ngdoc method
2044
+ * @name $httpBackend#whenDELETE
2045
+ * @module ngMockE2E
2046
+ * @description
2047
+ * Creates a new backend definition for DELETE requests. For more info see `when()`.
2048
+ *
2049
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
2050
+ * and returns true if the url matches the current definition.
2051
+ * @param {(Object|function(Object))=} headers HTTP headers.
2052
+ * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
2053
+ * control how a matched request is handled. You can save this object for later use and invoke
2054
+ * `respond` or `passThrough` again in order to change how a matched request is handled.
2055
+ */
2056
+
2057
+ /**
2058
+ * @ngdoc method
2059
+ * @name $httpBackend#whenPOST
2060
+ * @module ngMockE2E
2061
+ * @description
2062
+ * Creates a new backend definition for POST requests. For more info see `when()`.
2063
+ *
2064
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
2065
+ * and returns true if the url matches the current definition.
2066
+ * @param {(string|RegExp)=} data HTTP request body.
2067
+ * @param {(Object|function(Object))=} headers HTTP headers.
2068
+ * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
2069
+ * control how a matched request is handled. You can save this object for later use and invoke
2070
+ * `respond` or `passThrough` again in order to change how a matched request is handled.
2071
+ */
2072
+
2073
+ /**
2074
+ * @ngdoc method
2075
+ * @name $httpBackend#whenPUT
2076
+ * @module ngMockE2E
2077
+ * @description
2078
+ * Creates a new backend definition for PUT requests. For more info see `when()`.
2079
+ *
2080
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
2081
+ * and returns true if the url matches the current definition.
2082
+ * @param {(string|RegExp)=} data HTTP request body.
2083
+ * @param {(Object|function(Object))=} headers HTTP headers.
2084
+ * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
2085
+ * control how a matched request is handled. You can save this object for later use and invoke
2086
+ * `respond` or `passThrough` again in order to change how a matched request is handled.
2087
+ */
2088
+
2089
+ /**
2090
+ * @ngdoc method
2091
+ * @name $httpBackend#whenPATCH
2092
+ * @module ngMockE2E
2093
+ * @description
2094
+ * Creates a new backend definition for PATCH requests. For more info see `when()`.
2095
+ *
2096
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
2097
+ * and returns true if the url matches the current definition.
2098
+ * @param {(string|RegExp)=} data HTTP request body.
2099
+ * @param {(Object|function(Object))=} headers HTTP headers.
2100
+ * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
2101
+ * control how a matched request is handled. You can save this object for later use and invoke
2102
+ * `respond` or `passThrough` again in order to change how a matched request is handled.
2103
+ */
2104
+
2105
+ /**
2106
+ * @ngdoc method
2107
+ * @name $httpBackend#whenJSONP
2108
+ * @module ngMockE2E
2109
+ * @description
2110
+ * Creates a new backend definition for JSONP requests. For more info see `when()`.
2111
+ *
2112
+ * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
2113
+ * and returns true if the url matches the current definition.
2114
+ * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
2115
+ * control how a matched request is handled. You can save this object for later use and invoke
2116
+ * `respond` or `passThrough` again in order to change how a matched request is handled.
2117
+ */
2118
+ angular.mock.e2e = {};
2119
+ angular.mock.e2e.$httpBackendDecorator =
2120
+ ['$rootScope', '$timeout', '$delegate', '$browser', createHttpBackendMock];
2121
+
2122
+
2123
+ /**
2124
+ * @ngdoc type
2125
+ * @name $rootScope.Scope
2126
+ * @module ngMock
2127
+ * @description
2128
+ * {@link ng.$rootScope.Scope Scope} type decorated with helper methods useful for testing. These
2129
+ * methods are automatically available on any {@link ng.$rootScope.Scope Scope} instance when
2130
+ * `ngMock` module is loaded.
2131
+ *
2132
+ * In addition to all the regular `Scope` methods, the following helper methods are available:
2133
+ */
2134
+ angular.mock.$RootScopeDecorator = ['$delegate', function($delegate) {
2135
+
2136
+ var $rootScopePrototype = Object.getPrototypeOf($delegate);
2137
+
2138
+ $rootScopePrototype.$countChildScopes = countChildScopes;
2139
+ $rootScopePrototype.$countWatchers = countWatchers;
2140
+
2141
+ return $delegate;
2142
+
2143
+ // ------------------------------------------------------------------------------------------ //
2144
+
2145
+ /**
2146
+ * @ngdoc method
2147
+ * @name $rootScope.Scope#$countChildScopes
2148
+ * @module ngMock
2149
+ * @description
2150
+ * Counts all the direct and indirect child scopes of the current scope.
2151
+ *
2152
+ * The current scope is excluded from the count. The count includes all isolate child scopes.
2153
+ *
2154
+ * @returns {number} Total number of child scopes.
2155
+ */
2156
+ function countChildScopes() {
2157
+ // jshint validthis: true
2158
+ var count = 0; // exclude the current scope
2159
+ var pendingChildHeads = [this.$$childHead];
2160
+ var currentScope;
2161
+
2162
+ while (pendingChildHeads.length) {
2163
+ currentScope = pendingChildHeads.shift();
2164
+
2165
+ while (currentScope) {
2166
+ count += 1;
2167
+ pendingChildHeads.push(currentScope.$$childHead);
2168
+ currentScope = currentScope.$$nextSibling;
2169
+ }
2170
+ }
2171
+
2172
+ return count;
2173
+ }
2174
+
2175
+
2176
+ /**
2177
+ * @ngdoc method
2178
+ * @name $rootScope.Scope#$countWatchers
2179
+ * @module ngMock
2180
+ * @description
2181
+ * Counts all the watchers of direct and indirect child scopes of the current scope.
2182
+ *
2183
+ * The watchers of the current scope are included in the count and so are all the watchers of
2184
+ * isolate child scopes.
2185
+ *
2186
+ * @returns {number} Total number of watchers.
2187
+ */
2188
+ function countWatchers() {
2189
+ // jshint validthis: true
2190
+ var count = this.$$watchers ? this.$$watchers.length : 0; // include the current scope
2191
+ var pendingChildHeads = [this.$$childHead];
2192
+ var currentScope;
2193
+
2194
+ while (pendingChildHeads.length) {
2195
+ currentScope = pendingChildHeads.shift();
2196
+
2197
+ while (currentScope) {
2198
+ count += currentScope.$$watchers ? currentScope.$$watchers.length : 0;
2199
+ pendingChildHeads.push(currentScope.$$childHead);
2200
+ currentScope = currentScope.$$nextSibling;
2201
+ }
2202
+ }
2203
+
2204
+ return count;
2205
+ }
2206
+ }];
2207
+
2208
+
2209
+ if (window.jasmine || window.mocha) {
2210
+
2211
+ var currentSpec = null,
2212
+ annotatedFunctions = [],
2213
+ isSpecRunning = function() {
2214
+ return !!currentSpec;
2215
+ };
2216
+
2217
+ angular.mock.$$annotate = angular.injector.$$annotate;
2218
+ angular.injector.$$annotate = function(fn) {
2219
+ if (typeof fn === 'function' && !fn.$inject) {
2220
+ annotatedFunctions.push(fn);
2221
+ }
2222
+ return angular.mock.$$annotate.apply(this, arguments);
2223
+ };
2224
+
2225
+
2226
+ (window.beforeEach || window.setup)(function() {
2227
+ annotatedFunctions = [];
2228
+ currentSpec = this;
2229
+ });
2230
+
2231
+ (window.afterEach || window.teardown)(function() {
2232
+ var injector = currentSpec.$injector;
2233
+
2234
+ annotatedFunctions.forEach(function(fn) {
2235
+ delete fn.$inject;
2236
+ });
2237
+
2238
+ angular.forEach(currentSpec.$modules, function(module) {
2239
+ if (module && module.$$hashKey) {
2240
+ module.$$hashKey = undefined;
2241
+ }
2242
+ });
2243
+
2244
+ currentSpec.$injector = null;
2245
+ currentSpec.$modules = null;
2246
+ currentSpec = null;
2247
+
2248
+ if (injector) {
2249
+ injector.get('$rootElement').off();
2250
+ }
2251
+
2252
+ // clean up jquery's fragment cache
2253
+ angular.forEach(angular.element.fragments, function(val, key) {
2254
+ delete angular.element.fragments[key];
2255
+ });
2256
+
2257
+ MockXhr.$$lastInstance = null;
2258
+
2259
+ angular.forEach(angular.callbacks, function(val, key) {
2260
+ delete angular.callbacks[key];
2261
+ });
2262
+ angular.callbacks.counter = 0;
2263
+ });
2264
+
2265
+ /**
2266
+ * @ngdoc function
2267
+ * @name angular.mock.module
2268
+ * @description
2269
+ *
2270
+ * *NOTE*: This function is also published on window for easy access.<br>
2271
+ * *NOTE*: This function is declared ONLY WHEN running tests with jasmine or mocha
2272
+ *
2273
+ * This function registers a module configuration code. It collects the configuration information
2274
+ * which will be used when the injector is created by {@link angular.mock.inject inject}.
2275
+ *
2276
+ * See {@link angular.mock.inject inject} for usage example
2277
+ *
2278
+ * @param {...(string|Function|Object)} fns any number of modules which are represented as string
2279
+ * aliases or as anonymous module initialization functions. The modules are used to
2280
+ * configure the injector. The 'ng' and 'ngMock' modules are automatically loaded. If an
2281
+ * object literal is passed they will be registered as values in the module, the key being
2282
+ * the module name and the value being what is returned.
2283
+ */
2284
+ window.module = angular.mock.module = function() {
2285
+ var moduleFns = Array.prototype.slice.call(arguments, 0);
2286
+ return isSpecRunning() ? workFn() : workFn;
2287
+ /////////////////////
2288
+ function workFn() {
2289
+ if (currentSpec.$injector) {
2290
+ throw new Error('Injector already created, can not register a module!');
2291
+ } else {
2292
+ var modules = currentSpec.$modules || (currentSpec.$modules = []);
2293
+ angular.forEach(moduleFns, function(module) {
2294
+ if (angular.isObject(module) && !angular.isArray(module)) {
2295
+ modules.push(function($provide) {
2296
+ angular.forEach(module, function(value, key) {
2297
+ $provide.value(key, value);
2298
+ });
2299
+ });
2300
+ } else {
2301
+ modules.push(module);
2302
+ }
2303
+ });
2304
+ }
2305
+ }
2306
+ };
2307
+
2308
+ /**
2309
+ * @ngdoc function
2310
+ * @name angular.mock.inject
2311
+ * @description
2312
+ *
2313
+ * *NOTE*: This function is also published on window for easy access.<br>
2314
+ * *NOTE*: This function is declared ONLY WHEN running tests with jasmine or mocha
2315
+ *
2316
+ * The inject function wraps a function into an injectable function. The inject() creates new
2317
+ * instance of {@link auto.$injector $injector} per test, which is then used for
2318
+ * resolving references.
2319
+ *
2320
+ *
2321
+ * ## Resolving References (Underscore Wrapping)
2322
+ * Often, we would like to inject a reference once, in a `beforeEach()` block and reuse this
2323
+ * in multiple `it()` clauses. To be able to do this we must assign the reference to a variable
2324
+ * that is declared in the scope of the `describe()` block. Since we would, most likely, want
2325
+ * the variable to have the same name of the reference we have a problem, since the parameter
2326
+ * to the `inject()` function would hide the outer variable.
2327
+ *
2328
+ * To help with this, the injected parameters can, optionally, be enclosed with underscores.
2329
+ * These are ignored by the injector when the reference name is resolved.
2330
+ *
2331
+ * For example, the parameter `_myService_` would be resolved as the reference `myService`.
2332
+ * Since it is available in the function body as _myService_, we can then assign it to a variable
2333
+ * defined in an outer scope.
2334
+ *
2335
+ * ```
2336
+ * // Defined out reference variable outside
2337
+ * var myService;
2338
+ *
2339
+ * // Wrap the parameter in underscores
2340
+ * beforeEach( inject( function(_myService_){
2341
+ * myService = _myService_;
2342
+ * }));
2343
+ *
2344
+ * // Use myService in a series of tests.
2345
+ * it('makes use of myService', function() {
2346
+ * myService.doStuff();
2347
+ * });
2348
+ *
2349
+ * ```
2350
+ *
2351
+ * See also {@link angular.mock.module angular.mock.module}
2352
+ *
2353
+ * ## Example
2354
+ * Example of what a typical jasmine tests looks like with the inject method.
2355
+ * ```js
2356
+ *
2357
+ * angular.module('myApplicationModule', [])
2358
+ * .value('mode', 'app')
2359
+ * .value('version', 'v1.0.1');
2360
+ *
2361
+ *
2362
+ * describe('MyApp', function() {
2363
+ *
2364
+ * // You need to load modules that you want to test,
2365
+ * // it loads only the "ng" module by default.
2366
+ * beforeEach(module('myApplicationModule'));
2367
+ *
2368
+ *
2369
+ * // inject() is used to inject arguments of all given functions
2370
+ * it('should provide a version', inject(function(mode, version) {
2371
+ * expect(version).toEqual('v1.0.1');
2372
+ * expect(mode).toEqual('app');
2373
+ * }));
2374
+ *
2375
+ *
2376
+ * // The inject and module method can also be used inside of the it or beforeEach
2377
+ * it('should override a version and test the new version is injected', function() {
2378
+ * // module() takes functions or strings (module aliases)
2379
+ * module(function($provide) {
2380
+ * $provide.value('version', 'overridden'); // override version here
2381
+ * });
2382
+ *
2383
+ * inject(function(version) {
2384
+ * expect(version).toEqual('overridden');
2385
+ * });
2386
+ * });
2387
+ * });
2388
+ *
2389
+ * ```
2390
+ *
2391
+ * @param {...Function} fns any number of functions which will be injected using the injector.
2392
+ */
2393
+
2394
+
2395
+
2396
+ var ErrorAddingDeclarationLocationStack = function(e, errorForStack) {
2397
+ this.message = e.message;
2398
+ this.name = e.name;
2399
+ if (e.line) this.line = e.line;
2400
+ if (e.sourceId) this.sourceId = e.sourceId;
2401
+ if (e.stack && errorForStack)
2402
+ this.stack = e.stack + '\n' + errorForStack.stack;
2403
+ if (e.stackArray) this.stackArray = e.stackArray;
2404
+ };
2405
+ ErrorAddingDeclarationLocationStack.prototype.toString = Error.prototype.toString;
2406
+
2407
+ window.inject = angular.mock.inject = function() {
2408
+ var blockFns = Array.prototype.slice.call(arguments, 0);
2409
+ var errorForStack = new Error('Declaration Location');
2410
+ return isSpecRunning() ? workFn.call(currentSpec) : workFn;
2411
+ /////////////////////
2412
+ function workFn() {
2413
+ var modules = currentSpec.$modules || [];
2414
+ var strictDi = !!currentSpec.$injectorStrict;
2415
+ modules.unshift('ngMock');
2416
+ modules.unshift('ng');
2417
+ var injector = currentSpec.$injector;
2418
+ if (!injector) {
2419
+ if (strictDi) {
2420
+ // If strictDi is enabled, annotate the providerInjector blocks
2421
+ angular.forEach(modules, function(moduleFn) {
2422
+ if (typeof moduleFn === "function") {
2423
+ angular.injector.$$annotate(moduleFn);
2424
+ }
2425
+ });
2426
+ }
2427
+ injector = currentSpec.$injector = angular.injector(modules, strictDi);
2428
+ currentSpec.$injectorStrict = strictDi;
2429
+ }
2430
+ for (var i = 0, ii = blockFns.length; i < ii; i++) {
2431
+ if (currentSpec.$injectorStrict) {
2432
+ // If the injector is strict / strictDi, and the spec wants to inject using automatic
2433
+ // annotation, then annotate the function here.
2434
+ injector.annotate(blockFns[i]);
2435
+ }
2436
+ try {
2437
+ /* jshint -W040 *//* Jasmine explicitly provides a `this` object when calling functions */
2438
+ injector.invoke(blockFns[i] || angular.noop, this);
2439
+ /* jshint +W040 */
2440
+ } catch (e) {
2441
+ if (e.stack && errorForStack) {
2442
+ throw new ErrorAddingDeclarationLocationStack(e, errorForStack);
2443
+ }
2444
+ throw e;
2445
+ } finally {
2446
+ errorForStack = null;
2447
+ }
2448
+ }
2449
+ }
2450
+ };
2451
+
2452
+
2453
+ angular.mock.inject.strictDi = function(value) {
2454
+ value = arguments.length ? !!value : true;
2455
+ return isSpecRunning() ? workFn() : workFn;
2456
+
2457
+ function workFn() {
2458
+ if (value !== currentSpec.$injectorStrict) {
2459
+ if (currentSpec.$injector) {
2460
+ throw new Error('Injector already created, can not modify strict annotations');
2461
+ } else {
2462
+ currentSpec.$injectorStrict = value;
2463
+ }
2464
+ }
2465
+ }
2466
+ };
2467
+ }
2468
+
2469
+
2470
+ })(window, window.angular);