瀏覽代碼

Merge branch 'master' of github.com:grafana/grafana into develop

Torkel Ödegaard 8 年之前
父節點
當前提交
5849fbf04e
共有 100 個文件被更改,包括 605 次插入5163 次删除
  1. 0 3
      .bowerrc
  2. 0 21
      .jsfmtrc
  3. 13 3
      CHANGELOG.md
  4. 0 24
      bower.json
  5. 3 0
      conf/defaults.ini
  6. 4 1
      conf/sample.ini
  7. 12 13
      docker/blocks/prometheus/prometheus.yml
  8. 11 30
      docs/sources/features/panels/dashlist.md
  9. 10 7
      docs/sources/features/panels/graph.md
  10. 32 17
      docs/sources/features/panels/singlestat.md
  11. 1 0
      docs/sources/http_api/admin.md
  12. 8 1
      docs/sources/installation/configuration.md
  13. 5 5
      docs/sources/installation/debian.md
  14. 32 0
      docs/sources/installation/mac.md
  15. 5 5
      docs/sources/installation/rpm.md
  16. 1 1
      docs/sources/installation/windows.md
  17. 5 0
      docs/sources/plugins/installation.md
  18. 0 34
      docs/sources/reference/keyboard_shortcuts.md
  19. 2 2
      latest.json
  20. 7 0
      package.json
  21. 7 7
      packaging/publish/publish_both.sh
  22. 5 2
      pkg/api/app_routes.go
  23. 4 3
      pkg/api/metrics.go
  24. 5 0
      pkg/cmd/grafana-cli/commands/command_line.go
  25. 4 0
      pkg/cmd/grafana-cli/commands/commandstest/fake_commandLine.go
  26. 20 18
      pkg/cmd/grafana-cli/commands/install_command.go
  27. 6 0
      pkg/cmd/grafana-cli/main.go
  28. 4 1
      pkg/cmd/grafana-server/main.go
  29. 10 4
      pkg/components/imguploader/imguploader.go
  30. 5 7
      pkg/components/imguploader/s3uploader.go
  31. 10 1
      pkg/metrics/metrics.go
  32. 1 0
      pkg/models/datasource_cache.go
  33. 1 1
      pkg/services/alerting/conditions/query.go
  34. 1 1
      pkg/services/alerting/conditions/query_test.go
  35. 5 1
      pkg/services/notifications/mailer.go
  36. 11 9
      pkg/setting/setting_smtp.go
  37. 5 5
      pkg/tsdb/fake_test.go
  38. 9 13
      pkg/tsdb/graphite/graphite.go
  39. 13 13
      pkg/tsdb/influxdb/influxdb.go
  40. 2 18
      pkg/tsdb/models.go
  41. 5 5
      pkg/tsdb/mysql/mysql.go
  42. 8 11
      pkg/tsdb/opentsdb/opentsdb.go
  43. 8 8
      pkg/tsdb/prometheus/prometheus.go
  44. 1 1
      pkg/tsdb/query_endpoint.go
  45. 6 14
      pkg/tsdb/request.go
  46. 61 0
      pkg/tsdb/testdata/scenarios.go
  47. 6 6
      pkg/tsdb/testdata/testdata.go
  48. 3 8
      pkg/tsdb/tsdb_test.go
  49. 2 0
      public/app/app.ts
  50. 0 1
      public/app/core/components/PasswordStrength.tsx
  51. 0 1
      public/app/core/core.ts
  52. 1 1
      public/app/core/directives/misc.js
  53. 0 45
      public/app/core/directives/password_strength.js
  54. 2 0
      public/app/features/alerting/alert_def.ts
  55. 2 1
      public/app/features/alerting/alert_tab_ctrl.ts
  56. 1 1
      public/app/features/dashboard/export/export_modal.html
  57. 2 1
      public/app/features/panel/query_troubleshooter.ts
  58. 2 0
      public/app/features/plugins/import_list/import_list.ts
  59. 1 0
      public/app/features/plugins/plugin_edit_ctrl.ts
  60. 1 1
      public/app/features/templating/editor_ctrl.ts
  61. 2 1
      public/app/plugins/datasource/influxdb/query_ctrl.ts
  62. 26 2
      public/app/plugins/datasource/prometheus/datasource.ts
  63. 1 3
      public/app/plugins/datasource/prometheus/metric_find_query.js
  64. 2 0
      public/app/plugins/datasource/prometheus/partials/query.editor.html
  65. 3 0
      public/app/plugins/datasource/prometheus/query_ctrl.ts
  66. 77 3
      public/app/plugins/datasource/prometheus/specs/datasource_specs.ts
  67. 17 15
      public/app/plugins/panel/graph/data_processor.ts
  68. 3 0
      public/app/plugins/panel/graph/graph.ts
  69. 28 24
      public/app/plugins/panel/heatmap/heatmap_data_converter.ts
  70. 1 0
      public/app/plugins/panel/table/transformers.ts
  71. 7 6
      public/app/system.conf.js
  72. 33 1
      public/sass/components/_navbar.scss
  73. 14 7
      public/test/test-main.js
  74. 0 36
      public/vendor/angular-bindonce/.bower.json
  75. 0 43
      public/vendor/angular-bindonce/CHANGELOG.md
  76. 0 154
      public/vendor/angular-bindonce/README.md
  77. 0 325
      public/vendor/angular-bindonce/bindonce.js
  78. 0 0
      public/vendor/angular-bindonce/bindonce.min.js
  79. 0 28
      public/vendor/angular-bindonce/bower.json
  80. 0 28
      public/vendor/angular-bindonce/package.json
  81. 0 20
      public/vendor/angular-mocks/.bower.json
  82. 0 21
      public/vendor/angular-mocks/LICENSE.md
  83. 0 63
      public/vendor/angular-mocks/README.md
  84. 0 3408
      public/vendor/angular-mocks/angular-mocks.js
  85. 0 10
      public/vendor/angular-mocks/bower.json
  86. 0 2
      public/vendor/angular-mocks/ngAnimateMock.js
  87. 0 2
      public/vendor/angular-mocks/ngMock.js
  88. 0 2
      public/vendor/angular-mocks/ngMockE2E.js
  89. 0 34
      public/vendor/angular-mocks/package.json
  90. 0 36
      public/vendor/angular-native-dragdrop/.bower.json
  91. 0 25
      public/vendor/angular-native-dragdrop/Gulpfile.js
  92. 0 10
      public/vendor/angular-native-dragdrop/LICENSE
  93. 0 36
      public/vendor/angular-native-dragdrop/README.md
  94. 0 27
      public/vendor/angular-native-dragdrop/bower.json
  95. 0 29
      public/vendor/angular-native-dragdrop/demo/css/styles.css
  96. 0 178
      public/vendor/angular-native-dragdrop/demo/index.html
  97. 0 54
      public/vendor/angular-native-dragdrop/demo/js/app.js
  98. 0 8
      public/vendor/angular-native-dragdrop/docs/examples.md
  99. 0 26
      public/vendor/angular-native-dragdrop/docs/getting-started.md
  100. 0 120
      public/vendor/angular-native-dragdrop/docs/index.md

+ 0 - 3
.bowerrc

@@ -1,3 +0,0 @@
-{
-  "directory": "public/vendor/"
-}

+ 0 - 21
.jsfmtrc

@@ -1,21 +0,0 @@
-{
-  "preset" : "default",
-
-  "lineBreak" : {
-    "before" : {
-      "VariableDeclarationWithoutInit" : 0,
-    },
-
-    "after": {
-      "AssignmentOperator": -1,
-      "ArgumentListArrayExpression": ">=1"
-    }
-  },
-
-  "whiteSpace" : {
-    "before" : {
-    },
-    "after" : {
-    }
-  }
-}

+ 13 - 3
CHANGELOG.md

@@ -15,13 +15,20 @@
 * **Graph**: Add support for local formating in axis. [#1395](https://github.com/grafana/grafana/issues/1395), thx [@m0nhawk](https://github.com/m0nhawk)
 * **Jaeger**: Add support for open tracing using jaeger in Grafana. [#9213](https://github.com/grafana/grafana/pull/9213)
 * **Unit types**: New date & time unit types added, useful in singlestat to show dates & times. [#3678](https://github.com/grafana/grafana/issues/3678), [#6710](https://github.com/grafana/grafana/issues/6710), [#2764](https://github.com/grafana/grafana/issues/6710)
+* **CLI**: Make it possible to install plugins from any url [#5873](https://github.com/grafana/grafana/issues/5873)
+* **Prometheus**: Add support for instant queries [#5765](https://github.com/grafana/grafana/issues/5765), thx [@mtanda](https://github.com/mtanda)
 
-## Breaking changes
-* **Metrics**: The metric structure for internal metrics about Grafana published to graphite has changed. This might break dashboards for internal metrics. 
+## Minor
+* **SMTP**: Make it possible to set specific EHLO for smtp client. [#9319](https://github.com/grafana/grafana/issues/9319)
+* **Dataproxy**: Allow grafan to renegotiate tls connection [#9250](https://github.com/grafana/grafana/issues/9250)
 
-# 4.5.2 (unreleased)
+# 4.5.2 (2017-09-22)
 
 ## Fixes 
+* **Graphite**: Fix for issues with jsonData & graphiteVersion null errors [#9258](https://github.com/grafana/grafana/issues/9258)
+* **Graphite**: Fix for Grafana internal metrics to Graphite sending NaN values [#9279](https://github.com/grafana/grafana/issues/9279)
+* **HTTP API**: Fix for HEAD method requests [#9307](https://github.com/grafana/grafana/issues/9307)
+* **Templating**: Fix for duplicate template variable queries when refresh is set to time range change [#9185](https://github.com/grafana/grafana/issues/9185)
 * **Metrics**: dont write NaN values to graphite [#9279](https://github.com/grafana/grafana/issues/9279)
 
 # 4.5.1 (2017-09-15)
@@ -29,6 +36,9 @@
 ## Fixes
 * **MySQL**: Fixed issue with query editor not showing [#9247](https://github.com/grafana/grafana/issues/9247)
 
+## Breaking changes
+* **Metrics**: The metric structure for internal metrics about Grafana published to graphite has changed. This might break dashboards for internal metrics. 
+
 # 4.5.0 (2017-09-14)
 
 ## Fixes & Enhancements since beta1

+ 0 - 24
bower.json

@@ -1,24 +0,0 @@
-{
-  "name": "grafana",
-  "version": "2.0.2",
-  "homepage": "https://github.com/grafana/grafana",
-  "authors": [],
-  "license": "Apache 2.0",
-  "ignore": [
-    "**/.*",
-    "node_modules",
-    "bower_components",
-    "public/vendor/",
-    "test",
-    "tests"
-  ],
-  "dependencies": {
-    "angular": "1.6.1",
-    "angular-route": "1.6.1",
-    "angular-mocks": "1.6.1",
-    "angular-sanitize": "1.6.1",
-    "angular-native-dragdrop": "1.2.2",
-    "angular-bindonce": "0.3.3",
-    "clipboard": "^1.5.16"
-  }
-}

+ 3 - 0
conf/defaults.ini

@@ -318,6 +318,7 @@ key_file =
 skip_verify = false
 from_address = admin@grafana.localhost
 from_name = Grafana
+ehlo_identity =
 
 [emails]
 welcome_email_on_sign_up = false
@@ -476,6 +477,8 @@ provider =
 
 [external_image_storage.s3]
 bucket_url =
+bucket =
+region =
 access_key =
 secret_key =
 

+ 4 - 1
conf/sample.ini

@@ -295,6 +295,8 @@
 ;skip_verify = false
 ;from_address = admin@grafana.localhost
 ;from_name = Grafana
+# EHLO identity in SMTP dialog (defaults to instance_name)
+;ehlo_identity = dashboard.example.com
 
 [emails]
 ;welcome_email_on_sign_up = false
@@ -420,7 +422,8 @@
 ;provider =
 
 [external_image_storage.s3]
-;bucket_url =
+;bucket =
+;region =
 ;access_key =
 ;secret_key =
 

+ 12 - 13
docker/blocks/prometheus/prometheus.yml

@@ -17,20 +17,19 @@ alerting:
     - targets:
       - "127.0.0.1:9093"
 
-
-# A scrape configuration containing exactly one endpoint to scrape:
-# Here it's Prometheus itself.
 scrape_configs:
-  # The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
   - job_name: 'prometheus'
+    static_configs:
+      - targets: ['localhost:9090']
 
-    # Override the global default and scrape targets from this job every 5 seconds.
-    scrape_interval: 10s
-    scrape_timeout: 10s
-
-    # metrics_path defaults to '/metrics'
-    # scheme defaults to 'http'.
-
+  - job_name: 'node_exporter'
+    static_configs:
+      - targets: ['127.0.0.1:9100']
+ 
+  - job_name: 'fake-data-gen'
+    static_configs:
+      - targets: ['127.0.0.1:9091']
+  
+  - job_name: 'grafana'
     static_configs:
-      #- targets: ['localhost:9090', '172.17.0.1:9091', '172.17.0.1:9100', '172.17.0.1:9150']
-      - targets: ['localhost:9090', '127.0.0.1:9091', '127.0.0.1:9100', '127.0.0.1:9150']
+      - targets: ['127.0.0.1:3000']

+ 11 - 30
docs/sources/features/panels/dashlist.md

@@ -12,42 +12,23 @@ weight = 4
 
 # Dashboard List Panel
 
-The dashboard list panel allows you to display dynamic links to other dashboards. The list can be configured to use starred dashboards, a search query and/or dashboard tags.
+{{< docs-imagebox img="/img/docs/v45/dashboard-list-panels.png" max-width= "800px" >}}
 
-<img class="no-shadow" src="/img/docs/v2/dashboard_list_panels.png">
+The dashboard list panel allows you to display dynamic links to other dashboards. The list can be configured to use starred dashboards, recently viewed dashboards, a search query and/or dashboard tags.
 
 > On each dashboard load, the dashlist panel will re-query the dashboard list, always providing the most up to date results.
 
-## Mode: Starred Dashboards
+## Dashboard List Options
 
-The `starred` dashboard selection displays starred dashboards, up to the number specified in the `Limit Number to` field, in alphabetical order. On dashboard load, the dashlist panel will re-query the favorites to appear in dashboard list panel, always providing the most up to date results.
+{{< docs-imagebox img="/img/docs/v45/dashboard-list-options.png" max-width="600px" class="docs-image--no-shadow">}}
 
-<img class="no-shadow" src="/img/docs/v2/dashboard_list_config_starred.png">
-
-
-## Mode: Search Dashboards
-
-The panel may be configured to search by either string query or tag(s). On dashboard load, the dashlist panel will re-query the dashboard list, always providing the most up to date results.
-
-To configure dashboard list in this manner, select `search` from the Mode select box. When selected, the Search Options section will appear.
-
-
-Name | Description
------------- | -------------
-Mode | Set search or starred mode
-Query | If in search mode specify the search query
-Tags | if in search mode specify dashboard tags to search for
-Limit number to | Specify the maximum number of dashboards
-
-
-### Search by string
-
-To search by a string, enter a search query in the `Search Options: Query` field. Queries are case-insensitive, and partial values are accepted.
-<img class="no-shadow" src="/img/docs/v2/dashboard_list_config_string.png">
-
-### Search by tag
-To search by one or more tags, enter your selection in the `Search Options: Tags:` field. Note that existing tags will not appear as you type, and *are* case sensitive. To see a list of existing tags, you can always return to the dashboard, open the Dashboard Picker at the top and click `tags` link in the search bar.
-<img class="no-shadow" src="/img/docs/v2/dashboard_list_config_tags.png">
+1. `Starred`: The starred dashboard selection displays starred dashboards in alphabetical order.
+2. `Recently Viewed`: The recently viewed dashboard selection displays recently viewed dashboards in alphabetical order.
+3. `Search`: The search dashboard selection displays dashboards by search query or tag(s).
+4. `Show Headings`: When show headings is ticked the choosen list selection(Starred, Recently Viewed, Search) is shown as a heading.
+5. `Max Items`: Max items set the maximum of items in a list.
+6. `Query`: Here is where you enter your query you want to search by. Queries are case-insensitive, and partial values are accepted.
+7. `Tags`: Here is where you enter your tag(s) you want to search by. Note that existing tags will not appear as you type, and *are* case sensitive. To see a list of existing tags, you can always return to the dashboard, open the Dashboard Picker at the top and click `tags` link in the search bar.
 
 > When multiple tags and strings appear, the dashboard list will display those matching ALL conditions.
 

+ 10 - 7
docs/sources/features/panels/graph.md

@@ -13,14 +13,15 @@ weight = 1
 
 The main panel in Grafana is simply named Graph. It provides a very rich set of graphing options.
 
-<img src="/img/docs/v1/graph_overview.png" class="no-shadow">
+{{< docs-imagebox img="/img/docs/v45/graph_overview.png" class="docs-image--no-shadow" max-width= "900px" >}}
 
-Clicking the title for a panel exposes a menu.  The `edit` option opens additional configuration
-options for the panel.
+1. Clicking the title for a panel exposes a menu.  The `edit` option opens additional configuration options for the panel.
+2. Click to open color & axis selection.
+3. Click to only show this series. Shift/Ctrl + click to hide series.
 
 ## General
 
-![](/img/docs/v43/graph_general.png)
+{{< docs-imagebox img="/img/docs/v43/graph_general.png"  max-width= "900px" >}}
 
 The general tab allows customization of a panel's appearance and menu options.
 
@@ -52,7 +53,7 @@ options.
 
 ## Axes
 
-![](/img/docs/v43/graph_axes_grid_options.png)
+{{< docs-imagebox img="/img/docs/v43/graph_axes_grid_options.png"  max-width= "900px" >}}
 
 The Axes tab controls the display of axes, grids and legend.  The ``Left Y`` and ``Right Y`` can be customized using:
 
@@ -101,7 +102,7 @@ It is just the sum of all data points received by Grafana.
 
 ## Display styles
 
-![](/img/docs/v43/graph_display_styles.png)
+{{< docs-imagebox img="/img/docs/v43/graph_display_styles.png"  max-width= "900px" >}}
 
 Display styles control visual properties of the graph.
 
@@ -156,4 +157,6 @@ There is an option under Series overrides to draw lines as dashes. Set Dashes to
 
 ## Time Range
 
-![](/img/docs/v2/graph_time_range.png)
+The time range tab allows you to override the dashboard time range and specify a panel specific time. Either through a relative from now time option or through a timeshift.
+
+{{< docs-imagebox img="/img/docs/v45/graph-time-range.png"  max-width= "900px" >}}

+ 32 - 17
docs/sources/features/panels/singlestat.md

@@ -12,7 +12,7 @@ weight = 2
 
 # Singlestat Panel
 
-![](/img/docs/v1/singlestat_panel2.png)
+{{< docs-imagebox img="/img/docs/v45/singlestat-panel.png" max-width="900px" >}}
 
 The Singlestat Panel allows you to show the one main summary stat of a SINGLE series. It reduces the series into a single number (by looking at the max, min, average, or sum of values in the series). Singlestat also provides thresholds to color the stat or the Panel background. It can also translate the single number into a text value, and show a sparkline summary of the series.
 
@@ -20,11 +20,9 @@ The Singlestat Panel allows you to show the one main summary stat of a SINGLE se
 
 The singlestat panel has a normal query editor to allow you define your exact metric queries like many other Panels. Through the Options tab, you can access the Singlestat-specific functionality.
 
-<img class="no-shadow" src="/img/docs/v1/Singlestat-BaseSettings.png">
+{{< docs-imagebox img="/img/docs/v45/singlestat-value-options.png" class="docs-image--no-shadow" max-width= "900px" >}}
 
-1. `Big Value`: Big Value refers to how we display the main stat for the Singlestat Panel. This is always a single value that is displayed in the Panel in between two strings, `Prefix` and  `Suffix`. The single number is calculated by choosing a function (min,max,average,current,total) of your metric query. This functions reduces your query into a single numeric value.
-2. `Font Size`: You can use this section to select the font size of the different texts in the Singlestat Panel, i.e. prefix, value and postfix.
-3. `Values`: The Value fields let you set the function (min, max, average, current, total, first, delta, range) that your entire query is reduced into a single value with. You can also set the font size of the Value field and font-size (as a %) of the metric query that the Panel is configured with. This reduces the entire query into a single summary value that is displayed.
+1. `Stats`: The Stats field let you set the function (min, max, average, current, total, first, delta, range) that your entire query is reduced into a single value with. This reduces the entire query into a single summary value that is displayed.
    * `min` - The smallest value in the series
    * `max` - The largest value in the series
    * `avg` - The average of all the non-null values in the series
@@ -34,47 +32,64 @@ The singlestat panel has a normal query editor to allow you define your exact me
    * `delta` - The total incremental increase (of a counter) in the series. An attempt is made to account for counter resets, but this will only be accurate for single instance metrics. Used to show total counter increase in time series.
    * `diff` - The difference betwen 'current' (last value) and 'first'.
    * `range` - The difference between 'min' and 'max'. Useful the show the range of change for a gauge.
-4. `Prefix/Postfix`: The Prefix/Postfix fields let you define a custom label and font-size (as a %) to appear *before/after* the value. The `$__name` variable can be used here to use the series name or alias from the metric query.
-5. `Units`: Units are appended to the the Singlestat  within the panel, and will respect the color and threshold settings for the value.
-6. `Decimals`: The Decimal field allows you to override the automatic decimal precision, and set it explicitly.
+2. `Prefix/Postfix`: The Prefix/Postfix fields let you define a custom label to appear *before/after* the value. The `$__name` variable can be used here to use the series name or alias from the metric query.
+3. `Units`: Units are appended to the the Singlestat  within the panel, and will respect the color and threshold settings for the value.
+4. `Decimals`: The Decimal field allows you to override the automatic decimal precision, and set it explicitly.
+5. `Font Size`: You can use this section to select the font size of the different texts in the Singlestat Panel, i.e. prefix, value and postfix.
 
 ### Coloring
 
 The coloring options of the Singlestat Panel config allow you to dynamically change the colors based on the Singlestat value.
 
-<img class="no-shadow" src="/img/docs/v1/Singlestat-Coloring.png">
+{{< docs-imagebox img="/img/docs/v45/singlestat-color-options.png" max-width="500px" class="docs-image--right docs-image--no-shadow">}}
 
 1. `Background`: This checkbox applies the configured thresholds and colors to the entirety of the Singlestat Panel background.
-2. `Value`: This checkbox applies the configured thresholds and colors to the summary stat.
-3. `Thresholds`: Change the background and value colors dynamically within the panel, depending on the Singlestat value. The threshold field accepts **2 comma-separated** values which represent 3 ranges that correspond to the three colors directly to the right. For example: if the thresholds are 70, 90 then the first color represents < 70, the second color represents between 70 and 90 and the third color represents > 90.
-4. `Colors`: Select a color and opacity
+2. `Thresholds`: Change the background and value colors dynamically within the panel, depending on the Singlestat value. The threshold field accepts **2 comma-separated** values which represent 3 ranges that correspond to the three colors directly to the right. For example: if the thresholds are 70, 90 then the first color represents < 70, the second color represents between 70 and 90 and the third color represents > 90.
+3. `Colors`: Select a color and opacity
+4. `Value`: This checkbox applies the configured thresholds and colors to the summary stat.
 5. `Invert order`: This link toggles the threshold color order.</br>For example: Green, Orange, Red (<img class="no-shadow" src="/img/docs(v1/gyr.png">) will become Red, Orange, Green (<img class="no-shadow" src="/img/docs/v1/ryg.png">).
 
 ### Spark Lines
 
 Sparklines are a great way of seeing the historical data related to the summary stat, providing valuable context at a glance. Sparklines act differently than traditional Graph Panels and do not include x or y axis, coordinates, a legend, or ability to interact with the graph.
 
-<img class="no-shadow" src="/img/docs/v1/Singlestat-Sparklines.png">
+{{< docs-imagebox img="/img/docs/v45/singlestat-spark-options.png" max-width="500px" class="docs-image--right docs-image--no-shadow">}}
 
 1. `Show`: The show checkbox will toggle whether the spark line is shown in the Panel. When unselected, only the Singlestat value will appear.
-2. `Background`: Check if you want the sparklines to take up the full panel width, or uncheck if they should be below the main Singlestat value.
+2. `Full Height`: Check if you want the sparklines to take up the full panel height, or uncheck if they should be below the main Singlestat value.
 3. `Line Color`: This color selection applies to the color of the sparkline itself.
 4. `Fill Color`: This color selection applies to the area below the sparkline.
 
+<div class="clearfix"></div>
+
 > ***Pro-tip:*** Reduce the opacity on  fill colors for nice looking panels.
 
+### Gauge
+
+Gauges gives a clear picture of how high a value is in it's context. It's a great way to see if a value is close to the thresholds. The gauge uses the colors set in the color options.
+
+{{< docs-imagebox img="/img/docs/v45/singlestat-gauge-options.png" max-width="500px" class="docs-image--right docs-image--no-shadow">}}
+
+1. `Show`: The show checkbox will toggle wether the gauge is shown in the panel. When unselected, only the Singlestat value will appear.
+2. `Min/Max`: This sets the start and end point for the gauge.
+3. `Threshold Labels`: Check if you want to show the threshold labels. Thresholds are set in the color options.
+4. `Threshold Markers`: Check if you want to have a second meter showing the thresholds.
+
+<div class="clearfix"></div>
+
 ### Value to text mapping
 
+{{< docs-imagebox img="/img/docs/v45/singlestat-value-mapping.png" class="docs-image--right docs-image--no-shadow">}}
+
 Value to text mapping allows you to translate the value of the summary stat into explicit text. The text will respect all styling, thresholds and customization defined for the value. This can be useful to translate the number of the main Singlestat value into a context-specific human-readable word or message.
 
-<img class="no-shadow" src="/img/docs/v1/Singlestat-ValueMapping.png">
+<div class="clearfix"></div>
 
 ## Troubleshooting
 
 ### Multiple Series Error
 
-<img class="no-shadow" src="/img/docs/v2/Singlestat-MultiSeriesError.png">
-
+{{< docs-imagebox img="/img/docs/v45/singelstat-multiple-series-error.png" class="docs-image--right docs-image--no-shadow">}}
 
 Grafana 2.5 introduced stricter checking for multiple-series on singlestat panels. In previous versions, the panel logic did not verify that only a single series was used, and instead, displayed the first series encountered. Depending on your data source, this could have lead to inconsistent data being shown and/or a general confusion about which metric was being displayed.
 

+ 1 - 0
docs/sources/http_api/admin.md

@@ -161,6 +161,7 @@ Only works with Basic Authentication (username and password). See [introduction]
         "enabled":"false",
         "from_address":"admin@grafana.localhost",
         "from_name":"Grafana",
+        "ehlo_identity":"dashboard.example.com",
         "host":"localhost:25",
         "key_file":"",
         "password":"************",

+ 8 - 1
docs/sources/installation/configuration.md

@@ -593,6 +593,9 @@ Address used when sending out emails, defaults to `admin@grafana.localhost`
 ### from_name
 Name to be used when sending out emails, defaults to `Grafana`
 
+### ehlo_identity
+Name to be used as client identity for EHLO in SMTP dialog, defaults to instance_name.
+
 ## [log]
 
 ### mode
@@ -648,12 +651,16 @@ These options control how images should be made public so they can be shared on
 You can choose between (s3, webdav, gcs). If left empty Grafana will ignore the upload action.
 
 ## [external_image_storage.s3]
+### bucket
+Bucket name for S3. e.g. grafana.snapshot
+### region
+Region name for S3. e.g. 'us-east-1', 'cn-north-1', etc
 
 ### bucket_url
+(for backward compatibility, only works when no bucket or region are configured)
 Bucket URL for S3. AWS region can be specified within URL or defaults to 'us-east-1', e.g.
 - http://grafana.s3.amazonaws.com/
 - https://grafana.s3-ap-southeast-2.amazonaws.com/
-- https://grafana.s3-cn-north-1.amazonaws.com.cn
 
 ### access_key
 Access key. e.g. AAAAAAAAAAAAAAAAAAAA

+ 5 - 5
docs/sources/installation/debian.md

@@ -15,7 +15,7 @@ weight = 1
 
 Description | Download
 ------------ | -------------
-Stable for Debian-based Linux | [grafana_4.5.1_amd64.deb](https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_4.5.1_amd64.deb)
+Stable for Debian-based Linux | [grafana_4.5.2_amd64.deb](https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_4.5.2_amd64.deb)
 
 <!-- Beta for Debian-based Linux | [grafana_4.5.0-beta1_amd64.deb](https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_4.5.0-beta1_amd64.deb) -->
 
@@ -26,18 +26,18 @@ installation.
 
 
 ```bash
-wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_4.5.1_amd64.deb
+wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_4.5.2_amd64.deb
 sudo apt-get install -y adduser libfontconfig
-sudo dpkg -i grafana_4.5.1_amd64.deb
+sudo dpkg -i grafana_4.5.2_amd64.deb
 ```
 
 <!--
 ## Install Latest Beta
 
 ```bash
-wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_4.5.0-beta1_amd64.deb
+wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_4.5.2-beta1_amd64.deb
 sudo apt-get install -y adduser libfontconfig
-sudo dpkg -i grafana_4.5.0-beta1_amd64.deb
+sudo dpkg -i grafana_4.5.2-beta1_amd64.deb
 ```
 -->
 

+ 32 - 0
docs/sources/installation/mac.md

@@ -43,3 +43,35 @@ To upgrade grafana if you've installed from HEAD:
 ```
 brew reinstall --HEAD grafana/grafana/grafana
 ```
+
+### Starting Grafana
+
+To start Grafana using homebrew services first make sure homebrew/services is installed.
+
+```
+brew tap homebrew/services
+```
+
+Then start Grafana using:
+
+```
+brew services start grafana
+```
+
+
+### Configuration
+
+The Configuration file should be located at `/usr/local/etc/grafana/grafana.ini`.
+
+### Logs
+
+The log file should be located at `/usr/local/var/log/grafana/grafana.log`.
+
+### Plugins
+
+If you want to manually install a plugin place it here: `/usr/local/var/lib/grafana/plugins`.
+
+### Database
+
+The default sqlite database is located at `/usr/local/var/lib/grafana`
+

+ 5 - 5
docs/sources/installation/rpm.md

@@ -15,7 +15,7 @@ weight = 2
 
 Description | Download
 ------------ | -------------
-Stable for CentOS / Fedora / OpenSuse / Redhat Linux | [4.5.1 (x86-64 rpm)](https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-4.5.1-1.x86_64.rpm)
+Stable for CentOS / Fedora / OpenSuse / Redhat Linux | [4.5.2 (x86-64 rpm)](https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-4.5.2-1.x86_64.rpm)
 
 <!-- Latest Beta for CentOS / Fedora / OpenSuse / Redhat Linux | [4.5.0-beta1 (x86-64 rpm)](https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-4.5.0-beta1.x86_64.rpm) -->
 
@@ -26,19 +26,19 @@ installation.
 
 You can install Grafana using Yum directly.
 
-    $ sudo yum install https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-4.5.1-1.x86_64.rpm
+    $ sudo yum install https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-4.5.2-1.x86_64.rpm
 
 Or install manually using `rpm`.
 
 #### On CentOS / Fedora / Redhat:
 
-    $ wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-4.5.1-1.x86_64.rpm
+    $ wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-4.5.2-1.x86_64.rpm
     $ sudo yum install initscripts fontconfig
-    $ sudo rpm -Uvh grafana-4.5.1-1.x86_64.rpm
+    $ sudo rpm -Uvh grafana-4.5.2-1.x86_64.rpm
 
 #### On OpenSuse:
 
-    $ sudo rpm -i --nodeps grafana-4.5.1-1.x86_64.rpm
+    $ sudo rpm -i --nodeps grafana-4.5.2-1.x86_64.rpm
 
 ## Install via YUM Repository
 

+ 1 - 1
docs/sources/installation/windows.md

@@ -13,7 +13,7 @@ weight = 3
 
 Description | Download
 ------------ | -------------
-Latest stable package for Windows | [grafana.4.5.1.windows-x64.zip](https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-4.5.1.windows-x64.zip)
+Latest stable package for Windows | [grafana.4.5.2.windows-x64.zip](https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-4.5.2.windows-x64.zip)
 
 Read [Upgrading Grafana]({{< relref "installation/upgrading.md" >}}) for tips and guidance on updating an existing
 installation.

+ 5 - 0
docs/sources/plugins/installation.md

@@ -72,6 +72,11 @@ The Download URL from Grafana.com API is in this form:
 
 `https://grafana.com/api/plugins/<plugin id>/versions/<version number>/download`
 
+You can specify a local URL by using the `--pluginUrl` option.
+```
+grafana-cli --pluginUrl https://nexus.company.com/grafana/plugins/<plugin-id>-<plugin-version>.zip plugins install <plugin-id>
+```
+
 To manually install a Plugin via the Grafana.com API:
 
 1. Find the plugin you want to download, the plugin id can be found on the Installation Tab on the plugin's page on Grafana.com. In this example, the plugin id is `jdbranham-diagram-panel`:

+ 0 - 34
docs/sources/reference/keyboard_shortcuts.md

@@ -1,34 +0,0 @@
-+++
-title = "Keyboard shortcuts"
-keywords = ["grafana", "dashboard", "documentation", "shortcuts"]
-type = "docs"
-[menu.docs]
-parent = "dashboard_features"
-weight = 8
-+++
-
-# Keyboard Shortcuts
-
-No mouse? No problem. Grafana has extensive keyboard shortcuts to allow you to navigate throughout the interface. This comes in especially handy when dealing with dealing with single-purpose machines powering on-wall displays that may not have a mouse available.
-
-## Dashboard Keyboard Shortcuts
-
-Press `Shift`+`?` to open the keyboard shortcut dialog from anywhere within the dashboard views.
-
-<img class="no-shadow" src="/img/docs/v2/Grafana-Keyboard-Shortcuts.gif" style="width:80%;">
-
-
-|Shortcut|Action|
-|---|---|
-|`Esc`|Exit fullscreen edit/view mode, close search or any editor view|
-|`F`|Open dashboard search view (also contains import/playlist controls)|
-|`R`|Refresh (Fetches new data and rerenders panels)|
-|`CTRL`+`S`|Save dashboard|
-|`CTRL`+`H`|Hide row controls|
-|`CTRL`+`Z`|Zoom out|
-|`CTRL`+`O`|Enable/Disable shared graph crosshair|
-
-
-**Note**: Grafana keyboard shortcuts are the same across operating system.
-
-Have a suggestion for a new keyboard shortcut? Let us know.

+ 2 - 2
latest.json

@@ -1,4 +1,4 @@
 {
-  "stable": "4.4.1",
-	"testing": "4.4.1"
+  "stable": "4.5.2",
+	"testing": "4.5.2"
 }

+ 7 - 0
package.json

@@ -64,6 +64,13 @@
   "dependencies": {
     "@types/enzyme": "^2.8.8",
     "ace-builds": "^1.2.8",
+    "angular": "^1.6.6",
+    "angular-bindonce": "^0.3.1",
+    "angular-mocks": "^1.6.6",
+    "angular-native-dragdrop": "^1.2.2",
+    "angular-route": "^1.6.6",
+    "angular-sanitize": "^1.6.6",
+    "clipboard": "^1.7.1",
     "eventemitter3": "^2.0.2",
     "gaze": "^1.1.2",
     "gridstack": "https://github.com/grafana/gridstack.js#grafana",

+ 7 - 7
packaging/publish/publish_both.sh

@@ -1,5 +1,5 @@
 #! /usr/bin/env bash
-version=4.5.1
+version=4.5.2
 
 wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_${version}_amd64.deb
 
@@ -8,15 +8,15 @@ package_cloud push grafana/stable/debian/wheezy grafana_${version}_amd64.deb
 package_cloud push grafana/stable/debian/stretch grafana_${version}_amd64.deb
 
 package_cloud push grafana/testing/debian/jessie grafana_${version}_amd64.deb
-package_cloud push grafana/testing/debian/wheezy grafana_${version}_amd64.deb
-package_cloud push grafana/testing/debian/stretch grafana_${version}_amd64.deb
+package_cloud push grafana/testing/debian/wheezy grafana_${version}_amd64.deb --verbose
+package_cloud push grafana/testing/debian/stretch grafana_${version}_amd64.deb --verbose
 
 wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-${version}-1.x86_64.rpm
 
-package_cloud push grafana/testing/el/6 grafana-${version}-1.x86_64.rpm
-package_cloud push grafana/testing/el/7 grafana-${version}-1.x86_64.rpm
+package_cloud push grafana/testing/el/6 grafana-${version}-1.x86_64.rpm --verbose
+package_cloud push grafana/testing/el/7 grafana-${version}-1.x86_64.rpm --verbose
 
-package_cloud push grafana/stable/el/7 grafana-${version}-1.x86_64.rpm
-package_cloud push grafana/stable/el/6 grafana-${version}-1.x86_64.rpm
+package_cloud push grafana/stable/el/7 grafana-${version}-1.x86_64.rpm --verbose
+package_cloud push grafana/stable/el/6 grafana-${version}-1.x86_64.rpm --verbose
 
 rm grafana*.{deb,rpm}

+ 5 - 2
pkg/api/app_routes.go

@@ -17,8 +17,11 @@ import (
 )
 
 var pluginProxyTransport = &http.Transport{
-	TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
-	Proxy:           http.ProxyFromEnvironment,
+	TLSClientConfig: &tls.Config{
+		InsecureSkipVerify: true,
+		Renegotiation:      tls.RenegotiateFreelyAsClient,
+	},
+	Proxy: http.ProxyFromEnvironment,
 	Dial: (&net.Dialer{
 		Timeout:   30 * time.Second,
 		KeepAlive: 30 * time.Second,

+ 4 - 3
pkg/api/metrics.go

@@ -43,7 +43,7 @@ func QueryMetrics(c *middleware.Context, reqDto dtos.MetricRequest) Response {
 		})
 	}
 
-	resp, err := tsdb.HandleRequest(context.Background(), request)
+	resp, err := tsdb.HandleRequest(context.Background(), dsQuery.Result, request)
 	if err != nil {
 		return ApiError(500, "Metric request error", err)
 	}
@@ -100,16 +100,17 @@ func GetTestDataRandomWalk(c *middleware.Context) Response {
 	timeRange := tsdb.NewTimeRange(from, to)
 	request := &tsdb.TsdbQuery{TimeRange: timeRange}
 
+	dsInfo := &models.DataSource{Type: "grafana-testdata-datasource"}
 	request.Queries = append(request.Queries, &tsdb.Query{
 		RefId:      "A",
 		IntervalMs: intervalMs,
 		Model: simplejson.NewFromAny(&util.DynMap{
 			"scenario": "random_walk",
 		}),
-		DataSource: &models.DataSource{Type: "grafana-testdata-datasource"},
+		DataSource: dsInfo,
 	})
 
-	resp, err := tsdb.HandleRequest(context.Background(), request)
+	resp, err := tsdb.HandleRequest(context.Background(), dsInfo, request)
 	if err != nil {
 		return ApiError(500, "Metric request error", err)
 	}

+ 5 - 0
pkg/cmd/grafana-cli/commands/command_line.go

@@ -19,6 +19,7 @@ type CommandLine interface {
 
 	PluginDirectory() string
 	RepoDirectory() string
+	PluginURL() string
 }
 
 type contextCommandLine struct {
@@ -44,3 +45,7 @@ func (c *contextCommandLine) PluginDirectory() string {
 func (c *contextCommandLine) RepoDirectory() string {
 	return c.GlobalString("repo")
 }
+
+func (c *contextCommandLine) PluginURL() string {
+	return c.GlobalString("pluginUrl")
+}

+ 4 - 0
pkg/cmd/grafana-cli/commands/commandstest/fake_commandLine.go

@@ -101,3 +101,7 @@ func (fcli *FakeCommandLine) RepoDirectory() string {
 func (fcli *FakeCommandLine) PluginDirectory() string {
 	return fcli.GlobalString("pluginsDir")
 }
+
+func (fcli *FakeCommandLine) PluginURL() string {
+	return fcli.GlobalString("pluginUrl")
+}

+ 20 - 18
pkg/cmd/grafana-cli/commands/install_command.go

@@ -58,37 +58,39 @@ func installCommand(c CommandLine) error {
 }
 
 func InstallPlugin(pluginName, version string, c CommandLine) error {
-	plugin, err := s.GetPlugin(pluginName, c.RepoDirectory())
 	pluginFolder := c.PluginDirectory()
-	if err != nil {
-		return err
-	}
+	downloadURL := c.PluginURL()
+	if downloadURL == "" {
+		plugin, err := s.GetPlugin(pluginName, c.RepoDirectory())
+		if err != nil {
+			return err
+		}
 
-	v, err := SelectVersion(plugin, version)
-	if err != nil {
-		return err
-	}
+		v, err := SelectVersion(plugin, version)
+		if err != nil {
+			return err
+		}
 
-	if version == "" {
-		version = v.Version
+		if version == "" {
+			version = v.Version
+		}
+		downloadURL = fmt.Sprintf("%s/%s/versions/%s/download",
+			c.GlobalString("repo"),
+			pluginName,
+			version)
 	}
 
-	downloadURL := fmt.Sprintf("%s/%s/versions/%s/download",
-		c.GlobalString("repo"),
-		pluginName,
-		version)
-
-	logger.Infof("installing %v @ %v\n", plugin.Id, version)
+	logger.Infof("installing %v @ %v\n", pluginName, version)
 	logger.Infof("from url: %v\n", downloadURL)
 	logger.Infof("into: %v\n", pluginFolder)
 	logger.Info("\n")
 
-	err = downloadFile(plugin.Id, pluginFolder, downloadURL)
+	err := downloadFile(pluginName, pluginFolder, downloadURL)
 	if err != nil {
 		return err
 	}
 
-	logger.Infof("%s Installed %s successfully \n", color.GreenString("✔"), plugin.Id)
+	logger.Infof("%s Installed %s successfully \n", color.GreenString("✔"), pluginName)
 
 	res, _ := s.ReadPlugin(pluginFolder, pluginName)
 	for _, v := range res.Dependencies.Plugins {

+ 6 - 0
pkg/cmd/grafana-cli/main.go

@@ -38,6 +38,12 @@ func main() {
 			Value:  "https://grafana.com/api/plugins",
 			EnvVar: "GF_PLUGIN_REPO",
 		},
+		cli.StringFlag{
+			Name:   "pluginUrl",
+			Usage:  "Full url to the plugin zip file instead of downloading the plugin from grafana.com/api",
+			Value:  "",
+			EnvVar: "GF_PLUGIN_URL",
+		},
 		cli.BoolFlag{
 			Name:  "debug, d",
 			Usage: "enable debug logging",

+ 4 - 1
pkg/cmd/grafana-server/main.go

@@ -14,6 +14,7 @@ import (
 	"net/http"
 	_ "net/http/pprof"
 
+	"github.com/grafana/grafana/pkg/metrics"
 	"github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/services/sqlstore"
 	"github.com/grafana/grafana/pkg/setting"
@@ -29,7 +30,7 @@ import (
 	_ "github.com/grafana/grafana/pkg/tsdb/testdata"
 )
 
-var version = "4.1.0"
+var version = "4.6.0"
 var commit = "NA"
 var buildstamp string
 var build_date string
@@ -80,6 +81,8 @@ func main() {
 	setting.BuildCommit = commit
 	setting.BuildStamp = buildstampInt64
 
+	metrics.M_Grafana_Version.WithLabelValues(version).Set(1)
+
 	server := NewGrafanaServer()
 	server.Start()
 }

+ 10 - 4
pkg/components/imguploader/imguploader.go

@@ -28,15 +28,21 @@ func NewImageUploader() (ImageUploader, error) {
 			return nil, err
 		}
 
+		bucket := s3sec.Key("bucket").MustString("")
+		region := s3sec.Key("region").MustString("")
 		bucketUrl := s3sec.Key("bucket_url").MustString("")
 		accessKey := s3sec.Key("access_key").MustString("")
 		secretKey := s3sec.Key("secret_key").MustString("")
-		info, err := getRegionAndBucketFromUrl(bucketUrl)
-		if err != nil {
-			return nil, err
+		if bucket == "" || region == "" {
+			info, err := getRegionAndBucketFromUrl(bucketUrl)
+			if err != nil {
+				return nil, err
+			}
+			bucket = info.bucket
+			region = info.region
 		}
 
-		return NewS3Uploader(info.region, info.bucket, "public-read", accessKey, secretKey), nil
+		return NewS3Uploader(region, bucket, "public-read", accessKey, secretKey), nil
 	case "webdav":
 		webdavSec, err := setting.Cfg.GetSection("external_image_storage.webdav")
 		if err != nil {

+ 5 - 7
pkg/components/imguploader/s3uploader.go

@@ -9,6 +9,7 @@ import (
 	"github.com/aws/aws-sdk-go/aws/credentials"
 	"github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds"
 	"github.com/aws/aws-sdk-go/aws/ec2metadata"
+	"github.com/aws/aws-sdk-go/aws/endpoints"
 	"github.com/aws/aws-sdk-go/aws/session"
 	"github.com/aws/aws-sdk-go/service/s3"
 	"github.com/grafana/grafana/pkg/log"
@@ -54,8 +55,10 @@ func (u *S3Uploader) Upload(ctx context.Context, imageDiskPath string) (string,
 		Credentials: creds,
 	}
 
+	s3_endpoint, _ := endpoints.DefaultResolver().EndpointFor("s3", u.region)
 	key := util.GetRandomString(20) + ".png"
-	log.Debug("Uploading image to s3", "bucket = ", u.bucket, ", key = ", key)
+	image_url := s3_endpoint.URL + "/" + u.bucket + "/" + key
+	log.Debug("Uploading image to s3", "url = ", image_url)
 
 	file, err := os.Open(imageDiskPath)
 	if err != nil {
@@ -78,10 +81,5 @@ func (u *S3Uploader) Upload(ctx context.Context, imageDiskPath string) (string,
 	if err != nil {
 		return "", err
 	}
-
-	if u.region == "us-east-1" {
-		return "https://" + u.bucket + ".s3.amazonaws.com/" + key, nil
-	} else {
-		return "https://" + u.bucket + ".s3-" + u.region + ".amazonaws.com/" + key, nil
-	}
+	return image_url, nil
 }

+ 10 - 1
pkg/metrics/metrics.go

@@ -56,6 +56,7 @@ var (
 	M_StatTotal_Users        prometheus.Gauge
 	M_StatTotal_Orgs         prometheus.Gauge
 	M_StatTotal_Playlists    prometheus.Gauge
+	M_Grafana_Version        *prometheus.GaugeVec
 )
 
 func init() {
@@ -263,6 +264,13 @@ func init() {
 		Help:      "total amount of playlists",
 		Namespace: exporterName,
 	})
+
+	M_Grafana_Version = prometheus.NewGaugeVec(prometheus.GaugeOpts{
+		Name:      "info",
+		Help:      "Information about the Grafana",
+		Namespace: exporterName,
+	}, []string{"version"})
+
 }
 
 func initMetricVars(settings *MetricSettings) {
@@ -298,7 +306,8 @@ func initMetricVars(settings *MetricSettings) {
 		M_StatTotal_Dashboards,
 		M_StatTotal_Users,
 		M_StatTotal_Orgs,
-		M_StatTotal_Playlists)
+		M_StatTotal_Playlists,
+		M_Grafana_Version)
 
 	go instrumentationLoop(settings)
 }

+ 1 - 0
pkg/models/datasource_cache.go

@@ -48,6 +48,7 @@ func (ds *DataSource) GetHttpTransport() (*http.Transport, error) {
 	transport := &http.Transport{
 		TLSClientConfig: &tls.Config{
 			InsecureSkipVerify: true,
+			Renegotiation:      tls.RenegotiateFreelyAsClient,
 		},
 		Proxy: http.ProxyFromEnvironment,
 		Dial: (&net.Dialer{

+ 1 - 1
pkg/services/alerting/conditions/query.go

@@ -112,7 +112,7 @@ func (c *QueryCondition) executeQuery(context *alerting.EvalContext, timeRange *
 	req := c.getRequestForAlertRule(getDsInfo.Result, timeRange)
 	result := make(tsdb.TimeSeriesSlice, 0)
 
-	resp, err := c.HandleRequest(context.Ctx, req)
+	resp, err := c.HandleRequest(context.Ctx, getDsInfo.Result, req)
 	if err != nil {
 		if err == gocontext.DeadlineExceeded {
 			return nil, fmt.Errorf("Alert execution exceeded the timeout")

+ 1 - 1
pkg/services/alerting/conditions/query_test.go

@@ -168,7 +168,7 @@ func (ctx *queryConditionTestContext) exec() (*alerting.ConditionResult, error)
 
 	ctx.condition = condition
 
-	condition.HandleRequest = func(context context.Context, req *tsdb.TsdbQuery) (*tsdb.Response, error) {
+	condition.HandleRequest = func(context context.Context, dsInfo *m.DataSource, req *tsdb.TsdbQuery) (*tsdb.Response, error) {
 		return &tsdb.Response{
 			Results: map[string]*tsdb.QueryResult{
 				"A": {Series: ctx.series},

+ 5 - 1
pkg/services/notifications/mailer.go

@@ -101,7 +101,11 @@ func createDialer() (*gomail.Dialer, error) {
 
 	d := gomail.NewDialer(host, iPort, setting.Smtp.User, setting.Smtp.Password)
 	d.TLSConfig = tlsconfig
-	d.LocalName = setting.InstanceName
+	if setting.Smtp.EhloIdentity != "" {
+		d.LocalName = setting.Smtp.EhloIdentity
+	} else {
+		d.LocalName = setting.InstanceName
+	}
 	return d, nil
 }
 

+ 11 - 9
pkg/setting/setting_smtp.go

@@ -1,15 +1,16 @@
 package setting
 
 type SmtpSettings struct {
-	Enabled     bool
-	Host        string
-	User        string
-	Password    string
-	CertFile    string
-	KeyFile     string
-	FromAddress string
-	FromName    string
-	SkipVerify  bool
+	Enabled      bool
+	Host         string
+	User         string
+	Password     string
+	CertFile     string
+	KeyFile      string
+	FromAddress  string
+	FromName     string
+	EhloIdentity string
+	SkipVerify   bool
 
 	SendWelcomeEmailOnSignUp bool
 	TemplatesPattern         string
@@ -25,6 +26,7 @@ func readSmtpSettings() {
 	Smtp.KeyFile = sec.Key("key_file").String()
 	Smtp.FromAddress = sec.Key("from_address").String()
 	Smtp.FromName = sec.Key("from_name").String()
+	Smtp.EhloIdentity = sec.Key("ehlo_identity").String()
 	Smtp.SkipVerify = sec.Key("skip_verify").MustBool(false)
 
 	emails := Cfg.Section("emails")

+ 5 - 5
pkg/tsdb/fake_test.go

@@ -20,18 +20,18 @@ func NewFakeExecutor(dsInfo *models.DataSource) (*FakeExecutor, error) {
 	}, nil
 }
 
-func (e *FakeExecutor) Query(ctx context.Context, dsInfo *models.DataSource, context *TsdbQuery) *BatchResult {
-	result := &BatchResult{QueryResults: make(map[string]*QueryResult)}
+func (e *FakeExecutor) Query(ctx context.Context, dsInfo *models.DataSource, context *TsdbQuery) (*Response, error) {
+	result := &Response{Results: make(map[string]*QueryResult)}
 	for _, query := range context.Queries {
 		if results, has := e.results[query.RefId]; has {
-			result.QueryResults[query.RefId] = results
+			result.Results[query.RefId] = results
 		}
 		if testFunc, has := e.resultsFn[query.RefId]; has {
-			result.QueryResults[query.RefId] = testFunc(context)
+			result.Results[query.RefId] = testFunc(context)
 		}
 	}
 
-	return result
+	return result, nil
 }
 
 func (e *FakeExecutor) Return(refId string, series TimeSeriesSlice) {

+ 9 - 13
pkg/tsdb/graphite/graphite.go

@@ -37,8 +37,8 @@ func init() {
 	tsdb.RegisterTsdbQueryEndpoint("graphite", NewGraphiteExecutor)
 }
 
-func (e *GraphiteExecutor) Query(ctx context.Context, dsInfo *models.DataSource, tsdbQuery *tsdb.TsdbQuery) *tsdb.BatchResult {
-	result := &tsdb.BatchResult{}
+func (e *GraphiteExecutor) Query(ctx context.Context, dsInfo *models.DataSource, tsdbQuery *tsdb.TsdbQuery) (*tsdb.Response, error) {
+	result := &tsdb.Response{}
 
 	from := "-" + formatTimeRange(tsdbQuery.TimeRange.From)
 	until := formatTimeRange(tsdbQuery.TimeRange.To)
@@ -67,14 +67,12 @@ func (e *GraphiteExecutor) Query(ctx context.Context, dsInfo *models.DataSource,
 
 	req, err := e.createRequest(dsInfo, formData)
 	if err != nil {
-		result.Error = err
-		return result
+		return nil, err
 	}
 
 	httpClient, err := dsInfo.GetHttpClient()
 	if err != nil {
-		result.Error = err
-		return result
+		return nil, err
 	}
 
 	span, ctx := opentracing.StartSpanFromContext(ctx, "graphite query")
@@ -90,17 +88,15 @@ func (e *GraphiteExecutor) Query(ctx context.Context, dsInfo *models.DataSource,
 
 	res, err := ctxhttp.Do(ctx, httpClient, req)
 	if err != nil {
-		result.Error = err
-		return result
+		return nil, err
 	}
 
 	data, err := e.parseResponse(res)
 	if err != nil {
-		result.Error = err
-		return result
+		return nil, err
 	}
 
-	result.QueryResults = make(map[string]*tsdb.QueryResult)
+	result.Results = make(map[string]*tsdb.QueryResult)
 	queryRes := tsdb.NewQueryResult()
 
 	for _, series := range data {
@@ -114,8 +110,8 @@ func (e *GraphiteExecutor) Query(ctx context.Context, dsInfo *models.DataSource,
 		}
 	}
 
-	result.QueryResults["A"] = queryRes
-	return result
+	result.Results["A"] = queryRes
+	return result, nil
 }
 
 func (e *GraphiteExecutor) parseResponse(res *http.Response) ([]TargetResponseDTO, error) {

+ 13 - 13
pkg/tsdb/influxdb/influxdb.go

@@ -39,17 +39,17 @@ func init() {
 	tsdb.RegisterTsdbQueryEndpoint("influxdb", NewInfluxDBExecutor)
 }
 
-func (e *InfluxDBExecutor) Query(ctx context.Context, dsInfo *models.DataSource, tsdbQuery *tsdb.TsdbQuery) *tsdb.BatchResult {
-	result := &tsdb.BatchResult{}
+func (e *InfluxDBExecutor) Query(ctx context.Context, dsInfo *models.DataSource, tsdbQuery *tsdb.TsdbQuery) (*tsdb.Response, error) {
+	result := &tsdb.Response{}
 
 	query, err := e.getQuery(dsInfo, tsdbQuery.Queries, tsdbQuery)
 	if err != nil {
-		return result.WithError(err)
+		return nil, err
 	}
 
 	rawQuery, err := query.Build(tsdbQuery)
 	if err != nil {
-		return result.WithError(err)
+		return nil, err
 	}
 
 	if setting.Env == setting.DEV {
@@ -58,21 +58,21 @@ func (e *InfluxDBExecutor) Query(ctx context.Context, dsInfo *models.DataSource,
 
 	req, err := e.createRequest(dsInfo, rawQuery)
 	if err != nil {
-		return result.WithError(err)
+		return nil, err
 	}
 
 	httpClient, err := dsInfo.GetHttpClient()
 	if err != nil {
-		return result.WithError(err)
+		return nil, err
 	}
 
 	resp, err := ctxhttp.Do(ctx, httpClient, req)
 	if err != nil {
-		return result.WithError(err)
+		return nil, err
 	}
 
 	if resp.StatusCode/100 != 2 {
-		return result.WithError(fmt.Errorf("Influxdb returned statuscode invalid status code: %v", resp.Status))
+		return nil, fmt.Errorf("Influxdb returned statuscode invalid status code: %v", resp.Status)
 	}
 
 	var response Response
@@ -82,17 +82,17 @@ func (e *InfluxDBExecutor) Query(ctx context.Context, dsInfo *models.DataSource,
 	err = dec.Decode(&response)
 
 	if err != nil {
-		return result.WithError(err)
+		return nil, err
 	}
 
 	if response.Err != nil {
-		return result.WithError(response.Err)
+		return nil, response.Err
 	}
 
-	result.QueryResults = make(map[string]*tsdb.QueryResult)
-	result.QueryResults["A"] = e.ResponseParser.Parse(&response, query)
+	result.Results = make(map[string]*tsdb.QueryResult)
+	result.Results["A"] = e.ResponseParser.Parse(&response, query)
 
-	return result
+	return result, nil
 }
 
 func (e *InfluxDBExecutor) getQuery(dsInfo *models.DataSource, queries []*tsdb.Query, context *tsdb.TsdbQuery) (*Query, error) {

+ 2 - 18
pkg/tsdb/models.go

@@ -22,24 +22,8 @@ type Query struct {
 }
 
 type Response struct {
-	BatchTimings []*BatchTiming          `json:"timings"`
-	Results      map[string]*QueryResult `json:"results"`
-	Message      string                  `json:"message,omitempty"`
-}
-
-type BatchTiming struct {
-	TimeElapsed int64
-}
-
-type BatchResult struct {
-	Error        error
-	QueryResults map[string]*QueryResult
-	Timings      *BatchTiming
-}
-
-func (br *BatchResult) WithError(err error) *BatchResult {
-	br.Error = err
-	return br
+	Results map[string]*QueryResult `json:"results"`
+	Message string                  `json:"message,omitempty"`
 }
 
 type QueryResult struct {

+ 5 - 5
pkg/tsdb/mysql/mysql.go

@@ -85,9 +85,9 @@ func (e *MysqlExecutor) initEngine(dsInfo *models.DataSource) error {
 	return nil
 }
 
-func (e *MysqlExecutor) Query(ctx context.Context, dsInfo *models.DataSource, tsdbQuery *tsdb.TsdbQuery) *tsdb.BatchResult {
-	result := &tsdb.BatchResult{
-		QueryResults: make(map[string]*tsdb.QueryResult),
+func (e *MysqlExecutor) Query(ctx context.Context, dsInfo *models.DataSource, tsdbQuery *tsdb.TsdbQuery) (*tsdb.Response, error) {
+	result := &tsdb.Response{
+		Results: make(map[string]*tsdb.QueryResult),
 	}
 
 	macroEngine := NewMysqlMacroEngine(tsdbQuery.TimeRange)
@@ -102,7 +102,7 @@ func (e *MysqlExecutor) Query(ctx context.Context, dsInfo *models.DataSource, ts
 		}
 
 		queryResult := &tsdb.QueryResult{Meta: simplejson.New(), RefId: query.RefId}
-		result.QueryResults[query.RefId] = queryResult
+		result.Results[query.RefId] = queryResult
 
 		rawSql, err := macroEngine.Interpolate(rawSql)
 		if err != nil {
@@ -138,7 +138,7 @@ func (e *MysqlExecutor) Query(ctx context.Context, dsInfo *models.DataSource, ts
 		}
 	}
 
-	return result
+	return result, nil
 }
 
 func (e MysqlExecutor) TransformToTable(query *tsdb.Query, rows *core.Rows, result *tsdb.QueryResult) error {

+ 8 - 11
pkg/tsdb/opentsdb/opentsdb.go

@@ -50,8 +50,8 @@ func init() {
 	tsdb.RegisterTsdbQueryEndpoint("opentsdb", NewOpenTsdbExecutor)
 }
 
-func (e *OpenTsdbExecutor) Query(ctx context.Context, dsInfo *models.DataSource, queryContext *tsdb.TsdbQuery) *tsdb.BatchResult {
-	result := &tsdb.BatchResult{}
+func (e *OpenTsdbExecutor) Query(ctx context.Context, dsInfo *models.DataSource, queryContext *tsdb.TsdbQuery) (*tsdb.Response, error) {
+	result := &tsdb.Response{}
 
 	var tsdbQuery OpenTsdbQuery
 
@@ -69,29 +69,26 @@ func (e *OpenTsdbExecutor) Query(ctx context.Context, dsInfo *models.DataSource,
 
 	req, err := e.createRequest(dsInfo, tsdbQuery)
 	if err != nil {
-		result.Error = err
-		return result
+		return nil, err
 	}
 
 	httpClient, err := dsInfo.GetHttpClient()
 	if err != nil {
-		result.Error = err
-		return result
+		return nil, err
 	}
 
 	res, err := ctxhttp.Do(ctx, httpClient, req)
 	if err != nil {
-		result.Error = err
-		return result
+		return nil, err
 	}
 
 	queryResult, err := e.parseResponse(tsdbQuery, res)
 	if err != nil {
-		return result.WithError(err)
+		return nil, err
 	}
 
-	result.QueryResults = queryResult
-	return result
+	result.Results = queryResult
+	return result, nil
 }
 
 func (e *OpenTsdbExecutor) createRequest(dsInfo *models.DataSource, data OpenTsdbQuery) (*http.Request, error) {

+ 8 - 8
pkg/tsdb/prometheus/prometheus.go

@@ -80,17 +80,17 @@ func (e *PrometheusExecutor) getClient(dsInfo *models.DataSource) (apiv1.API, er
 	return apiv1.NewAPI(client), nil
 }
 
-func (e *PrometheusExecutor) Query(ctx context.Context, dsInfo *models.DataSource, tsdbQuery *tsdb.TsdbQuery) *tsdb.BatchResult {
-	result := &tsdb.BatchResult{}
+func (e *PrometheusExecutor) Query(ctx context.Context, dsInfo *models.DataSource, tsdbQuery *tsdb.TsdbQuery) (*tsdb.Response, error) {
+	result := &tsdb.Response{}
 
 	client, err := e.getClient(dsInfo)
 	if err != nil {
-		return result.WithError(err)
+		return nil, err
 	}
 
 	query, err := parseQuery(tsdbQuery.Queries, tsdbQuery)
 	if err != nil {
-		return result.WithError(err)
+		return nil, err
 	}
 
 	timeRange := apiv1.Range{
@@ -108,15 +108,15 @@ func (e *PrometheusExecutor) Query(ctx context.Context, dsInfo *models.DataSourc
 	value, err := client.QueryRange(ctx, query.Expr, timeRange)
 
 	if err != nil {
-		return result.WithError(err)
+		return nil, err
 	}
 
 	queryResult, err := parseResponse(value, query)
 	if err != nil {
-		return result.WithError(err)
+		return nil, err
 	}
-	result.QueryResults = queryResult
-	return result
+	result.Results = queryResult
+	return result, nil
 }
 
 func formatLegend(metric model.Metric, query *PrometheusQuery) string {

+ 1 - 1
pkg/tsdb/query_endpoint.go

@@ -8,7 +8,7 @@ import (
 )
 
 type TsdbQueryEndpoint interface {
-	Query(ctx context.Context, ds *models.DataSource, query *TsdbQuery) *BatchResult
+	Query(ctx context.Context, ds *models.DataSource, query *TsdbQuery) (*Response, error)
 }
 
 var registry map[string]GetTsdbQueryEndpointFn

+ 6 - 14
pkg/tsdb/request.go

@@ -2,25 +2,17 @@ package tsdb
 
 import (
 	"context"
+
+	"github.com/grafana/grafana/pkg/models"
 )
 
-type HandleRequestFunc func(ctx context.Context, req *TsdbQuery) (*Response, error)
+type HandleRequestFunc func(ctx context.Context, dsInfo *models.DataSource, req *TsdbQuery) (*Response, error)
 
-func HandleRequest(ctx context.Context, req *TsdbQuery) (*Response, error) {
-	//TODO niceify
-	ds := req.Queries[0].DataSource
-	endpoint, err := getTsdbQueryEndpointFor(ds)
+func HandleRequest(ctx context.Context, dsInfo *models.DataSource, req *TsdbQuery) (*Response, error) {
+	endpoint, err := getTsdbQueryEndpointFor(dsInfo)
 	if err != nil {
 		return nil, err
 	}
 
-	res := endpoint.Query(ctx, ds, req)
-	if res.Error != nil {
-		return nil, res.Error
-	}
-
-	return &Response{
-		Results:      res.QueryResults,
-		BatchTimings: []*BatchTiming{res.Timings},
-	}, nil
+	return endpoint.Query(ctx, dsInfo, req)
 }

+ 61 - 0
pkg/tsdb/testdata/scenarios.go

@@ -29,6 +29,67 @@ func init() {
 
 	logger.Debug("Initializing TestData Scenario")
 
+	registerScenario(&Scenario{
+		Id:   "exponential_heatmap_bucket_data",
+		Name: "Exponential heatmap bucket data",
+
+		Handler: func(query *tsdb.Query, context *tsdb.TsdbQuery) *tsdb.QueryResult {
+			to := context.TimeRange.GetToAsMsEpoch()
+
+			var series []*tsdb.TimeSeries
+			start := 1
+			factor := 2
+			for i := 0; i < 10; i++ {
+				timeWalkerMs := context.TimeRange.GetFromAsMsEpoch()
+				serie := &tsdb.TimeSeries{Name: strconv.Itoa(start)}
+				start *= factor
+
+				points := make(tsdb.TimeSeriesPoints, 0)
+				for j := int64(0); j < 100 && timeWalkerMs < to; j++ {
+					v := float64(rand.Int63n(100))
+					points = append(points, tsdb.NewTimePoint(null.FloatFrom(v), float64(timeWalkerMs)))
+					timeWalkerMs += query.IntervalMs * 50
+				}
+
+				serie.Points = points
+				series = append(series, serie)
+			}
+
+			queryRes := tsdb.NewQueryResult()
+			queryRes.Series = append(queryRes.Series, series...)
+			return queryRes
+		},
+	})
+
+	registerScenario(&Scenario{
+		Id:   "linear_heatmap_bucket_data",
+		Name: "Linear heatmap bucket data",
+
+		Handler: func(query *tsdb.Query, context *tsdb.TsdbQuery) *tsdb.QueryResult {
+			to := context.TimeRange.GetToAsMsEpoch()
+
+			var series []*tsdb.TimeSeries
+			for i := 0; i < 10; i++ {
+				timeWalkerMs := context.TimeRange.GetFromAsMsEpoch()
+				serie := &tsdb.TimeSeries{Name: strconv.Itoa(i * 10)}
+
+				points := make(tsdb.TimeSeriesPoints, 0)
+				for j := int64(0); j < 100 && timeWalkerMs < to; j++ {
+					v := float64(rand.Int63n(100))
+					points = append(points, tsdb.NewTimePoint(null.FloatFrom(v), float64(timeWalkerMs)))
+					timeWalkerMs += query.IntervalMs * 50
+				}
+
+				serie.Points = points
+				series = append(series, serie)
+			}
+
+			queryRes := tsdb.NewQueryResult()
+			queryRes.Series = append(queryRes.Series, series...)
+			return queryRes
+		},
+	})
+
 	registerScenario(&Scenario{
 		Id:   "random_walk",
 		Name: "Random Walk",

+ 6 - 6
pkg/tsdb/testdata/testdata.go

@@ -24,19 +24,19 @@ func init() {
 	tsdb.RegisterTsdbQueryEndpoint("grafana-testdata-datasource", NewTestDataExecutor)
 }
 
-func (e *TestDataExecutor) Query(ctx context.Context, dsInfo *models.DataSource, tsdbQuery *tsdb.TsdbQuery) *tsdb.BatchResult {
-	result := &tsdb.BatchResult{}
-	result.QueryResults = make(map[string]*tsdb.QueryResult)
+func (e *TestDataExecutor) Query(ctx context.Context, dsInfo *models.DataSource, tsdbQuery *tsdb.TsdbQuery) (*tsdb.Response, error) {
+	result := &tsdb.Response{}
+	result.Results = make(map[string]*tsdb.QueryResult)
 
 	for _, query := range tsdbQuery.Queries {
 		scenarioId := query.Model.Get("scenarioId").MustString("random_walk")
 		if scenario, exist := ScenarioRegistry[scenarioId]; exist {
-			result.QueryResults[query.RefId] = scenario.Handler(query, tsdbQuery)
-			result.QueryResults[query.RefId].RefId = query.RefId
+			result.Results[query.RefId] = scenario.Handler(query, tsdbQuery)
+			result.Results[query.RefId].RefId = query.RefId
 		} else {
 			e.log.Error("Scenario not found", "scenarioId", scenarioId)
 		}
 	}
 
-	return result
+	return result, nil
 }

+ 3 - 8
pkg/tsdb/tsdb_test.go

@@ -19,7 +19,7 @@ func TestMetricQuery(t *testing.T) {
 		fakeExecutor := registerFakeExecutor()
 		fakeExecutor.Return("A", TimeSeriesSlice{&TimeSeries{Name: "argh"}})
 
-		res, err := HandleRequest(context.TODO(), req)
+		res, err := HandleRequest(context.TODO(), &models.DataSource{Id: 1, Type: "test"}, req)
 		So(err, ShouldBeNil)
 
 		Convey("Should return query results", func() {
@@ -40,18 +40,13 @@ func TestMetricQuery(t *testing.T) {
 		fakeExecutor.Return("A", TimeSeriesSlice{&TimeSeries{Name: "argh"}})
 		fakeExecutor.Return("B", TimeSeriesSlice{&TimeSeries{Name: "barg"}})
 
-		res, err := HandleRequest(context.TODO(), req)
+		res, err := HandleRequest(context.TODO(), &models.DataSource{Id: 1, Type: "test"}, req)
 		So(err, ShouldBeNil)
 
 		Convey("Should return query results", func() {
 			So(len(res.Results), ShouldEqual, 2)
 			So(res.Results["B"].Series[0].Name, ShouldEqual, "barg")
 		})
-
-		Convey("Should have been batched in one request", func() {
-			So(len(res.BatchTimings), ShouldEqual, 1)
-		})
-
 	})
 
 	Convey("When query uses data source of unknown type", t, func() {
@@ -61,7 +56,7 @@ func TestMetricQuery(t *testing.T) {
 			},
 		}
 
-		_, err := HandleRequest(context.TODO(), req)
+		_, err := HandleRequest(context.TODO(), &models.DataSource{Id: 12, Type: "testjughjgjg"}, req)
 		So(err, ShouldNotBeNil)
 	})
 }

+ 2 - 0
public/app/app.ts

@@ -9,6 +9,8 @@ import 'angular-sanitize';
 import 'angular-dragdrop';
 import 'angular-bindonce';
 import 'angular-ui';
+import 'react';
+import 'react-dom';
 import 'ngreact';
 
 import $ from 'jquery';

+ 0 - 1
public/app/core/components/PasswordStrength.tsx

@@ -1,5 +1,4 @@
 import * as React from 'react';
-import 'react-dom';
 import coreModule from '../core_module';
 
 export interface IProps {

+ 0 - 1
public/app/core/core.ts

@@ -8,7 +8,6 @@ import "./directives/dropdown_typeahead";
 import "./directives/metric_segment";
 import "./directives/misc";
 import "./directives/ng_model_on_blur";
-import "./directives/password_strength";
 import "./directives/spectrum_picker";
 import "./directives/tags";
 import "./directives/value_select_dropdown";

+ 1 - 1
public/app/core/directives/misc.js

@@ -25,7 +25,7 @@ function (angular, require, coreModule, kbn) {
         getText: '&clipboardButton'
       },
       link: function(scope, elem) {
-        require(['vendor/clipboard/dist/clipboard'], function(Clipboard) {
+        require(['clipboard'], function(Clipboard) {
           scope.clipboard = new Clipboard(elem[0], {
             text: function() {
               return scope.getText();

+ 0 - 45
public/app/core/directives/password_strength.js

@@ -1,45 +0,0 @@
-define([
-  '../core_module',
-],
-function (coreModule) {
-  'use strict';
-
-  coreModule.default.directive('passwordStrength2', function() {
-    var template = '<div class="password-strength small" ng-if="!loginMode" ng-class="strengthClass">' +
-      '<em>{{strengthText}}</em>' +
-      '</div>';
-    return {
-      template: template,
-      scope: {
-        password: "=",
-      },
-      link: function($scope) {
-
-        $scope.strengthClass = '';
-
-        function passwordChanged(newValue) {
-          if (!newValue) {
-            $scope.strengthText = "";
-            $scope.strengthClass = "hidden";
-            return;
-          }
-          if (newValue.length < 4) {
-            $scope.strengthText = "strength: weak sauce.";
-            $scope.strengthClass = "password-strength-bad";
-            return;
-          }
-          if (newValue.length <= 8) {
-            $scope.strengthText = "strength: you can do better.";
-            $scope.strengthClass = "password-strength-ok";
-            return;
-          }
-
-          $scope.strengthText = "strength: strong like a bull.";
-          $scope.strengthClass = "password-strength-good";
-        }
-
-        $scope.$watch("password", passwordChanged);
-      }
-    };
-  });
-});

+ 2 - 0
public/app/features/alerting/alert_def.ts

@@ -106,6 +106,8 @@ function getStateDisplayModel(state) {
       };
     }
   }
+
+  throw {message: 'Unknown alert state'};
 }
 
 function joinEvalMatches(matches, separator: string) {

+ 2 - 1
public/app/features/alerting/alert_tab_ctrl.ts

@@ -84,7 +84,7 @@ export class AlertTabCtrl {
     });
   }
 
-  getNotificationIcon(type) {
+  getNotificationIcon(type): string {
     switch (type) {
       case "email": return "fa fa-envelope";
       case "slack": return "fa fa-slack";
@@ -95,6 +95,7 @@ export class AlertTabCtrl {
       case "hipchat": return "fa fa-mail-forward";
       case "pushover": return "fa fa-mobile";
     }
+    return 'fa fa-bell';
   }
 
   getNotifications() {

+ 1 - 1
public/app/features/dashboard/export/export_modal.html

@@ -11,7 +11,7 @@
 	<div>
 		<p class="share-modal-info-text">
 			Export the dashboard to a JSON file. The exporter will templatize the
-			dashboard's data sources to make it easy for other's to to import and reuse.
+			dashboard's data sources to make it easy for others to import and reuse.
 			You can share dashboards on <a class="external-link" href="https://grafana.com">Grafana.com</a>
 		</p>
 

+ 2 - 1
public/app/features/panel/query_troubleshooter.ts

@@ -69,10 +69,11 @@ export class QueryTroubleshooterCtrl {
     }
   }
 
-  getClipboardText() {
+  getClipboardText(): string {
     if (this.jsonExplorer) {
       return JSON.stringify(this.jsonExplorer.json, null, 2);
     }
+    return '';
   }
 
   onRequestResponse(data) {

+ 2 - 0
public/app/features/plugins/import_list/import_list.ts

@@ -38,6 +38,8 @@ export class DashImportListCtrl {
             });
           }, 500);
         });
+      } else {
+        return Promise.resolve();
       }
     });
   }

+ 1 - 0
public/app/features/plugins/plugin_edit_ctrl.ts

@@ -80,6 +80,7 @@ export class PluginEditCtrl {
       case 'app':  return 'icon-gf icon-gf-apps';
       case 'page':  return 'icon-gf icon-gf-endpoint-tiny';
       case 'dashboard':  return 'icon-gf icon-gf-dashboard';
+      default: return 'icon-gf icon-gf-apps';
     }
   }
 

+ 1 - 1
public/app/features/templating/editor_ctrl.ts

@@ -54,7 +54,7 @@ export class VariableEditorCtrl {
 
     $scope.isValid = function() {
       if (!$scope.ctrl.form.$valid) {
-        return;
+        return false;
       }
 
       if (!$scope.current.name.match(/^\w+$/)) {

+ 2 - 1
public/app/plugins/datasource/influxdb/query_ctrl.ts

@@ -352,12 +352,13 @@ export class InfluxQueryCtrl extends QueryCtrl {
     this.panelCtrl.refresh();
   }
 
-  getTagValueOperator(tagValue, tagOperator) {
+  getTagValueOperator(tagValue, tagOperator): string {
     if (tagOperator !== '=~' && tagOperator !== '!~' && /^\/.*\/$/.test(tagValue)) {
       return '=~';
     } else if ((tagOperator === '=~' || tagOperator === '!~') && /^(?!\/.*\/$)/.test(tagValue)) {
       return '=';
     }
+    return null;
   }
 
   getCollapsedText() {

+ 26 - 2
public/app/plugins/datasource/prometheus/datasource.ts

@@ -99,6 +99,7 @@ export class PrometheusDatasource {
       var query: any = {};
       query.expr = this.templateSrv.replace(target.expr, options.scopedVars, self.interpolateQueryExpr);
       query.requestId = options.panelId + target.refId;
+      query.instant = target.instant;
 
       var interval = this.templateSrv.replace(target.interval, options.scopedVars) || options.interval;
       var intervalFactor = target.intervalFactor || 1;
@@ -114,7 +115,11 @@ export class PrometheusDatasource {
     }
 
     var allQueryPromise = _.map(queries, query => {
-      return this.performTimeSeriesQuery(query, start, end);
+      if (!query.instant) {
+        return this.performTimeSeriesQuery(query, start, end);
+      } else {
+        return this.performInstantQuery(query, end);
+      }
     });
 
     return this.$q.all(allQueryPromise).then(responseList => {
@@ -129,7 +134,11 @@ export class PrometheusDatasource {
           result.push(self.transformMetricDataToTable(response.data.data.result));
         } else {
           for (let metricData of response.data.data.result) {
-            result.push(self.transformMetricData(metricData, activeTargets[index], start, end));
+            if (response.data.data.resultType === 'matrix') {
+              result.push(self.transformMetricData(metricData, activeTargets[index], start, end));
+            } else if (response.data.data.resultType === 'vector') {
+              result.push(self.transformInstantMetricData(metricData, activeTargets[index]));
+            }
           }
         }
       });
@@ -156,6 +165,11 @@ export class PrometheusDatasource {
     return this._request('GET', url, query.requestId);
   }
 
+  performInstantQuery(query, time) {
+    var url = '/api/v1/query?query=' + encodeURIComponent(query.expr) + '&time=' + time;
+    return this._request('GET', url, query.requestId);
+  }
+
   performSuggestQuery(query, cache = false) {
     var url = '/api/v1/label/__name__/values';
 
@@ -317,6 +331,9 @@ export class PrometheusDatasource {
 
     // Populate rows, set value to empty string when label not present.
     _.each(md, function(series) {
+      if (series.value) {
+        series.values = [series.value];
+      }
       if (series.values) {
         for (i = 0; i < series.values.length; i++) {
           var values = series.values[i];
@@ -340,6 +357,13 @@ export class PrometheusDatasource {
     return table;
   }
 
+  transformInstantMetricData(md, options) {
+    var dps = [], metricLabel = null;
+    metricLabel = this.createMetricLabel(md.metric, options);
+    dps.push([parseFloat(md.value[1]), md.value[0] * 1000]);
+    return { target: metricLabel, datapoints: dps };
+  }
+
   createMetricLabel(labelData, options) {
     if (_.isUndefined(options) || _.isEmpty(options.legendFormat)) {
       return this.getOriginalMetricName(labelData);

+ 1 - 3
public/app/plugins/datasource/prometheus/metric_find_query.js

@@ -95,9 +95,7 @@ function (_) {
 
   PrometheusMetricFindQuery.prototype.queryResultQuery = function(query) {
     var end = this.datasource.getPrometheusTime(this.range.to, true);
-    var url = '/api/v1/query?query=' + encodeURIComponent(query) + '&time=' + end;
-
-    return this.datasource._request('GET', url)
+    return this.datasource.performInstantQuery({ expr: query }, end)
     .then(function(result) {
       return _.map(result.data.data.result, function(metricData) {
         var text = metricData.metric.__name__ || '';

+ 2 - 0
public/app/plugins/datasource/prometheus/partials/query.editor.html

@@ -45,6 +45,8 @@
 			<div class="gf-form-select-wrapper width-8">
 				<select class="gf-form-input gf-size-auto" ng-model="ctrl.target.format" ng-options="f.value as f.text for f in ctrl.formats" ng-change="ctrl.refresh()"></select>
 			</div>
+			<gf-form-switch class="gf-form" label="Instant" label-class="width-5" checked="ctrl.target.instant" on-change="ctrl.refresh()">
+			</gf-form-switch>
 			<label class="gf-form-label">
 				<a href="{{ctrl.linkToPrometheus}}" target="_blank" bs-tooltip="'Link to Graph in Prometheus'">
 					<i class="fa fa-share-square-o"></i>

+ 3 - 0
public/app/plugins/datasource/prometheus/query_ctrl.ts

@@ -12,6 +12,7 @@ class PrometheusQueryCtrl extends QueryCtrl {
   metric: any;
   resolutions: any;
   formats: any;
+  instant: any;
   oldTarget: any;
   suggestMetrics: any;
   getMetricsAutocomplete: any;
@@ -36,6 +37,8 @@ class PrometheusQueryCtrl extends QueryCtrl {
       {text: 'Table', value: 'table'},
     ];
 
+    this.instant = false;
+
     this.updateLink();
   }
 

+ 77 - 3
public/app/plugins/datasource/prometheus/specs/datasource_specs.ts

@@ -26,7 +26,7 @@ describe('PrometheusDatasource', function() {
                       '&start=1443438675&end=1443460275&step=60';
     var query = {
       range: { from: moment(1443438674760), to: moment(1443460274760) },
-      targets: [{ expr: 'test{job="testjob"}' }],
+      targets: [{ expr: 'test{job="testjob"}', format: 'time_series' }],
       interval: '60s'
     };
     var response = {
@@ -62,7 +62,7 @@ describe('PrometheusDatasource', function() {
                       '&start=' + start + '&end=' + end + '&step=' + step;
     var query = {
       range: { from: moment(1443438674760), to: moment(1443460274760) },
-      targets: [{ expr: 'test{job="testjob"}' }],
+      targets: [{ expr: 'test{job="testjob"}', format: 'time_series' }],
       interval: '60s'
     };
     var response = {
@@ -119,7 +119,40 @@ describe('PrometheusDatasource', function() {
       expect(results.data[1].datapoints[3][0]).to.be(null);
     });
   });
-  describe('When performing annotationQuery', function() {
+  describe('When querying prometheus with one target and instant = true', function () {
+    var results;
+    var urlExpected = 'proxied/api/v1/query?query=' +
+      encodeURIComponent('test{job="testjob"}') +
+      '&time=1443460275';
+    var query = {
+      range: { from: moment(1443438674760), to: moment(1443460274760) },
+      targets: [{ expr: 'test{job="testjob"}', format: 'time_series', instant: true }],
+      interval: '60s'
+    };
+    var response = {
+      status: "success",
+      data: {
+        resultType: "vector",
+        result: [{
+          metric: { "__name__": "test", job: "testjob" },
+          value: [1443454528, "3846"]
+        }]
+      }
+    };
+    beforeEach(function () {
+      ctx.$httpBackend.expect('GET', urlExpected).respond(response);
+      ctx.ds.query(query).then(function (data) { results = data; });
+      ctx.$httpBackend.flush();
+    });
+    it('should generate the correct query', function () {
+      ctx.$httpBackend.verifyNoOutstandingExpectation();
+    });
+    it('should return series list', function () {
+      expect(results.data.length).to.be(1);
+      expect(results.data[0].target).to.be('test{job="testjob"}');
+    });
+  });
+  describe('When performing annotationQuery', function () {
     var results;
     var urlExpected = 'proxied/api/v1/query_range?query=' +
                       encodeURIComponent('ALERTS{alertstate="firing"}') +
@@ -195,4 +228,45 @@ describe('PrometheusDatasource', function() {
       );
     });
   });
+  describe('When resultFormat is table and instant = true', function() {
+    var results;
+    var urlExpected = 'proxied/api/v1/query?query=' +
+      encodeURIComponent('test{job="testjob"}') +
+      '&time=1443460275';
+    var query = {
+      range: { from: moment(1443438674760), to: moment(1443460274760) },
+      targets: [{ expr: 'test{job="testjob"}', format: 'time_series', instant: true }],
+      interval: '60s'
+    };
+    var response = {
+      status: "success",
+      data: {
+        resultType: "vector",
+        result: [{
+          metric: { "__name__": "test", job: "testjob" },
+          value: [1443454528, "3846"]
+        }]
+      }
+    };
+    beforeEach(function () {
+      ctx.$httpBackend.expect('GET', urlExpected).respond(response);
+      ctx.ds.query(query).then(function (data) { results = data; });
+      ctx.$httpBackend.flush();
+    });
+    it('should return table model', function() {
+      var table = ctx.ds.transformMetricDataToTable(response.data.result);
+      expect(table.type).to.be('table');
+      expect(table.rows).to.eql(
+        [
+          [ 1443454528000, 'test', 'testjob', 3846]
+        ]);
+      expect(table.columns).to.eql(
+        [ { text: 'Time', type: 'time' },
+          { text: '__name__' },
+          { text: 'job' },
+          { text: 'Value' }
+        ]
+      );
+    });
+  });
 });

+ 17 - 15
public/app/plugins/panel/graph/data_processor.ts

@@ -142,34 +142,34 @@ export class DataProcessor {
     let fields = [];
     var firstItem = dataList[0];
     let fieldParts = [];
+
     function getPropertiesRecursive(obj) {
-        _.forEach(obj, (value, key) => {
-          if (_.isObject(value)) {
-            fieldParts.push(key);
-            getPropertiesRecursive(value);
-          } else {
-            if (!onlyNumbers || _.isNumber(value)) {
-              let field = fieldParts.concat(key).join('.');
-              fields.push(field);
-            }
+      _.forEach(obj, (value, key) => {
+        if (_.isObject(value)) {
+          fieldParts.push(key);
+          getPropertiesRecursive(value);
+        } else {
+          if (!onlyNumbers || _.isNumber(value)) {
+            let field = fieldParts.concat(key).join('.');
+            fields.push(field);
           }
-        });
-        fieldParts.pop();
+        }
+      });
+      fieldParts.pop();
     }
+
     if (firstItem.type === 'docs') {
       if (firstItem.datapoints.length === 0) {
         return [];
       }
       getPropertiesRecursive(firstItem.datapoints[0]);
-      return fields;
     }
+
+    return fields;
   }
 
   getXAxisValueOptions(options) {
     switch (this.panel.xaxis.mode) {
-      case 'time': {
-        return [];
-      }
       case 'series': {
         return [
           {text: 'Avg', value: 'avg'},
@@ -180,6 +180,8 @@ export class DataProcessor {
         ];
       }
     }
+
+    return [];
   }
 
   pluckDeep(obj: any, property: string) {

+ 3 - 0
public/app/plugins/panel/graph/graph.ts

@@ -120,6 +120,8 @@ coreModule.directive('grafanaGraph', function($rootScope, timeSrv, popoverSrv) {
         if (panelWidth === 0) {
           return true;
         }
+
+        return false;
       }
 
       function drawHook(plot) {
@@ -385,6 +387,7 @@ coreModule.directive('grafanaGraph', function($rootScope, timeSrv, popoverSrv) {
         if (legendSideLastValue !== null && panel.legend.rightSide !== legendSideLastValue) {
           return true;
         }
+        return false;
       }
 
       function addTimeAxis(options) {

+ 28 - 24
public/app/plugins/panel/heatmap/heatmap_data_converter.ts

@@ -21,7 +21,7 @@ function elasticHistogramToHeatmap(seriesList) {
   for (let series of seriesList) {
     let bound = Number(series.alias);
     if (isNaN(bound)) {
-      return;
+      return heatmap;
     }
 
     for (let point of series.datapoints) {
@@ -384,36 +384,40 @@ function isHeatmapDataEqual(objA: any, objB: any): boolean {
   let is_eql = !emptyXOR(objA, objB);
 
   _.forEach(objA, (xBucket: XBucket, x) => {
-      if (objB[x]) {
+    if (objB[x]) {
       if (emptyXOR(xBucket.buckets, objB[x].buckets)) {
-      is_eql = false;
-      return false;
+        is_eql = false;
+        return false;
       }
 
       _.forEach(xBucket.buckets, (yBucket: YBucket, y) => {
-          if (objB[x].buckets && objB[x].buckets[y]) {
+        if (objB[x].buckets && objB[x].buckets[y]) {
           if (objB[x].buckets[y].values) {
-          is_eql = _.isEqual(_.sortBy(yBucket.values), _.sortBy(objB[x].buckets[y].values));
-          if (!is_eql) {
-          return false;
-          }
+            is_eql = _.isEqual(_.sortBy(yBucket.values), _.sortBy(objB[x].buckets[y].values));
+            if (!is_eql) {
+              return false;
+            } else {
+              return true;
+            }
           } else {
-          is_eql = false;
-          return false;
+            is_eql = false;
+            return false;
           }
-          } else {
+        } else {
           is_eql = false;
           return false;
-          }
-          });
+        }
+      });
 
       if (!is_eql) {
         return false;
-      }
       } else {
-        is_eql = false;
-        return false;
+        return true;
       }
+    } else {
+      is_eql = false;
+      return false;
+    }
   });
 
   return is_eql;
@@ -425,11 +429,11 @@ function emptyXOR(foo: any, bar: any): boolean {
 
 export {
   convertToHeatMap,
-    elasticHistogramToHeatmap,
-    convertToCards,
-    mergeZeroBuckets,
-    getMinLog,
-    getValueBucketBound,
-    isHeatmapDataEqual,
-    calculateBucketSize
+  elasticHistogramToHeatmap,
+  convertToCards,
+  mergeZeroBuckets,
+  getMinLog,
+  getValueBucketBound,
+  isHeatmapDataEqual,
+  calculateBucketSize
 };

+ 1 - 0
public/app/plugins/panel/table/transformers.ts

@@ -137,6 +137,7 @@ transformers['table'] = {
     if (!data || data.length === 0) {
       return [];
     }
+    return data[0].columns;
   },
   transform: function(data, panel, model) {
     if (!data || data.length === 0) {

+ 7 - 6
public/app/system.conf.js

@@ -15,14 +15,14 @@ System.config({
     "jquery": "vendor/npm/jquery/dist/jquery.js",
     'lodash-src': 'vendor/npm/lodash/lodash.js',
     "lodash": 'app/core/lodash_extended.js',
-    "angular": "vendor/angular/angular.js",
+    "angular": "vendor/npm/angular/angular.js",
     "bootstrap": "vendor/bootstrap/bootstrap.js",
-    'angular-route':          'vendor/angular-route/angular-route.js',
-    'angular-sanitize':       'vendor/angular-sanitize/angular-sanitize.js',
+    'angular-route':          'vendor/npm/angular-route/angular-route.js',
+    'angular-sanitize':       'vendor/npm/angular-sanitize/angular-sanitize.js',
     "angular-ui":             "vendor/angular-ui/ui-bootstrap-tpls.js",
     "angular-strap":          "vendor/angular-other/angular-strap.js",
-    "angular-dragdrop":       "vendor/angular-native-dragdrop/draganddrop.js",
-    "angular-bindonce":       "vendor/angular-bindonce/bindonce.js",
+    "angular-dragdrop":       "vendor/npm/angular-native-dragdrop/draganddrop.js",
+    "angular-bindonce":       "vendor/npm/angular-bindonce/bindonce.js",
     "spectrum": "vendor/spectrum.js",
     "bootstrap-tagsinput": "vendor/tagsinput/bootstrap-tagsinput.js",
     "jquery.flot": "vendor/flot/jquery.flot",
@@ -40,6 +40,7 @@ System.config({
     "gridstack": "vendor/npm/gridstack/dist/gridstack.js",
     "gridstack.jquery-ui": "vendor/npm/gridstack/dist/gridstack.jQueryUI.js",
     "ace": "vendor/npm/ace-builds/src-noconflict/ace"
+    "clipboard": "vendor/npm/clipboard/dist/clipboard.js"
   },
 
   packages: {
@@ -75,7 +76,7 @@ System.config({
       format: 'global',
       deps: ['gridstack'],
     },
-    'vendor/angular/angular.js': {
+    'vendor/npm/angular/angular.js': {
       format: 'global',
       deps: ['jquery'],
       exports: 'angular',

+ 33 - 1
public/sass/components/_navbar.scss

@@ -64,6 +64,38 @@
 
 .navbar-page-btn-wrapper {
   float: left;
+<<<<<<< HEAD
+||||||| merged common ancestors
+  margin: 0;
+  border-right: 1px solid $tight-form-border;
+  background-color: $navbarButtonBackground;
+  padding: 0.4rem 1.0rem 0.4rem 1rem;
+  min-height:: $navbarHeight;
+
+  .fa-caret-down {
+    font-size: 70%;
+  }
+
+  .fa-chevron-left{
+    display: none;
+  }
+
+=======
+  margin: 0;
+  border-right: 1px solid $tight-form-border;
+  background-color: $navbarButtonBackground;
+  padding: 0.4rem 1.0rem 0.4rem 1rem;
+  min-height: $navbarHeight;
+
+  .fa-caret-down {
+    font-size: 70%;
+  }
+
+  .fa-chevron-left{
+    display: none;
+  }
+
+>>>>>>> 0e5e2f3fb990293632402b20107aa09999c3f844
   &:hover {
     background: $navbarButtonBackgroundHighlight;
   }
@@ -78,7 +110,7 @@
   color: darken($link-color, 5%);
   font-size: $font-size-lg;
   padding: 1rem 1rem 0.75rem 1rem;
-  min-height:: $navbarHeight;
+  min-height: $navbarHeight;
 
   .fa-caret-down {
     font-size: 60%;

+ 14 - 7
public/test/test-main.js

@@ -23,15 +23,15 @@
       "jquery": "vendor/npm/jquery/dist/jquery.js",
       'lodash-src': 'vendor/npm/lodash/lodash.js',
       "lodash": 'app/core/lodash_extended.js',
-      "angular": 'vendor/angular/angular.js',
-      'angular-mocks': 'vendor/angular-mocks/angular-mocks.js',
+      "angular": 'vendor/npm/angular/angular.js',
+      'angular-mocks': 'vendor/npm/angular-mocks/angular-mocks.js',
       "bootstrap":  "vendor/bootstrap/bootstrap.js",
-      'angular-route':          'vendor/angular-route/angular-route.js',
-      'angular-sanitize':       'vendor/angular-sanitize/angular-sanitize.js',
+      'angular-route':          'vendor/npm/angular-route/angular-route.js',
+      'angular-sanitize':       'vendor/npm/angular-sanitize/angular-sanitize.js',
       "angular-ui":             "vendor/angular-ui/ui-bootstrap-tpls.js",
       "angular-strap":          "vendor/angular-other/angular-strap.js",
-      "angular-dragdrop":       "vendor/angular-native-dragdrop/draganddrop.js",
-      "angular-bindonce":       "vendor/angular-bindonce/bindonce.js",
+      "angular-dragdrop":       "vendor/npm/angular-native-dragdrop/draganddrop.js",
+      "angular-bindonce":       "vendor/npm/angular-bindonce/bindonce.js",
       "spectrum": "vendor/spectrum.js",
       "bootstrap-tagsinput": "vendor/tagsinput/bootstrap-tagsinput.js",
       "jquery.flot": "vendor/flot/jquery.flot",
@@ -49,6 +49,7 @@
       "gridstack": "vendor/npm/gridstack/dist/gridstack.js",
       "gridstack.jquery-ui": "vendor/npm/gridstack/dist/gridstack.jQueryUI.js",
       "ace": "vendor/npm/ace-builds/src-noconflict/ace",
+      "clipboard": "vendor/npm/clipboard/dist/clipboard.js"
     },
 
     packages: {
@@ -64,6 +65,7 @@
     },
 
     meta: {
+<<<<<<< HEAD
       'vendor/npm/jquery-ui/jquery-ui.js': {
         format: 'amd',
         deps: ['jquery'],
@@ -77,11 +79,16 @@
         deps: ['gridstack'],
       },
       'vendor/angular/angular.js': {
+||||||| merged common ancestors
+      'vendor/angular/angular.js': {
+=======
+      'vendor/npm/angular/angular.js': {
+>>>>>>> 0e5e2f3fb990293632402b20107aa09999c3f844
         format: 'global',
         deps: ['jquery'],
         exports: 'angular',
       },
-      'vendor/angular-mocks/angular-mocks.js': {
+      'vendor/npm/angular-mocks/angular-mocks.js': {
         format: 'global',
         deps: ['angular'],
       },

+ 0 - 36
public/vendor/angular-bindonce/.bower.json

@@ -1,36 +0,0 @@
-{
-  "name": "angular-bindonce",
-  "version": "0.3.3",
-  "main": "bindonce.js",
-  "description": "Zero watchers binding directives for AngularJS",
-  "homepage": "https://github.com/Pasvaz/bindonce",
-  "author": "Pasquale Vazzana <pasqualevazzana@gmail.com>",
-  "repository": {
-    "type": "git",
-    "url": "https://github.com/Pasvaz/bindonce.git"
-  },
-  "license": "MIT",
-  "ignore": [
-    "**/.*",
-    "node_modules",
-    "components"
-  ],
-  "dependencies": {},
-  "keywords": [
-    "angularjs",
-    "angular",
-    "directive",
-    "binding",
-    "watcher",
-    "bindonce"
-  ],
-  "_release": "0.3.3",
-  "_resolution": {
-    "type": "version",
-    "tag": "0.3.3",
-    "commit": "0fcf71e6effc88179893c9c06baf6c6bf9037632"
-  },
-  "_source": "https://github.com/Pasvaz/bindonce.git",
-  "_target": "0.3.3",
-  "_originalSource": "angular-bindonce"
-}

+ 0 - 43
public/vendor/angular-bindonce/CHANGELOG.md

@@ -1,43 +0,0 @@
-# 0.3.3 (2014-02-12)
-### Features
-- **bo-disabled:**
-  - Add support for ng-disabled/bo-disabled #110
-
-<hr />
-# 0.3.2 (2014-11-23)
-### Bug Fixes
-- **Angular 1.3 compatibility**
-
-<hr />
-# 0.3.1 (2014-02-12)
-### Features
-- **bo-bind:**
-  - alias for bo-text
-
-### Bug Fixes
-- **Angular Promises**
-	- Ensures that promises are resolved before to run binders ([b3ef1b4](https://github.com/Pasvaz/bindonce/commit/b3ef1b46edfe83f10ed455d5520027f731563f32))
-
-### Minor improvements
-- Updated Readme
-
-<hr />
-# 0.3.0 (2014-01-21)
-### Features
-- **bo-switch:**
-  - Create new directive: bo-switch ([652d0db](https://github.com/Pasvaz/bindonce/commit/652d0db04325166a180377c738a376543b5f2357))
-
-<hr />
-# 0.2.3 (2014-01-20)
-### Bug Fixes
-
-- **bo-if:**
-	- Ensures that we both process newly added binders from bo-if, and that
-we only process each binder once ([d11f863](https://github.com/Pasvaz/bindonce/commit/e091c273bbd17603d410fecc363874f0d1e6f38e))
-
-### Features
-
-- **Minification:**
-  - add min file ([47277ee](https://github.com/Pasvaz/bindonce/commit/47277eedd092b3210de362c725a7dadcddac8e87))
-- **Changelog:**
-  - Created a changelog file

+ 0 - 154
public/vendor/angular-bindonce/README.md

@@ -1,154 +0,0 @@
-Bindonce
-========
-
-High performance binding for AngularJs
-
-## Usage
-* download, clone or fork it or install it using [bower](http://twitter.github.com/bower/) `bower install angular-bindonce`
-* Include the `bindonce.js` script provided by this component into your app.
-* Add `'pasvaz.bindonce'` as a module dependency to your app: `angular.module('app', ['pasvaz.bindonce'])`
-
-## Demo
-Here is an example of how AngularJs can [freeze your UI](http://plnkr.co/edit/jwrHVb?p=preview), try to press and hold a key inside the input field, when the table is filled with only 1 person everything is ok, you can see how the DOM is updated by the input in real time, however if you try to load 1000 person *(or even 500 if the testing device is not powerfull)* and repeat the experiment you can see how the UI is frozen. In [this other demo](http://plnkr.co/edit/0DGOrk?p=preview) BindOnce will take care of your watchers and the UI will be reactive as it should be. The code is the same for both demos, the only difference is that I replaced any `ng-*` tag inside the table with the equivalent `bo-*` tag.
-* [AngularJs regular Demo](http://plnkr.co/edit/jwrHVb?p=preview)
-* [Demo with Bindonce](http://plnkr.co/edit/0DGOrk?p=preview)
-
-## Overview
-AngularJs provides a great data binding system but if you abuse of it the page can run into some performance issues, it's known that more of 2000 watchers can lag the UI and that amount can be reached easily if you don't pay attention to the data-binding. Sometime you really need to bind your data using watchers, especially for SPA because the data are updated in real time, but often you can avoid it with some efforts, most of the data presented in your page, once rendered, are immutable so you shouldn't keep watching them for changes.
-
-For instance, take a look to this snippet:
-```html
-<ul>
-	<li ng-repeat="person in Persons">
-		<a ng-href="#/people/{{person.id}}"><img ng-src="{{person.imageUrl}}"></a>
-		<a ng-href="#/people/{{person.id}}"><span ng-bind="person.name"></span></a>
-		<p ng-class="{'cycled':person.generated}" ng-bind-html-unsafe="person.description"></p>
-	</li>
-</ul>
-```
-Angular internally creates a `$watch` for each `ng-*` directive in order to keep the data up to date, so in this example just for displaying few info it creates 6 + 1 *(ngRepeatWatch)* watchers per `person`, even if the `person` is supposed to remain the same once shown. Iterate this amount for each person and you can have an idea about how easy is to reach 2000 watchers. Now if you need it because those data could change while you show the page or are bound to some models, it's ok. But most of the time they are static data that don't change once rendered. This is where **bindonce** can really help you.
-
-The above example done with **bindonce**:
-```html
-<ul>
-	<li bindonce ng-repeat="person in Persons">
-		<a bo-href="'#/people/' + person.id"><img bo-src="person.imageUrl"></a>
-		<a bo-href="'#/people/' + person.id" bo-text="person.name"></a>
-		<p bo-class="{'cycled':person.generated}" bo-html="person.description"></p>
-	</li>
-</ul>
-```
-Now this example uses **0 watches** per `person` and renders exactly the same result as the above that uses ng-*. *(Angular still uses 1 watcher for ngRepeatWatch)*
-
-### The smart approach
-OK until here nothing completely new, with a bit of efforts you could create your own directive and render the `person` inside the `link` function, or you could use [watch fighters](https://github.com/abourget/abourget-angular) that has a similar approach, but there is still one problem that you have to face and **bindonce** already handles it: *the existence of the data when the directive renders the content*. Usually the directives, unless you use watchers or bind their attributes to the scope (still a watcher), render the content when they are loaded into the markup, but if at that given time your data is not available, the directive can't render it. Bindonce can wait until the data is ready before to rendering the content.
-Let's take a look at the follow snippet to better understand the concept:
-```html
-<span my-custom-set-text="Person.firstname"></span>
-<span my-custom-set-text="Person.lastname"></span>
-...
-<script>
-angular.module('testApp', [])
-.directive('myCustomSetText', function () {
-	return {
-		link:function (scope, elem, attr, ctrl) {
-			elem.text(scope.$eval(attr.myCustomSetText));
-		}
-	}
-});
-</script>
-```
-This basic directive works as expected, it renders the `Person` data without using any watchers. However, if `Person` is not yet available inside the $scope when the page is loaded (say we get `Person` via $http or via $resource), the directive is useless, `scope.$eval(attr.myCustomSetText)` simply renders nothing and exits.
-
-Here is how we can solve this issue with **bindonce**:
-```html
-<div bindonce="Person" bo-title="Person.title">
-	<span bo-text="Person.firstname"></span>
-	<span bo-text="Person.lastname"></span>
-	<img bo-src="Person.picture" bo-alt="Person.title">
-	<p bo-class="{'fancy':Person.isNice}" bo-html="Person.story"></p>
-</div>
-```
-`bindonce="Person"` does the trick, any `bo-*` attribute belonging to `bindonce` waits until the parent `bindonce="{somedata}"` is validated and then renders its content. Once the scope contains the value `Person` then each bo-* child gets filled with the proper values. In order to accomplish this task, **bindonce** uses just **one** temporary watcher, no matters how many children need to be rendered. As soon as it gets `Person` the watcher is promptly removed. If the $scope already contains the data `bindonce` is looking for, then it doesn't create the temporary watcher and simply starts rendering its children.
-
-You may have noticed that the first example didn't assign any value to the `bindonce` attribute:
-```html
-<ul>
-	<li bindonce ng-repeat="person in Persons">
-	...
-```
-when used with `ng-repeat` `bindonce` doesn't need to check if `person` is defined because `ng-repeat` creates the directives only when `person` exists. You could be more explicit: `<li bindonce="person" ng-repeat="person in Persons">`, however assigning a value to `bindonce` in an `ng-repeat` won't make any difference.
-
-### Interpolation
-Some directives (ng-href, ng-src) use interpolation, ie: `ng-href="/profile/{{User.profileId}}"`.
-Both `ng-href` and `ng-src` have the bo-* equivalent directives: `bo-href-i` and `bo-src-i` (pay attention to the **-i**, it stands for **interpolate**). As expected they don't use watchers however Angular creates one watcher per  interpolation, for instance `bo-href-i="/profile/{{User.profileId}}"` sets the element's href **once**, as expected, but Angular keeps a watcher active on `{{User.profileId}}` even if `bo-href-i` doesn't use it.
-That's why by default the `bo-href` doesn't use interpolation or watchers. The above equivalent with 0 watchers would be `bo-href="'/profile/' + User.profileId"`. Nevertheless, `bo-href-i` and `bo-src-i` are still maintained for compatibility reasons.
-
-### Filters
-Almost every `bo-*` directive replace the equivalent `ng-*` and works in the same ways, except it is evaluated once. 
-Consequentially you can use any valid angular expression, including filters. This is an example how to use a filter:
-```html
-<div bindonce="Person">
-	<span bo-bind="Person.bill | currency:'USD$'"></span>
-</div>
-```
-
-## Attribute Usage
-| Directive  | 	Description | 	Example  |
-|------------|----------------|-----|
-| `bindonce="{somedata}"`| **bindonce** is the main directive. `{somedata}` is optional, and if present, forces bindonce to wait until `somedata` is defined before rendering its children  | `<div bindonce="Person">...<div>` |
-| `bo-if = "condition"`     | equivalent to `ng-if` but doesn't use watchers |`<ANY bo-if="Person.isPublic"></ANY>`|
-| `bo-switch = "expression"`     | equivalent to `ng-switch` but doesn't use watchers |`<div bo-switch="Person.isPublic">` `<span bo-switch-when="'yes">public</span>` `<span bo-switch-default>private</span>` `</div>`|
-| `bo-show = "condition"`     | equivalent to `ng-show` but doesn't use watchers |`<ANY bo-show="Person.isPublic"></ANY>`|
-| `bo-hide = "condition"`     | equivalent to `ng-hide` but doesn't use watchers |`<ANY bo-hide="Person.isPrivate"></ANY>`|
-| `bo-disabled = "condition"`     | equivalent to `ng-disabled` but doesn't use watchers |`<ANY bo-disabled="Person.isUnavailable"></ANY>`|
-| `bo-text = "text"`      | evaluates "text" and print it as text inside the element | `<span bo-text="Person.name"></span>` |
-| `bo-bind = "text"`      | alias for `bo-text`, equivalent to `ng-bind` but doesn't use watchers | `<span bo-bind="Person.name"></span>` |
-| `bo-html = "markup"`      | evaluates "markup" and render it as html inside the element |`bo-html="Person.description"`|
-| `bo-href-i = "url"`<br>*use `bo-href` instead* | **equivalent** to `ng-href`.<br>**Heads up!** Using interpolation `{{}}` it creates one watcher: <br>`bo-href-i="/p/{{Person.id}}"`. <br>Use `bo-href` to avoid the watcher:<br> `bo-href="'/p/' + Person.id"` |`<a bo-href-i="/profile{{Person.id}}"></a>`|
-| `bo-href = "url"`      | **similar** to `ng-href` but doesn't allow interpolation using `{{}}` like `ng-href`. <br>**Heads up!** You can't use interpolation `{{}}` inside the url, use bo-href-i for that purpose |`<a bo-href="'/profile' + Person.id"></a>` <br />or<br /> `<a bo-href="link" bo-text="Link"></a>`|
-| `bo-src-i = "url"`<br>*use `bo-src` instead* | **equivalent** to `ng-src`. <br>**Heads up!** It creates one watcher |`<img bo-src-i="{{picture}}" bo-alt="title">`|
-| `bo-src = "url"`      | **similar** to `ng-src` but doesn't allow interpolation using `{{}}` like `ng-src`. <br>**Heads up!** You can't use interpolation `{{}}`, use bo-src-i for that purpose |`<img bo-src="picture" bo-alt="title">`|
-| `bo-class = "object/string"`      | equivalent to `ng-class` but doesn't use watchers |`<span bo-class="{'fancy':Person.condition}">`|
-| `bo-alt = "text"`      | evaluates "text" and render it as `alt` for the element |`<ANY bo-alt="title">`|
-| `bo-title = "text"`      | evaluates "text" and render it as `title` for the element |`<ANY bo-title="title">`|
-| `bo-id = "#id"`      | evaluates "#id" and render it as `id` for the element |`<ANY bo-id="id">`|
-| `bo-style = "object"`      | equivalent to `ng-style` but doesn't use watchers |`<ANY bo-style="{'color':Person.color}">`|
-| `bo-value = "expression"`      | evaluates "expression" and render it as `value` for the element |`<input type="radio" bo-value="value">`|
-| `bo-attr bo-attr-foo = "text"`      | evaluates "text" and render it as a custom attribute for the element |`<div bo-attr bo-attr-foo="bar"></div>`|
-
-## Build
-```
-$ npm install uglify-js -g
-$ uglifyjs bindonce.js -c -m -o bindonce.min.js
-```
-
-## Todo
-Tests
-
-## Copyright
-BindOnce was written by **Pasquale Vazzana**, you can follow him on [google+](https://plus.google.com/101872882413388363602) or on [@twitter](https://twitter.com/PasqualeVazzana)
-
-Thanks to all the [contributors](https://github.com/Pasvaz/bindonce/graphs/contributors)
-
-## LICENSE - "MIT License"
-
-Copyright (c) 2013-2014 Pasquale Vazzana
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.

+ 0 - 325
public/vendor/angular-bindonce/bindonce.js

@@ -1,325 +0,0 @@
-(function () {
-	"use strict";
-	/**
-	 * Bindonce - Zero watches binding for AngularJs
-	 * @version v0.3.3
-	 * @link https://github.com/Pasvaz/bindonce
-	 * @author Pasquale Vazzana <pasqualevazzana@gmail.com>
-	 * @license MIT License, http://www.opensource.org/licenses/MIT
-	 */
-
-	var bindonceModule = angular.module('pasvaz.bindonce', []);
-
-	bindonceModule.directive('bindonce', function ()
-	{
-		var toBoolean = function (value)
-		{
-			if (value && value.length !== 0)
-			{
-				var v = angular.lowercase("" + value);
-				value = !(v === 'f' || v === '0' || v === 'false' || v === 'no' || v === 'n' || v === '[]');
-			}
-			else
-			{
-				value = false;
-			}
-			return value;
-		};
-
-		var msie = parseInt((/msie (\d+)/.exec(angular.lowercase(navigator.userAgent)) || [])[1], 10);
-		if (isNaN(msie))
-		{
-			msie = parseInt((/trident\/.*; rv:(\d+)/.exec(angular.lowercase(navigator.userAgent)) || [])[1], 10);
-		}
-
-		var bindonceDirective =
-		{
-			restrict: "AM",
-			controller: ['$scope', '$element', '$attrs', '$interpolate', function ($scope, $element, $attrs, $interpolate)
-			{
-				var showHideBinder = function (elm, attr, value)
-				{
-					var show = (attr === 'show') ? '' : 'none';
-					var hide = (attr === 'hide') ? '' : 'none';
-					elm.css('display', toBoolean(value) ? show : hide);
-				};
-				var classBinder = function (elm, value)
-				{
-					if (angular.isObject(value) && !angular.isArray(value))
-					{
-						var results = [];
-						angular.forEach(value, function (value, index)
-						{
-							if (value) results.push(index);
-						});
-						value = results;
-					}
-					if (value)
-					{
-						elm.addClass(angular.isArray(value) ? value.join(' ') : value);
-					}
-				};
-				var transclude = function (transcluder, scope)
-				{
-					transcluder.transclude(scope, function (clone)
-					{
-						var parent = transcluder.element.parent();
-						var afterNode = transcluder.element && transcluder.element[transcluder.element.length - 1];
-						var parentNode = parent && parent[0] || afterNode && afterNode.parentNode;
-						var afterNextSibling = (afterNode && afterNode.nextSibling) || null;
-						angular.forEach(clone, function (node)
-						{
-							parentNode.insertBefore(node, afterNextSibling);
-						});
-					});
-				};
-
-				var ctrl =
-				{
-					watcherRemover: undefined,
-					binders: [],
-					group: $attrs.boName,
-					element: $element,
-					ran: false,
-
-					addBinder: function (binder)
-					{
-						this.binders.push(binder);
-
-						// In case of late binding (when using the directive bo-name/bo-parent)
-						// it happens only when you use nested bindonce, if the bo-children
-						// are not dom children the linking can follow another order
-						if (this.ran)
-						{
-							this.runBinders();
-						}
-					},
-
-					setupWatcher: function (bindonceValue)
-					{
-						var that = this;
-						this.watcherRemover = $scope.$watch(bindonceValue, function (newValue)
-						{
-							if (newValue === undefined) return;
-							that.removeWatcher();
-							that.checkBindonce(newValue);
-						}, true);
-					},
-
-					checkBindonce: function (value)
-					{
-						var that = this, promise = (value.$promise) ? value.$promise.then : value.then;
-						// since Angular 1.2 promises are no longer
-						// undefined until they don't get resolved
-						if (typeof promise === 'function')
-						{
-							promise(function ()
-							{
-								that.runBinders();
-							});
-						}
-						else
-						{
-							that.runBinders();
-						}
-					},
-
-					removeWatcher: function ()
-					{
-						if (this.watcherRemover !== undefined)
-						{
-							this.watcherRemover();
-							this.watcherRemover = undefined;
-						}
-					},
-
-					runBinders: function ()
-					{
-						while (this.binders.length > 0)
-						{
-							var binder = this.binders.shift();
-							if (this.group && this.group != binder.group) continue;
-							var value = binder.scope.$eval((binder.interpolate) ? $interpolate(binder.value) : binder.value);
-							switch (binder.attr)
-							{
-								case 'boIf':
-									if (toBoolean(value))
-									{
-										transclude(binder, binder.scope.$new());
-									}
-									break;
-								case 'boSwitch':
-									var selectedTranscludes, switchCtrl = binder.controller[0];
-									if ((selectedTranscludes = switchCtrl.cases['!' + value] || switchCtrl.cases['?']))
-									{
-										binder.scope.$eval(binder.attrs.change);
-										angular.forEach(selectedTranscludes, function (selectedTransclude)
-										{
-											transclude(selectedTransclude, binder.scope.$new());
-										});
-									}
-									break;
-								case 'boSwitchWhen':
-									var ctrl = binder.controller[0];
-									ctrl.cases['!' + binder.attrs.boSwitchWhen] = (ctrl.cases['!' + binder.attrs.boSwitchWhen] || []);
-									ctrl.cases['!' + binder.attrs.boSwitchWhen].push({ transclude: binder.transclude, element: binder.element });
-									break;
-								case 'boSwitchDefault':
-									var ctrl = binder.controller[0];
-									ctrl.cases['?'] = (ctrl.cases['?'] || []);
-									ctrl.cases['?'].push({ transclude: binder.transclude, element: binder.element });
-									break;
-								case 'hide':
-								case 'show':
-									showHideBinder(binder.element, binder.attr, value);
-									break;
-								case 'class':
-									classBinder(binder.element, value);
-									break;
-								case 'text':
-									binder.element.text(value);
-									break;
-								case 'html':
-									binder.element.html(value);
-									break;
-								case 'style':
-									binder.element.css(value);
-									break;
-								case 'disabled':
-									binder.element.prop('disabled', value);
-									break;
-								case 'src':
-									binder.element.attr(binder.attr, value);
-									if (msie) binder.element.prop('src', value);
-									break;
-								case 'attr':
-									angular.forEach(binder.attrs, function (attrValue, attrKey)
-									{
-										var newAttr, newValue;
-										if (attrKey.match(/^boAttr./) && binder.attrs[attrKey])
-										{
-											newAttr = attrKey.replace(/^boAttr/, '').replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
-											newValue = binder.scope.$eval(binder.attrs[attrKey]);
-											binder.element.attr(newAttr, newValue);
-										}
-									});
-									break;
-								case 'href':
-								case 'alt':
-								case 'title':
-								case 'id':
-								case 'value':
-									binder.element.attr(binder.attr, value);
-									break;
-							}
-						}
-						this.ran = true;
-					}
-				};
-
-				angular.extend(this, ctrl);
-			}],
-
-			link: function (scope, elm, attrs, bindonceController)
-			{
-				var value = attrs.bindonce && scope.$eval(attrs.bindonce);
-				if (value !== undefined)
-				{
-					bindonceController.checkBindonce(value);
-				}
-				else
-				{
-					bindonceController.setupWatcher(attrs.bindonce);
-					elm.bind("$destroy", bindonceController.removeWatcher);
-				}
-			}
-		};
-
-		return bindonceDirective;
-	});
-
-	angular.forEach(
-	[
-		{ directiveName: 'boShow', attribute: 'show' },
-		{ directiveName: 'boHide', attribute: 'hide' },
-		{ directiveName: 'boClass', attribute: 'class' },
-		{ directiveName: 'boText', attribute: 'text' },
-		{ directiveName: 'boBind', attribute: 'text' },
-		{ directiveName: 'boHtml', attribute: 'html' },
-		{ directiveName: 'boSrcI', attribute: 'src', interpolate: true },
-		{ directiveName: 'boSrc', attribute: 'src' },
-		{ directiveName: 'boHrefI', attribute: 'href', interpolate: true },
-		{ directiveName: 'boHref', attribute: 'href' },
-		{ directiveName: 'boAlt', attribute: 'alt' },
-		{ directiveName: 'boTitle', attribute: 'title' },
-		{ directiveName: 'boId', attribute: 'id' },
-		{ directiveName: 'boStyle', attribute: 'style' },
-		{ directiveName: 'boDisabled', attribute: 'disabled' },
-		{ directiveName: 'boValue', attribute: 'value' },
-		{ directiveName: 'boAttr', attribute: 'attr' },
-
-		{ directiveName: 'boIf', transclude: 'element', terminal: true, priority: 1000 },
-		{ directiveName: 'boSwitch', require: 'boSwitch', controller: function () { this.cases = {}; } },
-		{ directiveName: 'boSwitchWhen', transclude: 'element', priority: 800, require: '^boSwitch' },
-		{ directiveName: 'boSwitchDefault', transclude: 'element', priority: 800, require: '^boSwitch' }
-	],
-	function (boDirective)
-	{
-		var childPriority = 200;
-		return bindonceModule.directive(boDirective.directiveName, function ()
-		{
-			var bindonceDirective =
-			{
-				priority: boDirective.priority || childPriority,
-				transclude: boDirective.transclude || false,
-				terminal: boDirective.terminal || false,
-				require: ['^bindonce'].concat(boDirective.require || []),
-				controller: boDirective.controller,
-				compile: function (tElement, tAttrs, transclude)
-				{
-					return function (scope, elm, attrs, controllers)
-					{
-						var bindonceController = controllers[0];
-						var name = attrs.boParent;
-						if (name && bindonceController.group !== name)
-						{
-							var element = bindonceController.element.parent();
-							bindonceController = undefined;
-							var parentValue;
-
-							while (element[0].nodeType !== 9 && element.length)
-							{
-								if ((parentValue = element.data('$bindonceController'))
-									&& parentValue.group === name)
-								{
-									bindonceController = parentValue;
-									break;
-								}
-								element = element.parent();
-							}
-							if (!bindonceController)
-							{
-								throw new Error("No bindonce controller: " + name);
-							}
-						}
-
-						bindonceController.addBinder(
-						{
-							element: elm,
-							attr: boDirective.attribute || boDirective.directiveName,
-							attrs: attrs,
-							value: attrs[boDirective.directiveName],
-							interpolate: boDirective.interpolate,
-							group: name,
-							transclude: transclude,
-							controller: controllers.slice(1),
-							scope: scope
-						});
-					};
-				}
-			};
-
-			return bindonceDirective;
-		});
-	})
-})();

文件差異過大導致無法顯示
+ 0 - 0
public/vendor/angular-bindonce/bindonce.min.js


+ 0 - 28
public/vendor/angular-bindonce/bower.json

@@ -1,28 +0,0 @@
-{
-  "name": "angular-bindonce",
-  "version": "0.3.3",
-  "main": "bindonce.js",
-  "description": "Zero watchers binding directives for AngularJS",
-  "homepage": "https://github.com/Pasvaz/bindonce",
-  "author": "Pasquale Vazzana <pasqualevazzana@gmail.com>",
-  "repository": {
-    "type": "git",
-    "url": "https://github.com/Pasvaz/bindonce.git"
-  },
-  "license": "MIT",
-  "ignore": [
-    "**/.*",
-    "node_modules",
-    "components"
-  ],
-  "dependencies": {
-  },
-  "keywords": [
-    "angularjs",
-    "angular",
-    "directive",
-    "binding",
-    "watcher",
-    "bindonce"
-  ]
-}

+ 0 - 28
public/vendor/angular-bindonce/package.json

@@ -1,28 +0,0 @@
-{
-  "name": "angular-bindonce",
-  "version": "0.3.3",
-  "main": "bindonce.js",
-  "description": "Zero watchers binding directives for AngularJS",
-  "homepage": "https://github.com/Pasvaz/bindonce",
-  "author": "Pasquale Vazzana <pasqualevazzana@gmail.com>",
-  "repository": {
-    "type": "git",
-    "url": "https://github.com/Pasvaz/bindonce.git"
-  },
-  "license": "MIT",
-  "ignore": [
-    "**/.*",
-    "node_modules",
-    "components"
-  ],
-  "dependencies": {
-  },
-  "keywords": [
-    "angularjs",
-    "angular",
-    "directive",
-    "binding",
-    "watcher",
-    "bindonce"
-  ]
-}

+ 0 - 20
public/vendor/angular-mocks/.bower.json

@@ -1,20 +0,0 @@
-{
-  "name": "angular-mocks",
-  "version": "1.6.1",
-  "license": "MIT",
-  "main": "./angular-mocks.js",
-  "ignore": [],
-  "dependencies": {
-    "angular": "1.6.1"
-  },
-  "homepage": "https://github.com/angular/bower-angular-mocks",
-  "_release": "1.6.1",
-  "_resolution": {
-    "type": "version",
-    "tag": "v1.6.1",
-    "commit": "d8ac5a2016c9714b7c87284d21a34648036e8eea"
-  },
-  "_source": "https://github.com/angular/bower-angular-mocks.git",
-  "_target": "1.6.1",
-  "_originalSource": "angular-mocks"
-}

+ 0 - 21
public/vendor/angular-mocks/LICENSE.md

@@ -1,21 +0,0 @@
-The MIT License (MIT)
-
-Copyright (c) 2016 Angular
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.

+ 0 - 63
public/vendor/angular-mocks/README.md

@@ -1,63 +0,0 @@
-# packaged angular-mocks
-
-This repo is for distribution on `npm` and `bower`. The source for this module is in the
-[main AngularJS repo](https://github.com/angular/angular.js/tree/master/src/ngMock).
-Please file issues and pull requests against that repo.
-
-## Install
-
-You can install this package either with `npm` or with `bower`.
-
-### npm
-
-```shell
-npm install angular-mocks
-```
-
-You can `require` ngMock modules:
-
-```js
-var angular = require('angular');
-angular.module('myMod', [
-  require('angular-animate'),
-  require('angular-mocks/ngMock'),
-  require('angular-mocks/ngAnimateMock')
-]);
-```
-
-### bower
-
-```shell
-bower install angular-mocks
-```
-
-The mocks are then available at `bower_components/angular-mocks/angular-mocks.js`.
-
-## Documentation
-
-Documentation is available on the
-[AngularJS docs site](https://docs.angularjs.org/guide/unit-testing).
-
-## License
-
-The MIT License
-
-Copyright (c) 2010-2015 Google, Inc. http://angularjs.org
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.

+ 0 - 3408
public/vendor/angular-mocks/angular-mocks.js

@@ -1,3408 +0,0 @@
-/**
- * @license AngularJS v1.6.1
- * (c) 2010-2016 Google, Inc. http://angularjs.org
- * License: MIT
- */
-(function(window, angular) {
-
-'use strict';
-
-/**
- * @ngdoc object
- * @name angular.mock
- * @description
- *
- * Namespace from 'angular-mocks.js' which contains testing related code.
- *
- */
-angular.mock = {};
-
-/**
- * ! This is a private undocumented service !
- *
- * @name $browser
- *
- * @description
- * This service is a mock implementation of {@link ng.$browser}. It provides fake
- * implementation for commonly used browser apis that are hard to test, e.g. setTimeout, xhr,
- * cookies, etc.
- *
- * The api of this service is the same as that of the real {@link ng.$browser $browser}, except
- * that there are several helper methods available which can be used in tests.
- */
-angular.mock.$BrowserProvider = function() {
-  this.$get = function() {
-    return new angular.mock.$Browser();
-  };
-};
-
-angular.mock.$Browser = function() {
-  var self = this;
-
-  this.isMock = true;
-  self.$$url = 'http://server/';
-  self.$$lastUrl = self.$$url; // used by url polling fn
-  self.pollFns = [];
-
-  // TODO(vojta): remove this temporary api
-  self.$$completeOutstandingRequest = angular.noop;
-  self.$$incOutstandingRequestCount = angular.noop;
-
-
-  // register url polling fn
-
-  self.onUrlChange = function(listener) {
-    self.pollFns.push(
-      function() {
-        if (self.$$lastUrl !== self.$$url || self.$$state !== self.$$lastState) {
-          self.$$lastUrl = self.$$url;
-          self.$$lastState = self.$$state;
-          listener(self.$$url, self.$$state);
-        }
-      }
-    );
-
-    return listener;
-  };
-
-  self.$$applicationDestroyed = angular.noop;
-  self.$$checkUrlChange = angular.noop;
-
-  self.deferredFns = [];
-  self.deferredNextId = 0;
-
-  self.defer = function(fn, delay) {
-    delay = delay || 0;
-    self.deferredFns.push({time:(self.defer.now + delay), fn:fn, id: self.deferredNextId});
-    self.deferredFns.sort(function(a, b) { return a.time - b.time;});
-    return self.deferredNextId++;
-  };
-
-
-  /**
-   * @name $browser#defer.now
-   *
-   * @description
-   * Current milliseconds mock time.
-   */
-  self.defer.now = 0;
-
-
-  self.defer.cancel = function(deferId) {
-    var fnIndex;
-
-    angular.forEach(self.deferredFns, function(fn, index) {
-      if (fn.id === deferId) fnIndex = index;
-    });
-
-    if (angular.isDefined(fnIndex)) {
-      self.deferredFns.splice(fnIndex, 1);
-      return true;
-    }
-
-    return false;
-  };
-
-
-  /**
-   * @name $browser#defer.flush
-   *
-   * @description
-   * Flushes all pending requests and executes the defer callbacks.
-   *
-   * @param {number=} number of milliseconds to flush. See {@link #defer.now}
-   */
-  self.defer.flush = function(delay) {
-    var nextTime;
-
-    if (angular.isDefined(delay)) {
-      // A delay was passed so compute the next time
-      nextTime = self.defer.now + delay;
-    } else {
-      if (self.deferredFns.length) {
-        // No delay was passed so set the next time so that it clears the deferred queue
-        nextTime = self.deferredFns[self.deferredFns.length - 1].time;
-      } else {
-        // No delay passed, but there are no deferred tasks so flush - indicates an error!
-        throw new Error('No deferred tasks to be flushed');
-      }
-    }
-
-    while (self.deferredFns.length && self.deferredFns[0].time <= nextTime) {
-      // Increment the time and call the next deferred function
-      self.defer.now = self.deferredFns[0].time;
-      self.deferredFns.shift().fn();
-    }
-
-    // Ensure that the current time is correct
-    self.defer.now = nextTime;
-  };
-
-  self.$$baseHref = '/';
-  self.baseHref = function() {
-    return this.$$baseHref;
-  };
-};
-angular.mock.$Browser.prototype = {
-
-  /**
-   * @name $browser#poll
-   *
-   * @description
-   * run all fns in pollFns
-   */
-  poll: function poll() {
-    angular.forEach(this.pollFns, function(pollFn) {
-      pollFn();
-    });
-  },
-
-  url: function(url, replace, state) {
-    if (angular.isUndefined(state)) {
-      state = null;
-    }
-    if (url) {
-      this.$$url = url;
-      // Native pushState serializes & copies the object; simulate it.
-      this.$$state = angular.copy(state);
-      return this;
-    }
-
-    return this.$$url;
-  },
-
-  state: function() {
-    return this.$$state;
-  },
-
-  notifyWhenNoOutstandingRequests: function(fn) {
-    fn();
-  }
-};
-
-
-/**
- * @ngdoc provider
- * @name $exceptionHandlerProvider
- *
- * @description
- * Configures the mock implementation of {@link ng.$exceptionHandler} to rethrow or to log errors
- * passed to the `$exceptionHandler`.
- */
-
-/**
- * @ngdoc service
- * @name $exceptionHandler
- *
- * @description
- * Mock implementation of {@link ng.$exceptionHandler} that rethrows or logs errors passed
- * to it. See {@link ngMock.$exceptionHandlerProvider $exceptionHandlerProvider} for configuration
- * information.
- *
- *
- * ```js
- *   describe('$exceptionHandlerProvider', function() {
- *
- *     it('should capture log messages and exceptions', function() {
- *
- *       module(function($exceptionHandlerProvider) {
- *         $exceptionHandlerProvider.mode('log');
- *       });
- *
- *       inject(function($log, $exceptionHandler, $timeout) {
- *         $timeout(function() { $log.log(1); });
- *         $timeout(function() { $log.log(2); throw 'banana peel'; });
- *         $timeout(function() { $log.log(3); });
- *         expect($exceptionHandler.errors).toEqual([]);
- *         expect($log.assertEmpty());
- *         $timeout.flush();
- *         expect($exceptionHandler.errors).toEqual(['banana peel']);
- *         expect($log.log.logs).toEqual([[1], [2], [3]]);
- *       });
- *     });
- *   });
- * ```
- */
-
-angular.mock.$ExceptionHandlerProvider = function() {
-  var handler;
-
-  /**
-   * @ngdoc method
-   * @name $exceptionHandlerProvider#mode
-   *
-   * @description
-   * Sets the logging mode.
-   *
-   * @param {string} mode Mode of operation, defaults to `rethrow`.
-   *
-   *   - `log`: Sometimes it is desirable to test that an error is thrown, for this case the `log`
-   *     mode stores an array of errors in `$exceptionHandler.errors`, to allow later assertion of
-   *     them. See {@link ngMock.$log#assertEmpty assertEmpty()} and
-   *     {@link ngMock.$log#reset reset()}.
-   *   - `rethrow`: If any errors are passed to the handler in tests, it typically means that there
-   *     is a bug in the application or test, so this mock will make these tests fail. For any
-   *     implementations that expect exceptions to be thrown, the `rethrow` mode will also maintain
-   *     a log of thrown errors in `$exceptionHandler.errors`.
-   */
-  this.mode = function(mode) {
-
-    switch (mode) {
-      case 'log':
-      case 'rethrow':
-        var errors = [];
-        handler = function(e) {
-          if (arguments.length === 1) {
-            errors.push(e);
-          } else {
-            errors.push([].slice.call(arguments, 0));
-          }
-          if (mode === 'rethrow') {
-            throw e;
-          }
-        };
-        handler.errors = errors;
-        break;
-      default:
-        throw new Error('Unknown mode \'' + mode + '\', only \'log\'/\'rethrow\' modes are allowed!');
-    }
-  };
-
-  this.$get = function() {
-    return handler;
-  };
-
-  this.mode('rethrow');
-};
-
-
-/**
- * @ngdoc service
- * @name $log
- *
- * @description
- * Mock implementation of {@link ng.$log} that gathers all logged messages in arrays
- * (one array per logging level). These arrays are exposed as `logs` property of each of the
- * level-specific log function, e.g. for level `error` the array is exposed as `$log.error.logs`.
- *
- */
-angular.mock.$LogProvider = function() {
-  var debug = true;
-
-  function concat(array1, array2, index) {
-    return array1.concat(Array.prototype.slice.call(array2, index));
-  }
-
-  this.debugEnabled = function(flag) {
-    if (angular.isDefined(flag)) {
-      debug = flag;
-      return this;
-    } else {
-      return debug;
-    }
-  };
-
-  this.$get = function() {
-    var $log = {
-      log: function() { $log.log.logs.push(concat([], arguments, 0)); },
-      warn: function() { $log.warn.logs.push(concat([], arguments, 0)); },
-      info: function() { $log.info.logs.push(concat([], arguments, 0)); },
-      error: function() { $log.error.logs.push(concat([], arguments, 0)); },
-      debug: function() {
-        if (debug) {
-          $log.debug.logs.push(concat([], arguments, 0));
-        }
-      }
-    };
-
-    /**
-     * @ngdoc method
-     * @name $log#reset
-     *
-     * @description
-     * Reset all of the logging arrays to empty.
-     */
-    $log.reset = function() {
-      /**
-       * @ngdoc property
-       * @name $log#log.logs
-       *
-       * @description
-       * Array of messages logged using {@link ng.$log#log `log()`}.
-       *
-       * @example
-       * ```js
-       * $log.log('Some Log');
-       * var first = $log.log.logs.unshift();
-       * ```
-       */
-      $log.log.logs = [];
-      /**
-       * @ngdoc property
-       * @name $log#info.logs
-       *
-       * @description
-       * Array of messages logged using {@link ng.$log#info `info()`}.
-       *
-       * @example
-       * ```js
-       * $log.info('Some Info');
-       * var first = $log.info.logs.unshift();
-       * ```
-       */
-      $log.info.logs = [];
-      /**
-       * @ngdoc property
-       * @name $log#warn.logs
-       *
-       * @description
-       * Array of messages logged using {@link ng.$log#warn `warn()`}.
-       *
-       * @example
-       * ```js
-       * $log.warn('Some Warning');
-       * var first = $log.warn.logs.unshift();
-       * ```
-       */
-      $log.warn.logs = [];
-      /**
-       * @ngdoc property
-       * @name $log#error.logs
-       *
-       * @description
-       * Array of messages logged using {@link ng.$log#error `error()`}.
-       *
-       * @example
-       * ```js
-       * $log.error('Some Error');
-       * var first = $log.error.logs.unshift();
-       * ```
-       */
-      $log.error.logs = [];
-        /**
-       * @ngdoc property
-       * @name $log#debug.logs
-       *
-       * @description
-       * Array of messages logged using {@link ng.$log#debug `debug()`}.
-       *
-       * @example
-       * ```js
-       * $log.debug('Some Error');
-       * var first = $log.debug.logs.unshift();
-       * ```
-       */
-      $log.debug.logs = [];
-    };
-
-    /**
-     * @ngdoc method
-     * @name $log#assertEmpty
-     *
-     * @description
-     * Assert that all of the logging methods have no logged messages. If any messages are present,
-     * an exception is thrown.
-     */
-    $log.assertEmpty = function() {
-      var errors = [];
-      angular.forEach(['error', 'warn', 'info', 'log', 'debug'], function(logLevel) {
-        angular.forEach($log[logLevel].logs, function(log) {
-          angular.forEach(log, function(logItem) {
-            errors.push('MOCK $log (' + logLevel + '): ' + String(logItem) + '\n' +
-                        (logItem.stack || ''));
-          });
-        });
-      });
-      if (errors.length) {
-        errors.unshift('Expected $log to be empty! Either a message was logged unexpectedly, or ' +
-          'an expected log message was not checked and removed:');
-        errors.push('');
-        throw new Error(errors.join('\n---------\n'));
-      }
-    };
-
-    $log.reset();
-    return $log;
-  };
-};
-
-
-/**
- * @ngdoc service
- * @name $interval
- *
- * @description
- * Mock implementation of the $interval service.
- *
- * Use {@link ngMock.$interval#flush `$interval.flush(millis)`} to
- * move forward by `millis` milliseconds and trigger any functions scheduled to run in that
- * time.
- *
- * @param {function()} fn A function that should be called repeatedly.
- * @param {number} delay Number of milliseconds between each function call.
- * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
- *   indefinitely.
- * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
- *   will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
- * @param {...*=} Pass additional parameters to the executed function.
- * @returns {promise} A promise which will be notified on each iteration.
- */
-angular.mock.$IntervalProvider = function() {
-  this.$get = ['$browser', '$rootScope', '$q', '$$q',
-       function($browser,   $rootScope,   $q,   $$q) {
-    var repeatFns = [],
-        nextRepeatId = 0,
-        now = 0;
-
-    var $interval = function(fn, delay, count, invokeApply) {
-      var hasParams = arguments.length > 4,
-          args = hasParams ? Array.prototype.slice.call(arguments, 4) : [],
-          iteration = 0,
-          skipApply = (angular.isDefined(invokeApply) && !invokeApply),
-          deferred = (skipApply ? $$q : $q).defer(),
-          promise = deferred.promise;
-
-      count = (angular.isDefined(count)) ? count : 0;
-      promise.then(null, function() {}, (!hasParams) ? fn : function() {
-        fn.apply(null, args);
-      });
-
-      promise.$$intervalId = nextRepeatId;
-
-      function tick() {
-        deferred.notify(iteration++);
-
-        if (count > 0 && iteration >= count) {
-          var fnIndex;
-          deferred.resolve(iteration);
-
-          angular.forEach(repeatFns, function(fn, index) {
-            if (fn.id === promise.$$intervalId) fnIndex = index;
-          });
-
-          if (angular.isDefined(fnIndex)) {
-            repeatFns.splice(fnIndex, 1);
-          }
-        }
-
-        if (skipApply) {
-          $browser.defer.flush();
-        } else {
-          $rootScope.$apply();
-        }
-      }
-
-      repeatFns.push({
-        nextTime:(now + delay),
-        delay: delay,
-        fn: tick,
-        id: nextRepeatId,
-        deferred: deferred
-      });
-      repeatFns.sort(function(a, b) { return a.nextTime - b.nextTime;});
-
-      nextRepeatId++;
-      return promise;
-    };
-    /**
-     * @ngdoc method
-     * @name $interval#cancel
-     *
-     * @description
-     * Cancels a task associated with the `promise`.
-     *
-     * @param {promise} promise A promise from calling the `$interval` function.
-     * @returns {boolean} Returns `true` if the task was successfully cancelled.
-     */
-    $interval.cancel = function(promise) {
-      if (!promise) return false;
-      var fnIndex;
-
-      angular.forEach(repeatFns, function(fn, index) {
-        if (fn.id === promise.$$intervalId) fnIndex = index;
-      });
-
-      if (angular.isDefined(fnIndex)) {
-        repeatFns[fnIndex].deferred.promise.then(undefined, function() {});
-        repeatFns[fnIndex].deferred.reject('canceled');
-        repeatFns.splice(fnIndex, 1);
-        return true;
-      }
-
-      return false;
-    };
-
-    /**
-     * @ngdoc method
-     * @name $interval#flush
-     * @description
-     *
-     * Runs interval tasks scheduled to be run in the next `millis` milliseconds.
-     *
-     * @param {number=} millis maximum timeout amount to flush up until.
-     *
-     * @return {number} The amount of time moved forward.
-     */
-    $interval.flush = function(millis) {
-      now += millis;
-      while (repeatFns.length && repeatFns[0].nextTime <= now) {
-        var task = repeatFns[0];
-        task.fn();
-        task.nextTime += task.delay;
-        repeatFns.sort(function(a, b) { return a.nextTime - b.nextTime;});
-      }
-      return millis;
-    };
-
-    return $interval;
-  }];
-};
-
-
-function jsonStringToDate(string) {
-  // The R_ISO8061_STR regex is never going to fit into the 100 char limit!
-  // eslit-disable-next-line max-len
-  var R_ISO8061_STR = /^(-?\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d{3}))?)?)?(Z|([+-])(\d\d):?(\d\d)))?$/;
-
-  var match;
-  if ((match = string.match(R_ISO8061_STR))) {
-    var date = new Date(0),
-        tzHour = 0,
-        tzMin  = 0;
-    if (match[9]) {
-      tzHour = toInt(match[9] + match[10]);
-      tzMin = toInt(match[9] + match[11]);
-    }
-    date.setUTCFullYear(toInt(match[1]), toInt(match[2]) - 1, toInt(match[3]));
-    date.setUTCHours(toInt(match[4] || 0) - tzHour,
-                     toInt(match[5] || 0) - tzMin,
-                     toInt(match[6] || 0),
-                     toInt(match[7] || 0));
-    return date;
-  }
-  return string;
-}
-
-function toInt(str) {
-  return parseInt(str, 10);
-}
-
-function padNumberInMock(num, digits, trim) {
-  var neg = '';
-  if (num < 0) {
-    neg =  '-';
-    num = -num;
-  }
-  num = '' + num;
-  while (num.length < digits) num = '0' + num;
-  if (trim) {
-    num = num.substr(num.length - digits);
-  }
-  return neg + num;
-}
-
-
-/**
- * @ngdoc type
- * @name angular.mock.TzDate
- * @description
- *
- * *NOTE*: this is not an injectable instance, just a globally available mock class of `Date`.
- *
- * Mock of the Date type which has its timezone specified via constructor arg.
- *
- * The main purpose is to create Date-like instances with timezone fixed to the specified timezone
- * offset, so that we can test code that depends on local timezone settings without dependency on
- * the time zone settings of the machine where the code is running.
- *
- * @param {number} offset Offset of the *desired* timezone in hours (fractions will be honored)
- * @param {(number|string)} timestamp Timestamp representing the desired time in *UTC*
- *
- * @example
- * !!!! WARNING !!!!!
- * This is not a complete Date object so only methods that were implemented can be called safely.
- * To make matters worse, TzDate instances inherit stuff from Date via a prototype.
- *
- * We do our best to intercept calls to "unimplemented" methods, but since the list of methods is
- * incomplete we might be missing some non-standard methods. This can result in errors like:
- * "Date.prototype.foo called on incompatible Object".
- *
- * ```js
- * var newYearInBratislava = new TzDate(-1, '2009-12-31T23:00:00Z');
- * newYearInBratislava.getTimezoneOffset() => -60;
- * newYearInBratislava.getFullYear() => 2010;
- * newYearInBratislava.getMonth() => 0;
- * newYearInBratislava.getDate() => 1;
- * newYearInBratislava.getHours() => 0;
- * newYearInBratislava.getMinutes() => 0;
- * newYearInBratislava.getSeconds() => 0;
- * ```
- *
- */
-angular.mock.TzDate = function(offset, timestamp) {
-  var self = new Date(0);
-  if (angular.isString(timestamp)) {
-    var tsStr = timestamp;
-
-    self.origDate = jsonStringToDate(timestamp);
-
-    timestamp = self.origDate.getTime();
-    if (isNaN(timestamp)) {
-      // eslint-disable-next-line no-throw-literal
-      throw {
-        name: 'Illegal Argument',
-        message: 'Arg \'' + tsStr + '\' passed into TzDate constructor is not a valid date string'
-      };
-    }
-  } else {
-    self.origDate = new Date(timestamp);
-  }
-
-  var localOffset = new Date(timestamp).getTimezoneOffset();
-  self.offsetDiff = localOffset * 60 * 1000 - offset * 1000 * 60 * 60;
-  self.date = new Date(timestamp + self.offsetDiff);
-
-  self.getTime = function() {
-    return self.date.getTime() - self.offsetDiff;
-  };
-
-  self.toLocaleDateString = function() {
-    return self.date.toLocaleDateString();
-  };
-
-  self.getFullYear = function() {
-    return self.date.getFullYear();
-  };
-
-  self.getMonth = function() {
-    return self.date.getMonth();
-  };
-
-  self.getDate = function() {
-    return self.date.getDate();
-  };
-
-  self.getHours = function() {
-    return self.date.getHours();
-  };
-
-  self.getMinutes = function() {
-    return self.date.getMinutes();
-  };
-
-  self.getSeconds = function() {
-    return self.date.getSeconds();
-  };
-
-  self.getMilliseconds = function() {
-    return self.date.getMilliseconds();
-  };
-
-  self.getTimezoneOffset = function() {
-    return offset * 60;
-  };
-
-  self.getUTCFullYear = function() {
-    return self.origDate.getUTCFullYear();
-  };
-
-  self.getUTCMonth = function() {
-    return self.origDate.getUTCMonth();
-  };
-
-  self.getUTCDate = function() {
-    return self.origDate.getUTCDate();
-  };
-
-  self.getUTCHours = function() {
-    return self.origDate.getUTCHours();
-  };
-
-  self.getUTCMinutes = function() {
-    return self.origDate.getUTCMinutes();
-  };
-
-  self.getUTCSeconds = function() {
-    return self.origDate.getUTCSeconds();
-  };
-
-  self.getUTCMilliseconds = function() {
-    return self.origDate.getUTCMilliseconds();
-  };
-
-  self.getDay = function() {
-    return self.date.getDay();
-  };
-
-  // provide this method only on browsers that already have it
-  if (self.toISOString) {
-    self.toISOString = function() {
-      return padNumberInMock(self.origDate.getUTCFullYear(), 4) + '-' +
-            padNumberInMock(self.origDate.getUTCMonth() + 1, 2) + '-' +
-            padNumberInMock(self.origDate.getUTCDate(), 2) + 'T' +
-            padNumberInMock(self.origDate.getUTCHours(), 2) + ':' +
-            padNumberInMock(self.origDate.getUTCMinutes(), 2) + ':' +
-            padNumberInMock(self.origDate.getUTCSeconds(), 2) + '.' +
-            padNumberInMock(self.origDate.getUTCMilliseconds(), 3) + 'Z';
-    };
-  }
-
-  //hide all methods not implemented in this mock that the Date prototype exposes
-  var unimplementedMethods = ['getUTCDay',
-      'getYear', 'setDate', 'setFullYear', 'setHours', 'setMilliseconds',
-      'setMinutes', 'setMonth', 'setSeconds', 'setTime', 'setUTCDate', 'setUTCFullYear',
-      'setUTCHours', 'setUTCMilliseconds', 'setUTCMinutes', 'setUTCMonth', 'setUTCSeconds',
-      'setYear', 'toDateString', 'toGMTString', 'toJSON', 'toLocaleFormat', 'toLocaleString',
-      'toLocaleTimeString', 'toSource', 'toString', 'toTimeString', 'toUTCString', 'valueOf'];
-
-  angular.forEach(unimplementedMethods, function(methodName) {
-    self[methodName] = function() {
-      throw new Error('Method \'' + methodName + '\' is not implemented in the TzDate mock');
-    };
-  });
-
-  return self;
-};
-
-//make "tzDateInstance instanceof Date" return true
-angular.mock.TzDate.prototype = Date.prototype;
-
-
-/**
- * @ngdoc service
- * @name $animate
- *
- * @description
- * Mock implementation of the {@link ng.$animate `$animate`} service. Exposes two additional methods
- * for testing animations.
- *
- * You need to require the `ngAnimateMock` module in your test suite for instance `beforeEach(module('ngAnimateMock'))`
- */
-angular.mock.animate = angular.module('ngAnimateMock', ['ng'])
-
-  .config(['$provide', function($provide) {
-
-    $provide.factory('$$forceReflow', function() {
-      function reflowFn() {
-        reflowFn.totalReflows++;
-      }
-      reflowFn.totalReflows = 0;
-      return reflowFn;
-    });
-
-    $provide.factory('$$animateAsyncRun', function() {
-      var queue = [];
-      var queueFn = function() {
-        return function(fn) {
-          queue.push(fn);
-        };
-      };
-      queueFn.flush = function() {
-        if (queue.length === 0) return false;
-
-        for (var i = 0; i < queue.length; i++) {
-          queue[i]();
-        }
-        queue = [];
-
-        return true;
-      };
-      return queueFn;
-    });
-
-    $provide.decorator('$$animateJs', ['$delegate', function($delegate) {
-      var runners = [];
-
-      var animateJsConstructor = function() {
-        var animator = $delegate.apply($delegate, arguments);
-        // If no javascript animation is found, animator is undefined
-        if (animator) {
-          runners.push(animator);
-        }
-        return animator;
-      };
-
-      animateJsConstructor.$closeAndFlush = function() {
-        runners.forEach(function(runner) {
-          runner.end();
-        });
-        runners = [];
-      };
-
-      return animateJsConstructor;
-    }]);
-
-    $provide.decorator('$animateCss', ['$delegate', function($delegate) {
-      var runners = [];
-
-      var animateCssConstructor = function(element, options) {
-        var animator = $delegate(element, options);
-        runners.push(animator);
-        return animator;
-      };
-
-      animateCssConstructor.$closeAndFlush = function() {
-        runners.forEach(function(runner) {
-          runner.end();
-        });
-        runners = [];
-      };
-
-      return animateCssConstructor;
-    }]);
-
-    $provide.decorator('$animate', ['$delegate', '$timeout', '$browser', '$$rAF', '$animateCss', '$$animateJs',
-                                    '$$forceReflow', '$$animateAsyncRun', '$rootScope',
-                            function($delegate,   $timeout,   $browser,   $$rAF,   $animateCss,   $$animateJs,
-                                     $$forceReflow,   $$animateAsyncRun,  $rootScope) {
-      var animate = {
-        queue: [],
-        cancel: $delegate.cancel,
-        on: $delegate.on,
-        off: $delegate.off,
-        pin: $delegate.pin,
-        get reflows() {
-          return $$forceReflow.totalReflows;
-        },
-        enabled: $delegate.enabled,
-        /**
-         * @ngdoc method
-         * @name $animate#closeAndFlush
-         * @description
-         *
-         * This method will close all pending animations (both {@link ngAnimate#javascript-based-animations Javascript}
-         * and {@link ngAnimate.$animateCss CSS}) and it will also flush any remaining animation frames and/or callbacks.
-         */
-        closeAndFlush: function() {
-          // we allow the flush command to swallow the errors
-          // because depending on whether CSS or JS animations are
-          // used, there may not be a RAF flush. The primary flush
-          // at the end of this function must throw an exception
-          // because it will track if there were pending animations
-          this.flush(true);
-          $animateCss.$closeAndFlush();
-          $$animateJs.$closeAndFlush();
-          this.flush();
-        },
-        /**
-         * @ngdoc method
-         * @name $animate#flush
-         * @description
-         *
-         * This method is used to flush the pending callbacks and animation frames to either start
-         * an animation or conclude an animation. Note that this will not actually close an
-         * actively running animation (see {@link ngMock.$animate#closeAndFlush `closeAndFlush()`} for that).
-         */
-        flush: function(hideErrors) {
-          $rootScope.$digest();
-
-          var doNextRun, somethingFlushed = false;
-          do {
-            doNextRun = false;
-
-            if ($$rAF.queue.length) {
-              $$rAF.flush();
-              doNextRun = somethingFlushed = true;
-            }
-
-            if ($$animateAsyncRun.flush()) {
-              doNextRun = somethingFlushed = true;
-            }
-          } while (doNextRun);
-
-          if (!somethingFlushed && !hideErrors) {
-            throw new Error('No pending animations ready to be closed or flushed');
-          }
-
-          $rootScope.$digest();
-        }
-      };
-
-      angular.forEach(
-        ['animate','enter','leave','move','addClass','removeClass','setClass'], function(method) {
-        animate[method] = function() {
-          animate.queue.push({
-            event: method,
-            element: arguments[0],
-            options: arguments[arguments.length - 1],
-            args: arguments
-          });
-          return $delegate[method].apply($delegate, arguments);
-        };
-      });
-
-      return animate;
-    }]);
-
-  }]);
-
-
-/**
- * @ngdoc function
- * @name angular.mock.dump
- * @description
- *
- * *NOTE*: This is not an injectable instance, just a globally available function.
- *
- * Method for serializing common angular objects (scope, elements, etc..) into strings.
- * It is useful for logging objects to the console when debugging.
- *
- * @param {*} object - any object to turn into string.
- * @return {string} a serialized string of the argument
- */
-angular.mock.dump = function(object) {
-  return serialize(object);
-
-  function serialize(object) {
-    var out;
-
-    if (angular.isElement(object)) {
-      object = angular.element(object);
-      out = angular.element('<div></div>');
-      angular.forEach(object, function(element) {
-        out.append(angular.element(element).clone());
-      });
-      out = out.html();
-    } else if (angular.isArray(object)) {
-      out = [];
-      angular.forEach(object, function(o) {
-        out.push(serialize(o));
-      });
-      out = '[ ' + out.join(', ') + ' ]';
-    } else if (angular.isObject(object)) {
-      if (angular.isFunction(object.$eval) && angular.isFunction(object.$apply)) {
-        out = serializeScope(object);
-      } else if (object instanceof Error) {
-        out = object.stack || ('' + object.name + ': ' + object.message);
-      } else {
-        // TODO(i): this prevents methods being logged,
-        // we should have a better way to serialize objects
-        out = angular.toJson(object, true);
-      }
-    } else {
-      out = String(object);
-    }
-
-    return out;
-  }
-
-  function serializeScope(scope, offset) {
-    offset = offset ||  '  ';
-    var log = [offset + 'Scope(' + scope.$id + '): {'];
-    for (var key in scope) {
-      if (Object.prototype.hasOwnProperty.call(scope, key) && !key.match(/^(\$|this)/)) {
-        log.push('  ' + key + ': ' + angular.toJson(scope[key]));
-      }
-    }
-    var child = scope.$$childHead;
-    while (child) {
-      log.push(serializeScope(child, offset + '  '));
-      child = child.$$nextSibling;
-    }
-    log.push('}');
-    return log.join('\n' + offset);
-  }
-};
-
-/**
- * @ngdoc service
- * @name $httpBackend
- * @description
- * Fake HTTP backend implementation suitable for unit testing applications that use the
- * {@link ng.$http $http service}.
- *
- * <div class="alert alert-info">
- * **Note**: For fake HTTP backend implementation suitable for end-to-end testing or backend-less
- * development please see {@link ngMockE2E.$httpBackend e2e $httpBackend mock}.
- * </div>
- *
- * During unit testing, we want our unit tests to run quickly and have no external dependencies so
- * we don’t want to send [XHR](https://developer.mozilla.org/en/xmlhttprequest) or
- * [JSONP](http://en.wikipedia.org/wiki/JSONP) requests to a real server. All we really need is
- * to verify whether a certain request has been sent or not, or alternatively just let the
- * application make requests, respond with pre-trained responses and assert that the end result is
- * what we expect it to be.
- *
- * This mock implementation can be used to respond with static or dynamic responses via the
- * `expect` and `when` apis and their shortcuts (`expectGET`, `whenPOST`, etc).
- *
- * When an Angular application needs some data from a server, it calls the $http service, which
- * sends the request to a real server using $httpBackend service. With dependency injection, it is
- * easy to inject $httpBackend mock (which has the same API as $httpBackend) and use it to verify
- * the requests and respond with some testing data without sending a request to a real server.
- *
- * There are two ways to specify what test data should be returned as http responses by the mock
- * backend when the code under test makes http requests:
- *
- * - `$httpBackend.expect` - specifies a request expectation
- * - `$httpBackend.when` - specifies a backend definition
- *
- *
- * ## Request Expectations vs Backend Definitions
- *
- * Request expectations provide a way to make assertions about requests made by the application and
- * to define responses for those requests. The test will fail if the expected requests are not made
- * or they are made in the wrong order.
- *
- * Backend definitions allow you to define a fake backend for your application which doesn't assert
- * if a particular request was made or not, it just returns a trained response if a request is made.
- * The test will pass whether or not the request gets made during testing.
- *
- *
- * <table class="table">
- *   <tr><th width="220px"></th><th>Request expectations</th><th>Backend definitions</th></tr>
- *   <tr>
- *     <th>Syntax</th>
- *     <td>.expect(...).respond(...)</td>
- *     <td>.when(...).respond(...)</td>
- *   </tr>
- *   <tr>
- *     <th>Typical usage</th>
- *     <td>strict unit tests</td>
- *     <td>loose (black-box) unit testing</td>
- *   </tr>
- *   <tr>
- *     <th>Fulfills multiple requests</th>
- *     <td>NO</td>
- *     <td>YES</td>
- *   </tr>
- *   <tr>
- *     <th>Order of requests matters</th>
- *     <td>YES</td>
- *     <td>NO</td>
- *   </tr>
- *   <tr>
- *     <th>Request required</th>
- *     <td>YES</td>
- *     <td>NO</td>
- *   </tr>
- *   <tr>
- *     <th>Response required</th>
- *     <td>optional (see below)</td>
- *     <td>YES</td>
- *   </tr>
- * </table>
- *
- * In cases where both backend definitions and request expectations are specified during unit
- * testing, the request expectations are evaluated first.
- *
- * If a request expectation has no response specified, the algorithm will search your backend
- * definitions for an appropriate response.
- *
- * If a request didn't match any expectation or if the expectation doesn't have the response
- * defined, the backend definitions are evaluated in sequential order to see if any of them match
- * the request. The response from the first matched definition is returned.
- *
- *
- * ## Flushing HTTP requests
- *
- * The $httpBackend used in production always responds to requests asynchronously. If we preserved
- * this behavior in unit testing, we'd have to create async unit tests, which are hard to write,
- * to follow and to maintain. But neither can the testing mock respond synchronously; that would
- * change the execution of the code under test. For this reason, the mock $httpBackend has a
- * `flush()` method, which allows the test to explicitly flush pending requests. This preserves
- * the async api of the backend, while allowing the test to execute synchronously.
- *
- *
- * ## Unit testing with mock $httpBackend
- * The following code shows how to setup and use the mock backend when unit testing a controller.
- * First we create the controller under test:
- *
-  ```js
-  // The module code
-  angular
-    .module('MyApp', [])
-    .controller('MyController', MyController);
-
-  // The controller code
-  function MyController($scope, $http) {
-    var authToken;
-
-    $http.get('/auth.py').then(function(response) {
-      authToken = response.headers('A-Token');
-      $scope.user = response.data;
-    });
-
-    $scope.saveMessage = function(message) {
-      var headers = { 'Authorization': authToken };
-      $scope.status = 'Saving...';
-
-      $http.post('/add-msg.py', message, { headers: headers } ).then(function(response) {
-        $scope.status = '';
-      }).catch(function() {
-        $scope.status = 'Failed...';
-      });
-    };
-  }
-  ```
- *
- * Now we setup the mock backend and create the test specs:
- *
-  ```js
-    // testing controller
-    describe('MyController', function() {
-       var $httpBackend, $rootScope, createController, authRequestHandler;
-
-       // Set up the module
-       beforeEach(module('MyApp'));
-
-       beforeEach(inject(function($injector) {
-         // Set up the mock http service responses
-         $httpBackend = $injector.get('$httpBackend');
-         // backend definition common for all tests
-         authRequestHandler = $httpBackend.when('GET', '/auth.py')
-                                .respond({userId: 'userX'}, {'A-Token': 'xxx'});
-
-         // Get hold of a scope (i.e. the root scope)
-         $rootScope = $injector.get('$rootScope');
-         // The $controller service is used to create instances of controllers
-         var $controller = $injector.get('$controller');
-
-         createController = function() {
-           return $controller('MyController', {'$scope' : $rootScope });
-         };
-       }));
-
-
-       afterEach(function() {
-         $httpBackend.verifyNoOutstandingExpectation();
-         $httpBackend.verifyNoOutstandingRequest();
-       });
-
-
-       it('should fetch authentication token', function() {
-         $httpBackend.expectGET('/auth.py');
-         var controller = createController();
-         $httpBackend.flush();
-       });
-
-
-       it('should fail authentication', function() {
-
-         // Notice how you can change the response even after it was set
-         authRequestHandler.respond(401, '');
-
-         $httpBackend.expectGET('/auth.py');
-         var controller = createController();
-         $httpBackend.flush();
-         expect($rootScope.status).toBe('Failed...');
-       });
-
-
-       it('should send msg to server', function() {
-         var controller = createController();
-         $httpBackend.flush();
-
-         // now you don’t care about the authentication, but
-         // the controller will still send the request and
-         // $httpBackend will respond without you having to
-         // specify the expectation and response for this request
-
-         $httpBackend.expectPOST('/add-msg.py', 'message content').respond(201, '');
-         $rootScope.saveMessage('message content');
-         expect($rootScope.status).toBe('Saving...');
-         $httpBackend.flush();
-         expect($rootScope.status).toBe('');
-       });
-
-
-       it('should send auth header', function() {
-         var controller = createController();
-         $httpBackend.flush();
-
-         $httpBackend.expectPOST('/add-msg.py', undefined, function(headers) {
-           // check if the header was sent, if it wasn't the expectation won't
-           // match the request and the test will fail
-           return headers['Authorization'] === 'xxx';
-         }).respond(201, '');
-
-         $rootScope.saveMessage('whatever');
-         $httpBackend.flush();
-       });
-    });
-  ```
- *
- * ## Dynamic responses
- *
- * You define a response to a request by chaining a call to `respond()` onto a definition or expectation.
- * If you provide a **callback** as the first parameter to `respond(callback)` then you can dynamically generate
- * a response based on the properties of the request.
- *
- * The `callback` function should be of the form `function(method, url, data, headers, params)`.
- *
- * ### Query parameters
- *
- * By default, query parameters on request URLs are parsed into the `params` object. So a request URL
- * of `/list?q=searchstr&orderby=-name` would set `params` to be `{q: 'searchstr', orderby: '-name'}`.
- *
- * ### Regex parameter matching
- *
- * If an expectation or definition uses a **regex** to match the URL, you can provide an array of **keys** via a
- * `params` argument. The index of each **key** in the array will match the index of a **group** in the
- * **regex**.
- *
- * The `params` object in the **callback** will now have properties with these keys, which hold the value of the
- * corresponding **group** in the **regex**.
- *
- * This also applies to the `when` and `expect` shortcut methods.
- *
- *
- * ```js
- *   $httpBackend.expect('GET', /\/user\/(.+)/, undefined, undefined, ['id'])
- *     .respond(function(method, url, data, headers, params) {
- *       // for requested url of '/user/1234' params is {id: '1234'}
- *     });
- *
- *   $httpBackend.whenPATCH(/\/user\/(.+)\/article\/(.+)/, undefined, undefined, ['user', 'article'])
- *     .respond(function(method, url, data, headers, params) {
- *       // for url of '/user/1234/article/567' params is {user: '1234', article: '567'}
- *     });
- * ```
- *
- * ## Matching route requests
- *
- * For extra convenience, `whenRoute` and `expectRoute` shortcuts are available. These methods offer colon
- * delimited matching of the url path, ignoring the query string. This allows declarations
- * similar to how application routes are configured with `$routeProvider`. Because these methods convert
- * the definition url to regex, declaration order is important. Combined with query parameter parsing,
- * the following is possible:
- *
-  ```js
-    $httpBackend.whenRoute('GET', '/users/:id')
-      .respond(function(method, url, data, headers, params) {
-        return [200, MockUserList[Number(params.id)]];
-      });
-
-    $httpBackend.whenRoute('GET', '/users')
-      .respond(function(method, url, data, headers, params) {
-        var userList = angular.copy(MockUserList),
-          defaultSort = 'lastName',
-          count, pages, isPrevious, isNext;
-
-        // paged api response '/v1/users?page=2'
-        params.page = Number(params.page) || 1;
-
-        // query for last names '/v1/users?q=Archer'
-        if (params.q) {
-          userList = $filter('filter')({lastName: params.q});
-        }
-
-        pages = Math.ceil(userList.length / pagingLength);
-        isPrevious = params.page > 1;
-        isNext = params.page < pages;
-
-        return [200, {
-          count:    userList.length,
-          previous: isPrevious,
-          next:     isNext,
-          // sort field -> '/v1/users?sortBy=firstName'
-          results:  $filter('orderBy')(userList, params.sortBy || defaultSort)
-                      .splice((params.page - 1) * pagingLength, pagingLength)
-        }];
-      });
-  ```
- */
-angular.mock.$HttpBackendProvider = function() {
-  this.$get = ['$rootScope', '$timeout', createHttpBackendMock];
-};
-
-/**
- * General factory function for $httpBackend mock.
- * Returns instance for unit testing (when no arguments specified):
- *   - passing through is disabled
- *   - auto flushing is disabled
- *
- * Returns instance for e2e testing (when `$delegate` and `$browser` specified):
- *   - passing through (delegating request to real backend) is enabled
- *   - auto flushing is enabled
- *
- * @param {Object=} $delegate Real $httpBackend instance (allow passing through if specified)
- * @param {Object=} $browser Auto-flushing enabled if specified
- * @return {Object} Instance of $httpBackend mock
- */
-function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
-  var definitions = [],
-      expectations = [],
-      responses = [],
-      responsesPush = angular.bind(responses, responses.push),
-      copy = angular.copy;
-
-  function createResponse(status, data, headers, statusText) {
-    if (angular.isFunction(status)) return status;
-
-    return function() {
-      return angular.isNumber(status)
-          ? [status, data, headers, statusText]
-          : [200, status, data, headers];
-    };
-  }
-
-  // TODO(vojta): change params to: method, url, data, headers, callback
-  function $httpBackend(method, url, data, callback, headers, timeout, withCredentials, responseType, eventHandlers, uploadEventHandlers) {
-
-    var xhr = new MockXhr(),
-        expectation = expectations[0],
-        wasExpected = false;
-
-    xhr.$$events = eventHandlers;
-    xhr.upload.$$events = uploadEventHandlers;
-
-    function prettyPrint(data) {
-      return (angular.isString(data) || angular.isFunction(data) || data instanceof RegExp)
-          ? data
-          : angular.toJson(data);
-    }
-
-    function wrapResponse(wrapped) {
-      if (!$browser && timeout) {
-        if (timeout.then) {
-          timeout.then(handleTimeout);
-        } else {
-          $timeout(handleTimeout, timeout);
-        }
-      }
-
-      return handleResponse;
-
-      function handleResponse() {
-        var response = wrapped.response(method, url, data, headers, wrapped.params(url));
-        xhr.$$respHeaders = response[2];
-        callback(copy(response[0]), copy(response[1]), xhr.getAllResponseHeaders(),
-                 copy(response[3] || ''));
-      }
-
-      function handleTimeout() {
-        for (var i = 0, ii = responses.length; i < ii; i++) {
-          if (responses[i] === handleResponse) {
-            responses.splice(i, 1);
-            callback(-1, undefined, '');
-            break;
-          }
-        }
-      }
-    }
-
-    if (expectation && expectation.match(method, url)) {
-      if (!expectation.matchData(data)) {
-        throw new Error('Expected ' + expectation + ' with different data\n' +
-            'EXPECTED: ' + prettyPrint(expectation.data) + '\nGOT:      ' + data);
-      }
-
-      if (!expectation.matchHeaders(headers)) {
-        throw new Error('Expected ' + expectation + ' with different headers\n' +
-                        'EXPECTED: ' + prettyPrint(expectation.headers) + '\nGOT:      ' +
-                        prettyPrint(headers));
-      }
-
-      expectations.shift();
-
-      if (expectation.response) {
-        responses.push(wrapResponse(expectation));
-        return;
-      }
-      wasExpected = true;
-    }
-
-    var i = -1, definition;
-    while ((definition = definitions[++i])) {
-      if (definition.match(method, url, data, headers || {})) {
-        if (definition.response) {
-          // if $browser specified, we do auto flush all requests
-          ($browser ? $browser.defer : responsesPush)(wrapResponse(definition));
-        } else if (definition.passThrough) {
-          $delegate(method, url, data, callback, headers, timeout, withCredentials, responseType, eventHandlers, uploadEventHandlers);
-        } else throw new Error('No response defined !');
-        return;
-      }
-    }
-    throw wasExpected ?
-        new Error('No response defined !') :
-        new Error('Unexpected request: ' + method + ' ' + url + '\n' +
-                  (expectation ? 'Expected ' + expectation : 'No more request expected'));
-  }
-
-  /**
-   * @ngdoc method
-   * @name $httpBackend#when
-   * @description
-   * Creates a new backend definition.
-   *
-   * @param {string} method HTTP method.
-   * @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
-   *   and returns true if the url matches the current definition.
-   * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
-   *   data string and returns true if the data is as expected.
-   * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
-   *   object and returns true if the headers match the current definition.
-   * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
-   * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
-   *   request is handled. You can save this object for later use and invoke `respond` again in
-   *   order to change how a matched request is handled.
-   *
-   *  - respond –
-   *      ```js
-   *      {function([status,] data[, headers, statusText])
-   *      | function(function(method, url, data, headers, params)}
-   *      ```
-   *    – The respond method takes a set of static data to be returned or a function that can
-   *    return an array containing response status (number), response data (Array|Object|string),
-   *    response headers (Object), and the text for the status (string). The respond method returns
-   *    the `requestHandler` object for possible overrides.
-   */
-  $httpBackend.when = function(method, url, data, headers, keys) {
-
-    assertArgDefined(arguments, 1, 'url');
-
-    var definition = new MockHttpExpectation(method, url, data, headers, keys),
-        chain = {
-          respond: function(status, data, headers, statusText) {
-            definition.passThrough = undefined;
-            definition.response = createResponse(status, data, headers, statusText);
-            return chain;
-          }
-        };
-
-    if ($browser) {
-      chain.passThrough = function() {
-        definition.response = undefined;
-        definition.passThrough = true;
-        return chain;
-      };
-    }
-
-    definitions.push(definition);
-    return chain;
-  };
-
-  /**
-   * @ngdoc method
-   * @name $httpBackend#whenGET
-   * @description
-   * Creates a new backend definition for GET requests. For more info see `when()`.
-   *
-   * @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
-   *   and returns true if the url matches the current definition.
-   * @param {(Object|function(Object))=} headers HTTP headers.
-   * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
-   * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
-   * request is handled. You can save this object for later use and invoke `respond` again in
-   * order to change how a matched request is handled.
-   */
-
-  /**
-   * @ngdoc method
-   * @name $httpBackend#whenHEAD
-   * @description
-   * Creates a new backend definition for HEAD requests. For more info see `when()`.
-   *
-   * @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
-   *   and returns true if the url matches the current definition.
-   * @param {(Object|function(Object))=} headers HTTP headers.
-   * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
-   * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
-   * request is handled. You can save this object for later use and invoke `respond` again in
-   * order to change how a matched request is handled.
-   */
-
-  /**
-   * @ngdoc method
-   * @name $httpBackend#whenDELETE
-   * @description
-   * Creates a new backend definition for DELETE requests. For more info see `when()`.
-   *
-   * @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
-   *   and returns true if the url matches the current definition.
-   * @param {(Object|function(Object))=} headers HTTP headers.
-   * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
-   * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
-   * request is handled. You can save this object for later use and invoke `respond` again in
-   * order to change how a matched request is handled.
-   */
-
-  /**
-   * @ngdoc method
-   * @name $httpBackend#whenPOST
-   * @description
-   * Creates a new backend definition for POST requests. For more info see `when()`.
-   *
-   * @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
-   *   and returns true if the url matches the current definition.
-   * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
-   *   data string and returns true if the data is as expected.
-   * @param {(Object|function(Object))=} headers HTTP headers.
-   * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
-   * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
-   * request is handled. You can save this object for later use and invoke `respond` again in
-   * order to change how a matched request is handled.
-   */
-
-  /**
-   * @ngdoc method
-   * @name $httpBackend#whenPUT
-   * @description
-   * Creates a new backend definition for PUT requests.  For more info see `when()`.
-   *
-   * @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
-   *   and returns true if the url matches the current definition.
-   * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
-   *   data string and returns true if the data is as expected.
-   * @param {(Object|function(Object))=} headers HTTP headers.
-   * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
-   * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
-   * request is handled. You can save this object for later use and invoke `respond` again in
-   * order to change how a matched request is handled.
-   */
-
-  /**
-   * @ngdoc method
-   * @name $httpBackend#whenJSONP
-   * @description
-   * Creates a new backend definition for JSONP requests. For more info see `when()`.
-   *
-   * @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
-   *   and returns true if the url matches the current definition.
-   * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
-   * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
-   * request is handled. You can save this object for later use and invoke `respond` again in
-   * order to change how a matched request is handled.
-   */
-  createShortMethods('when');
-
-  /**
-   * @ngdoc method
-   * @name $httpBackend#whenRoute
-   * @description
-   * Creates a new backend definition that compares only with the requested route.
-   *
-   * @param {string} method HTTP method.
-   * @param {string} url HTTP url string that supports colon param matching.
-   * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
-   * request is handled. You can save this object for later use and invoke `respond` again in
-   * order to change how a matched request is handled. See #when for more info.
-   */
-  $httpBackend.whenRoute = function(method, url) {
-    var pathObj = parseRoute(url);
-    return $httpBackend.when(method, pathObj.regexp, undefined, undefined, pathObj.keys);
-  };
-
-  function parseRoute(url) {
-    var ret = {
-      regexp: url
-    },
-    keys = ret.keys = [];
-
-    if (!url || !angular.isString(url)) return ret;
-
-    url = url
-      .replace(/([().])/g, '\\$1')
-      .replace(/(\/)?:(\w+)([?*])?/g, function(_, slash, key, option) {
-        var optional = option === '?' ? option : null;
-        var star = option === '*' ? option : null;
-        keys.push({ name: key, optional: !!optional });
-        slash = slash || '';
-        return ''
-          + (optional ? '' : slash)
-          + '(?:'
-          + (optional ? slash : '')
-          + (star && '(.+?)' || '([^/]+)')
-          + (optional || '')
-          + ')'
-          + (optional || '');
-      })
-      .replace(/([/$*])/g, '\\$1');
-
-    ret.regexp = new RegExp('^' + url, 'i');
-    return ret;
-  }
-
-  /**
-   * @ngdoc method
-   * @name $httpBackend#expect
-   * @description
-   * Creates a new request expectation.
-   *
-   * @param {string} method HTTP method.
-   * @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
-   *   and returns true if the url matches the current definition.
-   * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
-   *  receives data string and returns true if the data is as expected, or Object if request body
-   *  is in JSON format.
-   * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
-   *   object and returns true if the headers match the current expectation.
-   * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
-   * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
-   *  request is handled. You can save this object for later use and invoke `respond` again in
-   *  order to change how a matched request is handled.
-   *
-   *  - respond –
-   *    ```
-   *    { function([status,] data[, headers, statusText])
-   *    | function(function(method, url, data, headers, params)}
-   *    ```
-   *    – The respond method takes a set of static data to be returned or a function that can
-   *    return an array containing response status (number), response data (Array|Object|string),
-   *    response headers (Object), and the text for the status (string). The respond method returns
-   *    the `requestHandler` object for possible overrides.
-   */
-  $httpBackend.expect = function(method, url, data, headers, keys) {
-
-    assertArgDefined(arguments, 1, 'url');
-
-    var expectation = new MockHttpExpectation(method, url, data, headers, keys),
-        chain = {
-          respond: function(status, data, headers, statusText) {
-            expectation.response = createResponse(status, data, headers, statusText);
-            return chain;
-          }
-        };
-
-    expectations.push(expectation);
-    return chain;
-  };
-
-  /**
-   * @ngdoc method
-   * @name $httpBackend#expectGET
-   * @description
-   * Creates a new request expectation for GET requests. For more info see `expect()`.
-   *
-   * @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
-   *   and returns true if the url matches the current definition.
-   * @param {Object=} headers HTTP headers.
-   * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
-   * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
-   * request is handled. You can save this object for later use and invoke `respond` again in
-   * order to change how a matched request is handled. See #expect for more info.
-   */
-
-  /**
-   * @ngdoc method
-   * @name $httpBackend#expectHEAD
-   * @description
-   * Creates a new request expectation for HEAD requests. For more info see `expect()`.
-   *
-   * @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
-   *   and returns true if the url matches the current definition.
-   * @param {Object=} headers HTTP headers.
-   * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
-   * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
-   *   request is handled. You can save this object for later use and invoke `respond` again in
-   *   order to change how a matched request is handled.
-   */
-
-  /**
-   * @ngdoc method
-   * @name $httpBackend#expectDELETE
-   * @description
-   * Creates a new request expectation for DELETE requests. For more info see `expect()`.
-   *
-   * @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
-   *   and returns true if the url matches the current definition.
-   * @param {Object=} headers HTTP headers.
-   * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
-   * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
-   *   request is handled. You can save this object for later use and invoke `respond` again in
-   *   order to change how a matched request is handled.
-   */
-
-  /**
-   * @ngdoc method
-   * @name $httpBackend#expectPOST
-   * @description
-   * Creates a new request expectation for POST requests. For more info see `expect()`.
-   *
-   * @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
-   *   and returns true if the url matches the current definition.
-   * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
-   *  receives data string and returns true if the data is as expected, or Object if request body
-   *  is in JSON format.
-   * @param {Object=} headers HTTP headers.
-   * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
-   * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
-   *   request is handled. You can save this object for later use and invoke `respond` again in
-   *   order to change how a matched request is handled.
-   */
-
-  /**
-   * @ngdoc method
-   * @name $httpBackend#expectPUT
-   * @description
-   * Creates a new request expectation for PUT requests. For more info see `expect()`.
-   *
-   * @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
-   *   and returns true if the url matches the current definition.
-   * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
-   *  receives data string and returns true if the data is as expected, or Object if request body
-   *  is in JSON format.
-   * @param {Object=} headers HTTP headers.
-   * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
-   * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
-   *   request is handled. You can save this object for later use and invoke `respond` again in
-   *   order to change how a matched request is handled.
-   */
-
-  /**
-   * @ngdoc method
-   * @name $httpBackend#expectPATCH
-   * @description
-   * Creates a new request expectation for PATCH requests. For more info see `expect()`.
-   *
-   * @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
-   *   and returns true if the url matches the current definition.
-   * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
-   *  receives data string and returns true if the data is as expected, or Object if request body
-   *  is in JSON format.
-   * @param {Object=} headers HTTP headers.
-   * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
-   * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
-   *   request is handled. You can save this object for later use and invoke `respond` again in
-   *   order to change how a matched request is handled.
-   */
-
-  /**
-   * @ngdoc method
-   * @name $httpBackend#expectJSONP
-   * @description
-   * Creates a new request expectation for JSONP requests. For more info see `expect()`.
-   *
-   * @param {string|RegExp|function(string)=} url HTTP url or function that receives an url
-   *   and returns true if the url matches the current definition.
-   * @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
-   * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
-   *   request is handled. You can save this object for later use and invoke `respond` again in
-   *   order to change how a matched request is handled.
-   */
-  createShortMethods('expect');
-
-  /**
-   * @ngdoc method
-   * @name $httpBackend#expectRoute
-   * @description
-   * Creates a new request expectation that compares only with the requested route.
-   *
-   * @param {string} method HTTP method.
-   * @param {string} url HTTP url string that supports colon param matching.
-   * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
-   * request is handled. You can save this object for later use and invoke `respond` again in
-   * order to change how a matched request is handled. See #expect for more info.
-   */
-  $httpBackend.expectRoute = function(method, url) {
-    var pathObj = parseRoute(url);
-    return $httpBackend.expect(method, pathObj.regexp, undefined, undefined, pathObj.keys);
-  };
-
-
-  /**
-   * @ngdoc method
-   * @name $httpBackend#flush
-   * @description
-   * Flushes pending requests using the trained responses. Requests are flushed in the order they
-   * were made, but it is also possible to skip one or more requests (for example to have them
-   * flushed later). This is useful for simulating scenarios where responses arrive from the server
-   * in any order.
-   *
-   * If there are no pending requests to flush when the method is called, an exception is thrown (as
-   * this is typically a sign of programming error).
-   *
-   * @param {number=} count - Number of responses to flush. If undefined/null, all pending requests
-   *     (starting after `skip`) will be flushed.
-   * @param {number=} [skip=0] - Number of pending requests to skip. For example, a value of `5`
-   *     would skip the first 5 pending requests and start flushing from the 6th onwards.
-   */
-  $httpBackend.flush = function(count, skip, digest) {
-    if (digest !== false) $rootScope.$digest();
-
-    skip = skip || 0;
-    if (skip >= responses.length) throw new Error('No pending request to flush !');
-
-    if (angular.isDefined(count) && count !== null) {
-      while (count--) {
-        var part = responses.splice(skip, 1);
-        if (!part.length) throw new Error('No more pending request to flush !');
-        part[0]();
-      }
-    } else {
-      while (responses.length > skip) {
-        responses.splice(skip, 1)[0]();
-      }
-    }
-    $httpBackend.verifyNoOutstandingExpectation(digest);
-  };
-
-
-  /**
-   * @ngdoc method
-   * @name $httpBackend#verifyNoOutstandingExpectation
-   * @description
-   * Verifies that all of the requests defined via the `expect` api were made. If any of the
-   * requests were not made, verifyNoOutstandingExpectation throws an exception.
-   *
-   * Typically, you would call this method following each test case that asserts requests using an
-   * "afterEach" clause.
-   *
-   * ```js
-   *   afterEach($httpBackend.verifyNoOutstandingExpectation);
-   * ```
-   */
-  $httpBackend.verifyNoOutstandingExpectation = function(digest) {
-    if (digest !== false) $rootScope.$digest();
-    if (expectations.length) {
-      throw new Error('Unsatisfied requests: ' + expectations.join(', '));
-    }
-  };
-
-
-  /**
-   * @ngdoc method
-   * @name $httpBackend#verifyNoOutstandingRequest
-   * @description
-   * Verifies that there are no outstanding requests that need to be flushed.
-   *
-   * Typically, you would call this method following each test case that asserts requests using an
-   * "afterEach" clause.
-   *
-   * ```js
-   *   afterEach($httpBackend.verifyNoOutstandingRequest);
-   * ```
-   */
-  $httpBackend.verifyNoOutstandingRequest = function(digest) {
-    if (digest !== false) $rootScope.$digest();
-    if (responses.length) {
-      throw new Error('Unflushed requests: ' + responses.length);
-    }
-  };
-
-
-  /**
-   * @ngdoc method
-   * @name $httpBackend#resetExpectations
-   * @description
-   * Resets all request expectations, but preserves all backend definitions. Typically, you would
-   * call resetExpectations during a multiple-phase test when you want to reuse the same instance of
-   * $httpBackend mock.
-   */
-  $httpBackend.resetExpectations = function() {
-    expectations.length = 0;
-    responses.length = 0;
-  };
-
-  return $httpBackend;
-
-
-  function createShortMethods(prefix) {
-    angular.forEach(['GET', 'DELETE', 'JSONP', 'HEAD'], function(method) {
-     $httpBackend[prefix + method] = function(url, headers, keys) {
-        assertArgDefined(arguments, 0, 'url');
-
-        // Change url to `null` if `undefined` to stop it throwing an exception further down
-        if (angular.isUndefined(url)) url = null;
-
-       return $httpBackend[prefix](method, url, undefined, headers, keys);
-     };
-    });
-
-    angular.forEach(['PUT', 'POST', 'PATCH'], function(method) {
-      $httpBackend[prefix + method] = function(url, data, headers, keys) {
-        assertArgDefined(arguments, 0, 'url');
-
-        // Change url to `null` if `undefined` to stop it throwing an exception further down
-        if (angular.isUndefined(url)) url = null;
-
-        return $httpBackend[prefix](method, url, data, headers, keys);
-      };
-    });
-  }
-}
-
-function assertArgDefined(args, index, name) {
-  if (args.length > index && angular.isUndefined(args[index])) {
-    throw new Error('Undefined argument `' + name + '`; the argument is provided but not defined');
-  }
-}
-
-
-function MockHttpExpectation(method, url, data, headers, keys) {
-
-  function getUrlParams(u) {
-    var params = u.slice(u.indexOf('?') + 1).split('&');
-    return params.sort();
-  }
-
-  function compareUrl(u) {
-    return (url.slice(0, url.indexOf('?')) === u.slice(0, u.indexOf('?')) &&
-      getUrlParams(url).join() === getUrlParams(u).join());
-  }
-
-  this.data = data;
-  this.headers = headers;
-
-  this.match = function(m, u, d, h) {
-    if (method !== m) return false;
-    if (!this.matchUrl(u)) return false;
-    if (angular.isDefined(d) && !this.matchData(d)) return false;
-    if (angular.isDefined(h) && !this.matchHeaders(h)) return false;
-    return true;
-  };
-
-  this.matchUrl = function(u) {
-    if (!url) return true;
-    if (angular.isFunction(url.test)) return url.test(u);
-    if (angular.isFunction(url)) return url(u);
-    return (url === u || compareUrl(u));
-  };
-
-  this.matchHeaders = function(h) {
-    if (angular.isUndefined(headers)) return true;
-    if (angular.isFunction(headers)) return headers(h);
-    return angular.equals(headers, h);
-  };
-
-  this.matchData = function(d) {
-    if (angular.isUndefined(data)) return true;
-    if (data && angular.isFunction(data.test)) return data.test(d);
-    if (data && angular.isFunction(data)) return data(d);
-    if (data && !angular.isString(data)) {
-      return angular.equals(angular.fromJson(angular.toJson(data)), angular.fromJson(d));
-    }
-    // eslint-disable-next-line eqeqeq
-    return data == d;
-  };
-
-  this.toString = function() {
-    return method + ' ' + url;
-  };
-
-  this.params = function(u) {
-    return angular.extend(parseQuery(), pathParams());
-
-    function pathParams() {
-      var keyObj = {};
-      if (!url || !angular.isFunction(url.test) || !keys || keys.length === 0) return keyObj;
-
-      var m = url.exec(u);
-      if (!m) return keyObj;
-      for (var i = 1, len = m.length; i < len; ++i) {
-        var key = keys[i - 1];
-        var val = m[i];
-        if (key && val) {
-          keyObj[key.name || key] = val;
-        }
-      }
-
-      return keyObj;
-    }
-
-    function parseQuery() {
-      var obj = {}, key_value, key,
-          queryStr = u.indexOf('?') > -1
-          ? u.substring(u.indexOf('?') + 1)
-          : '';
-
-      angular.forEach(queryStr.split('&'), function(keyValue) {
-        if (keyValue) {
-          key_value = keyValue.replace(/\+/g,'%20').split('=');
-          key = tryDecodeURIComponent(key_value[0]);
-          if (angular.isDefined(key)) {
-            var val = angular.isDefined(key_value[1]) ? tryDecodeURIComponent(key_value[1]) : true;
-            if (!hasOwnProperty.call(obj, key)) {
-              obj[key] = val;
-            } else if (angular.isArray(obj[key])) {
-              obj[key].push(val);
-            } else {
-              obj[key] = [obj[key],val];
-            }
-          }
-        }
-      });
-      return obj;
-    }
-    function tryDecodeURIComponent(value) {
-      try {
-        return decodeURIComponent(value);
-      } catch (e) {
-        // Ignore any invalid uri component
-      }
-    }
-  };
-}
-
-function createMockXhr() {
-  return new MockXhr();
-}
-
-function MockXhr() {
-
-  // hack for testing $http, $httpBackend
-  MockXhr.$$lastInstance = this;
-
-  this.open = function(method, url, async) {
-    this.$$method = method;
-    this.$$url = url;
-    this.$$async = async;
-    this.$$reqHeaders = {};
-    this.$$respHeaders = {};
-  };
-
-  this.send = function(data) {
-    this.$$data = data;
-  };
-
-  this.setRequestHeader = function(key, value) {
-    this.$$reqHeaders[key] = value;
-  };
-
-  this.getResponseHeader = function(name) {
-    // the lookup must be case insensitive,
-    // that's why we try two quick lookups first and full scan last
-    var header = this.$$respHeaders[name];
-    if (header) return header;
-
-    name = angular.lowercase(name);
-    header = this.$$respHeaders[name];
-    if (header) return header;
-
-    header = undefined;
-    angular.forEach(this.$$respHeaders, function(headerVal, headerName) {
-      if (!header && angular.lowercase(headerName) === name) header = headerVal;
-    });
-    return header;
-  };
-
-  this.getAllResponseHeaders = function() {
-    var lines = [];
-
-    angular.forEach(this.$$respHeaders, function(value, key) {
-      lines.push(key + ': ' + value);
-    });
-    return lines.join('\n');
-  };
-
-  this.abort = angular.noop;
-
-  // This section simulates the events on a real XHR object (and the upload object)
-  // When we are testing $httpBackend (inside the angular project) we make partial use of this
-  // but store the events directly ourselves on `$$events`, instead of going through the `addEventListener`
-  this.$$events = {};
-  this.addEventListener = function(name, listener) {
-    if (angular.isUndefined(this.$$events[name])) this.$$events[name] = [];
-    this.$$events[name].push(listener);
-  };
-
-  this.upload = {
-    $$events: {},
-    addEventListener: this.addEventListener
-  };
-}
-
-
-/**
- * @ngdoc service
- * @name $timeout
- * @description
- *
- * This service is just a simple decorator for {@link ng.$timeout $timeout} service
- * that adds a "flush" and "verifyNoPendingTasks" methods.
- */
-
-angular.mock.$TimeoutDecorator = ['$delegate', '$browser', function($delegate, $browser) {
-
-  /**
-   * @ngdoc method
-   * @name $timeout#flush
-   * @description
-   *
-   * Flushes the queue of pending tasks.
-   *
-   * @param {number=} delay maximum timeout amount to flush up until
-   */
-  $delegate.flush = function(delay) {
-    $browser.defer.flush(delay);
-  };
-
-  /**
-   * @ngdoc method
-   * @name $timeout#verifyNoPendingTasks
-   * @description
-   *
-   * Verifies that there are no pending tasks that need to be flushed.
-   */
-  $delegate.verifyNoPendingTasks = function() {
-    if ($browser.deferredFns.length) {
-      throw new Error('Deferred tasks to flush (' + $browser.deferredFns.length + '): ' +
-          formatPendingTasksAsString($browser.deferredFns));
-    }
-  };
-
-  function formatPendingTasksAsString(tasks) {
-    var result = [];
-    angular.forEach(tasks, function(task) {
-      result.push('{id: ' + task.id + ', time: ' + task.time + '}');
-    });
-
-    return result.join(', ');
-  }
-
-  return $delegate;
-}];
-
-angular.mock.$RAFDecorator = ['$delegate', function($delegate) {
-  var rafFn = function(fn) {
-    var index = rafFn.queue.length;
-    rafFn.queue.push(fn);
-    return function() {
-      rafFn.queue.splice(index, 1);
-    };
-  };
-
-  rafFn.queue = [];
-  rafFn.supported = $delegate.supported;
-
-  rafFn.flush = function() {
-    if (rafFn.queue.length === 0) {
-      throw new Error('No rAF callbacks present');
-    }
-
-    var length = rafFn.queue.length;
-    for (var i = 0; i < length; i++) {
-      rafFn.queue[i]();
-    }
-
-    rafFn.queue = rafFn.queue.slice(i);
-  };
-
-  return rafFn;
-}];
-
-/**
- *
- */
-var originalRootElement;
-angular.mock.$RootElementProvider = function() {
-  this.$get = ['$injector', function($injector) {
-    originalRootElement = angular.element('<div ng-app></div>').data('$injector', $injector);
-    return originalRootElement;
-  }];
-};
-
-/**
- * @ngdoc service
- * @name $controller
- * @description
- * A decorator for {@link ng.$controller} with additional `bindings` parameter, useful when testing
- * controllers of directives that use {@link $compile#-bindtocontroller- `bindToController`}.
- *
- * Depending on the value of
- * {@link ng.$compileProvider#preAssignBindingsEnabled `preAssignBindingsEnabled()`}, the properties
- * will be bound before or after invoking the constructor.
- *
- *
- * ## Example
- *
- * ```js
- *
- * // Directive definition ...
- *
- * myMod.directive('myDirective', {
- *   controller: 'MyDirectiveController',
- *   bindToController: {
- *     name: '@'
- *   }
- * });
- *
- *
- * // Controller definition ...
- *
- * myMod.controller('MyDirectiveController', ['$log', function($log) {
- *   this.log = function() {
- *     $log.info(this.name);
- *   };
- * }]);
- *
- *
- * // In a test ...
- *
- * describe('myDirectiveController', function() {
- *   describe('log()', function() {
- *     it('should write the bound name to the log', inject(function($controller, $log) {
- *       var ctrl = $controller('MyDirectiveController', { /* no locals &#42;/ }, { name: 'Clark Kent' });
- *       ctrl.log();
- *
- *       expect(ctrl.name).toEqual('Clark Kent');
- *       expect($log.info.logs).toEqual(['Clark Kent']);
- *     }));
- *   });
- * });
- *
- * ```
- *
- * @param {Function|string} constructor If called with a function then it's considered to be the
- *    controller constructor function. Otherwise it's considered to be a string which is used
- *    to retrieve the controller constructor using the following steps:
- *
- *    * check if a controller with given name is registered via `$controllerProvider`
- *    * check if evaluating the string on the current scope returns a constructor
- *    * if $controllerProvider#allowGlobals, check `window[constructor]` on the global
- *      `window` object (deprecated, not recommended)
- *
- *    The string can use the `controller as property` syntax, where the controller instance is published
- *    as the specified property on the `scope`; the `scope` must be injected into `locals` param for this
- *    to work correctly.
- *
- * @param {Object} locals Injection locals for Controller.
- * @param {Object=} bindings Properties to add to the controller instance. This is used to simulate
- *                           the `bindToController` feature and simplify certain kinds of tests.
- * @return {Object} Instance of given controller.
- */
-function createControllerDecorator(compileProvider) {
-  angular.mock.$ControllerDecorator = ['$delegate', function($delegate) {
-    return function(expression, locals, later, ident) {
-      if (later && typeof later === 'object') {
-        var preAssignBindingsEnabled = compileProvider.preAssignBindingsEnabled();
-
-        var instantiate = $delegate(expression, locals, true, ident);
-        if (preAssignBindingsEnabled) {
-          angular.extend(instantiate.instance, later);
-        }
-
-        var instance = instantiate();
-        if (!preAssignBindingsEnabled || instance !== instantiate.instance) {
-          angular.extend(instance, later);
-        }
-
-        return instance;
-      }
-      return $delegate(expression, locals, later, ident);
-    };
-  }];
-
-  return angular.mock.$ControllerDecorator;
-}
-
-/**
- * @ngdoc service
- * @name $componentController
- * @description
- * A service that can be used to create instances of component controllers. Useful for unit-testing.
- *
- * Be aware that the controller will be instantiated and attached to the scope as specified in
- * the component definition object. If you do not provide a `$scope` object in the `locals` param
- * then the helper will create a new isolated scope as a child of `$rootScope`.
- *
- * If you are using `$element` or `$attrs` in the controller, make sure to provide them as `locals`.
- * The `$element` must be a jqLite-wrapped DOM element, and `$attrs` should be an object that
- * has all properties / functions that you are using in the controller. If this is getting too complex,
- * you should compile the component instead and access the component's controller via the
- * {@link angular.element#methods `controller`} function.
- *
- * See also the section on {@link guide/component#unit-testing-component-controllers unit-testing component controllers}
- * in the guide.
- *
- * @param {string} componentName the name of the component whose controller we want to instantiate
- * @param {Object} locals Injection locals for Controller.
- * @param {Object=} bindings Properties to add to the controller before invoking the constructor. This is used
- *                           to simulate the `bindToController` feature and simplify certain kinds of tests.
- * @param {string=} ident Override the property name to use when attaching the controller to the scope.
- * @return {Object} Instance of requested controller.
- */
-angular.mock.$ComponentControllerProvider = ['$compileProvider',
-    function ComponentControllerProvider($compileProvider) {
-  this.$get = ['$controller','$injector', '$rootScope', function($controller, $injector, $rootScope) {
-    return function $componentController(componentName, locals, bindings, ident) {
-      // get all directives associated to the component name
-      var directives = $injector.get(componentName + 'Directive');
-      // look for those directives that are components
-      var candidateDirectives = directives.filter(function(directiveInfo) {
-        // components have controller, controllerAs and restrict:'E'
-        return directiveInfo.controller && directiveInfo.controllerAs && directiveInfo.restrict === 'E';
-      });
-      // check if valid directives found
-      if (candidateDirectives.length === 0) {
-        throw new Error('No component found');
-      }
-      if (candidateDirectives.length > 1) {
-        throw new Error('Too many components found');
-      }
-      // get the info of the component
-      var directiveInfo = candidateDirectives[0];
-      // create a scope if needed
-      locals = locals || {};
-      locals.$scope = locals.$scope || $rootScope.$new(true);
-      return $controller(directiveInfo.controller, locals, bindings, ident || directiveInfo.controllerAs);
-    };
-  }];
-}];
-
-
-/**
- * @ngdoc module
- * @name ngMock
- * @packageName angular-mocks
- * @description
- *
- * # ngMock
- *
- * The `ngMock` module provides support to inject and mock Angular services into unit tests.
- * In addition, ngMock also extends various core ng services such that they can be
- * inspected and controlled in a synchronous manner within test code.
- *
- *
- * <div doc-module-components="ngMock"></div>
- *
- * @installation
- *
- *  First, download the file:
- *  * [Google CDN](https://developers.google.com/speed/libraries/devguide#angularjs) e.g.
- *    `"//ajax.googleapis.com/ajax/libs/angularjs/X.Y.Z/angular-mocks.js"`
- *  * [NPM](https://www.npmjs.com/) e.g. `npm install angular-mocks@X.Y.Z`
- *  * [Yarn](https://yarnpkg.com) e.g. `yarn add angular-mocks@X.Y.Z`
- *  * [Bower](http://bower.io) e.g. `bower install angular-mocks#X.Y.Z`
- *  * [code.angularjs.org](https://code.angularjs.org/) (discouraged for production use)  e.g.
- *    `"//code.angularjs.org/X.Y.Z/angular-mocks.js"`
- *
- * where X.Y.Z is the AngularJS version you are running.
- *
- * Then, configure your test runner to load `angular-mocks.js` after `angular.js`.
- * This example uses <a href="http://karma-runner.github.io/">Karma</a>:
- *
- * ```
- * config.set({
- *   files: [
- *     'build/angular.js', // and other module files you need
- *     'build/angular-mocks.js',
- *     '<path/to/application/files>',
- *     '<path/to/spec/files>'
- *   ]
- * });
- * ```
- *
- * Including the `angular-mocks.js` file automatically adds the `ngMock` module, so your tests
- *  are ready to go!
- */
-angular.module('ngMock', ['ng']).provider({
-  $browser: angular.mock.$BrowserProvider,
-  $exceptionHandler: angular.mock.$ExceptionHandlerProvider,
-  $log: angular.mock.$LogProvider,
-  $interval: angular.mock.$IntervalProvider,
-  $httpBackend: angular.mock.$HttpBackendProvider,
-  $rootElement: angular.mock.$RootElementProvider,
-  $componentController: angular.mock.$ComponentControllerProvider
-}).config(['$provide', '$compileProvider', function($provide, $compileProvider) {
-  $provide.decorator('$timeout', angular.mock.$TimeoutDecorator);
-  $provide.decorator('$$rAF', angular.mock.$RAFDecorator);
-  $provide.decorator('$rootScope', angular.mock.$RootScopeDecorator);
-  $provide.decorator('$controller', createControllerDecorator($compileProvider));
-}]);
-
-/**
- * @ngdoc module
- * @name ngMockE2E
- * @module ngMockE2E
- * @packageName angular-mocks
- * @description
- *
- * The `ngMockE2E` is an angular module which contains mocks suitable for end-to-end testing.
- * Currently there is only one mock present in this module -
- * the {@link ngMockE2E.$httpBackend e2e $httpBackend} mock.
- */
-angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
-  $provide.value('$httpBackend', angular.injector(['ng']).get('$httpBackend'));
-  $provide.decorator('$httpBackend', angular.mock.e2e.$httpBackendDecorator);
-}]);
-
-/**
- * @ngdoc service
- * @name $httpBackend
- * @module ngMockE2E
- * @description
- * Fake HTTP backend implementation suitable for end-to-end testing or backend-less development of
- * applications that use the {@link ng.$http $http service}.
- *
- * <div class="alert alert-info">
- * **Note**: For fake http backend implementation suitable for unit testing please see
- * {@link ngMock.$httpBackend unit-testing $httpBackend mock}.
- * </div>
- *
- * This implementation can be used to respond with static or dynamic responses via the `when` api
- * and its shortcuts (`whenGET`, `whenPOST`, etc) and optionally pass through requests to the
- * real $httpBackend for specific requests (e.g. to interact with certain remote apis or to fetch
- * templates from a webserver).
- *
- * As opposed to unit-testing, in an end-to-end testing scenario or in scenario when an application
- * is being developed with the real backend api replaced with a mock, it is often desirable for
- * certain category of requests to bypass the mock and issue a real http request (e.g. to fetch
- * templates or static files from the webserver). To configure the backend with this behavior
- * use the `passThrough` request handler of `when` instead of `respond`.
- *
- * Additionally, we don't want to manually have to flush mocked out requests like we do during unit
- * testing. For this reason the e2e $httpBackend flushes mocked out requests
- * automatically, closely simulating the behavior of the XMLHttpRequest object.
- *
- * To setup the application to run with this http backend, you have to create a module that depends
- * on the `ngMockE2E` and your application modules and defines the fake backend:
- *
- * ```js
- *   var myAppDev = angular.module('myAppDev', ['myApp', 'ngMockE2E']);
- *   myAppDev.run(function($httpBackend) {
- *     var phones = [{name: 'phone1'}, {name: 'phone2'}];
- *
- *     // returns the current list of phones
- *     $httpBackend.whenGET('/phones').respond(phones);
- *
- *     // adds a new phone to the phones array
- *     $httpBackend.whenPOST('/phones').respond(function(method, url, data) {
- *       var phone = angular.fromJson(data);
- *       phones.push(phone);
- *       return [200, phone, {}];
- *     });
- *     $httpBackend.whenGET(/^\/templates\//).passThrough(); // Requests for templates are handled by the real server
- *     //...
- *   });
- * ```
- *
- * Afterwards, bootstrap your app with this new module.
- *
- * ## Example
- * <example name="httpbackend-e2e-testing" module="myAppE2E" deps="angular-mocks.js">
- * <file name="app.js">
- *   var myApp = angular.module('myApp', []);
- *
- *   myApp.controller('MainCtrl', function MainCtrl($http) {
- *     var ctrl = this;
- *
- *     ctrl.phones = [];
- *     ctrl.newPhone = {
- *       name: ''
- *     };
- *
- *     ctrl.getPhones = function() {
- *       $http.get('/phones').then(function(response) {
- *         ctrl.phones = response.data;
- *       });
- *     };
- *
- *     ctrl.addPhone = function(phone) {
- *       $http.post('/phones', phone).then(function() {
- *         ctrl.newPhone = {name: ''};
- *         return ctrl.getPhones();
- *       });
- *     };
- *
- *     ctrl.getPhones();
- *   });
- * </file>
- * <file name="e2e.js">
- *   var myAppDev = angular.module('myAppE2E', ['myApp', 'ngMockE2E']);
- *
- *   myAppDev.run(function($httpBackend) {
- *     var phones = [{name: 'phone1'}, {name: 'phone2'}];
- *
- *     // returns the current list of phones
- *     $httpBackend.whenGET('/phones').respond(phones);
- *
- *     // adds a new phone to the phones array
- *     $httpBackend.whenPOST('/phones').respond(function(method, url, data) {
- *       var phone = angular.fromJson(data);
- *       phones.push(phone);
- *       return [200, phone, {}];
- *     });
- *   });
- * </file>
- * <file name="index.html">
- *   <div ng-controller="MainCtrl as $ctrl">
- *   <form name="newPhoneForm" ng-submit="$ctrl.addPhone($ctrl.newPhone)">
- *     <input type="text" ng-model="$ctrl.newPhone.name">
- *     <input type="submit" value="Add Phone">
- *   </form>
- *   <h1>Phones</h1>
- *   <ul>
- *     <li ng-repeat="phone in $ctrl.phones">{{phone.name}}</li>
- *   </ul>
- *   </div>
- * </file>
- * </example>
- *
- *
- */
-
-/**
- * @ngdoc method
- * @name $httpBackend#when
- * @module ngMockE2E
- * @description
- * Creates a new backend definition.
- *
- * @param {string} method HTTP method.
- * @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
- *   and returns true if the url matches the current definition.
- * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
- *   data string and returns true if the data is as expected.
- * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
- *   object and returns true if the headers match the current definition.
- * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
- *   {@link ngMock.$httpBackend $httpBackend mock}.
- * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
- *   control how a matched request is handled. You can save this object for later use and invoke
- *   `respond` or `passThrough` again in order to change how a matched request is handled.
- *
- *  - respond –
- *    ```
- *    { function([status,] data[, headers, statusText])
- *    | function(function(method, url, data, headers, params)}
- *    ```
- *    – The respond method takes a set of static data to be returned or a function that can return
- *    an array containing response status (number), response data (Array|Object|string), response
- *    headers (Object), and the text for the status (string).
- *  - passThrough – `{function()}` – Any request matching a backend definition with
- *    `passThrough` handler will be passed through to the real backend (an XHR request will be made
- *    to the server.)
- *  - Both methods return the `requestHandler` object for possible overrides.
- */
-
-/**
- * @ngdoc method
- * @name $httpBackend#whenGET
- * @module ngMockE2E
- * @description
- * Creates a new backend definition for GET requests. For more info see `when()`.
- *
- * @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
- *   and returns true if the url matches the current definition.
- * @param {(Object|function(Object))=} headers HTTP headers.
- * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
- *   {@link ngMock.$httpBackend $httpBackend mock}.
- * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
- *   control how a matched request is handled. You can save this object for later use and invoke
- *   `respond` or `passThrough` again in order to change how a matched request is handled.
- */
-
-/**
- * @ngdoc method
- * @name $httpBackend#whenHEAD
- * @module ngMockE2E
- * @description
- * Creates a new backend definition for HEAD requests. For more info see `when()`.
- *
- * @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
- *   and returns true if the url matches the current definition.
- * @param {(Object|function(Object))=} headers HTTP headers.
- * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
- *   {@link ngMock.$httpBackend $httpBackend mock}.
- * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
- *   control how a matched request is handled. You can save this object for later use and invoke
- *   `respond` or `passThrough` again in order to change how a matched request is handled.
- */
-
-/**
- * @ngdoc method
- * @name $httpBackend#whenDELETE
- * @module ngMockE2E
- * @description
- * Creates a new backend definition for DELETE requests. For more info see `when()`.
- *
- * @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
- *   and returns true if the url matches the current definition.
- * @param {(Object|function(Object))=} headers HTTP headers.
- * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
- *   {@link ngMock.$httpBackend $httpBackend mock}.
- * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
- *   control how a matched request is handled. You can save this object for later use and invoke
- *   `respond` or `passThrough` again in order to change how a matched request is handled.
- */
-
-/**
- * @ngdoc method
- * @name $httpBackend#whenPOST
- * @module ngMockE2E
- * @description
- * Creates a new backend definition for POST requests. For more info see `when()`.
- *
- * @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
- *   and returns true if the url matches the current definition.
- * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
- *   data string and returns true if the data is as expected.
- * @param {(Object|function(Object))=} headers HTTP headers.
- * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
- *   {@link ngMock.$httpBackend $httpBackend mock}.
- * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
- *   control how a matched request is handled. You can save this object for later use and invoke
- *   `respond` or `passThrough` again in order to change how a matched request is handled.
- */
-
-/**
- * @ngdoc method
- * @name $httpBackend#whenPUT
- * @module ngMockE2E
- * @description
- * Creates a new backend definition for PUT requests.  For more info see `when()`.
- *
- * @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
- *   and returns true if the url matches the current definition.
- * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
- *   data string and returns true if the data is as expected.
- * @param {(Object|function(Object))=} headers HTTP headers.
- * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
- *   {@link ngMock.$httpBackend $httpBackend mock}.
- * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
- *   control how a matched request is handled. You can save this object for later use and invoke
- *   `respond` or `passThrough` again in order to change how a matched request is handled.
- */
-
-/**
- * @ngdoc method
- * @name $httpBackend#whenPATCH
- * @module ngMockE2E
- * @description
- * Creates a new backend definition for PATCH requests.  For more info see `when()`.
- *
- * @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
- *   and returns true if the url matches the current definition.
- * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
- *   data string and returns true if the data is as expected.
- * @param {(Object|function(Object))=} headers HTTP headers.
- * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
- *   {@link ngMock.$httpBackend $httpBackend mock}.
- * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
- *   control how a matched request is handled. You can save this object for later use and invoke
- *   `respond` or `passThrough` again in order to change how a matched request is handled.
- */
-
-/**
- * @ngdoc method
- * @name $httpBackend#whenJSONP
- * @module ngMockE2E
- * @description
- * Creates a new backend definition for JSONP requests. For more info see `when()`.
- *
- * @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
- *   and returns true if the url matches the current definition.
- * @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
- *   {@link ngMock.$httpBackend $httpBackend mock}.
- * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
- *   control how a matched request is handled. You can save this object for later use and invoke
- *   `respond` or `passThrough` again in order to change how a matched request is handled.
- */
-/**
- * @ngdoc method
- * @name $httpBackend#whenRoute
- * @module ngMockE2E
- * @description
- * Creates a new backend definition that compares only with the requested route.
- *
- * @param {string} method HTTP method.
- * @param {string} url HTTP url string that supports colon param matching.
- * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
- *   control how a matched request is handled. You can save this object for later use and invoke
- *   `respond` or `passThrough` again in order to change how a matched request is handled.
- */
-angular.mock.e2e = {};
-angular.mock.e2e.$httpBackendDecorator =
-  ['$rootScope', '$timeout', '$delegate', '$browser', createHttpBackendMock];
-
-
-/**
- * @ngdoc type
- * @name $rootScope.Scope
- * @module ngMock
- * @description
- * {@link ng.$rootScope.Scope Scope} type decorated with helper methods useful for testing. These
- * methods are automatically available on any {@link ng.$rootScope.Scope Scope} instance when
- * `ngMock` module is loaded.
- *
- * In addition to all the regular `Scope` methods, the following helper methods are available:
- */
-angular.mock.$RootScopeDecorator = ['$delegate', function($delegate) {
-
-  var $rootScopePrototype = Object.getPrototypeOf($delegate);
-
-  $rootScopePrototype.$countChildScopes = countChildScopes;
-  $rootScopePrototype.$countWatchers = countWatchers;
-
-  return $delegate;
-
-  // ------------------------------------------------------------------------------------------ //
-
-  /**
-   * @ngdoc method
-   * @name $rootScope.Scope#$countChildScopes
-   * @module ngMock
-   * @this $rootScope.Scope
-   * @description
-   * Counts all the direct and indirect child scopes of the current scope.
-   *
-   * The current scope is excluded from the count. The count includes all isolate child scopes.
-   *
-   * @returns {number} Total number of child scopes.
-   */
-  function countChildScopes() {
-    var count = 0; // exclude the current scope
-    var pendingChildHeads = [this.$$childHead];
-    var currentScope;
-
-    while (pendingChildHeads.length) {
-      currentScope = pendingChildHeads.shift();
-
-      while (currentScope) {
-        count += 1;
-        pendingChildHeads.push(currentScope.$$childHead);
-        currentScope = currentScope.$$nextSibling;
-      }
-    }
-
-    return count;
-  }
-
-
-  /**
-   * @ngdoc method
-   * @name $rootScope.Scope#$countWatchers
-   * @this $rootScope.Scope
-   * @module ngMock
-   * @description
-   * Counts all the watchers of direct and indirect child scopes of the current scope.
-   *
-   * The watchers of the current scope are included in the count and so are all the watchers of
-   * isolate child scopes.
-   *
-   * @returns {number} Total number of watchers.
-   */
-  function countWatchers() {
-    var count = this.$$watchers ? this.$$watchers.length : 0; // include the current scope
-    var pendingChildHeads = [this.$$childHead];
-    var currentScope;
-
-    while (pendingChildHeads.length) {
-      currentScope = pendingChildHeads.shift();
-
-      while (currentScope) {
-        count += currentScope.$$watchers ? currentScope.$$watchers.length : 0;
-        pendingChildHeads.push(currentScope.$$childHead);
-        currentScope = currentScope.$$nextSibling;
-      }
-    }
-
-    return count;
-  }
-}];
-
-
-(function(jasmineOrMocha) {
-
-  if (!jasmineOrMocha) {
-    return;
-  }
-
-  var currentSpec = null,
-      injectorState = new InjectorState(),
-      annotatedFunctions = [],
-      wasInjectorCreated = function() {
-        return !!currentSpec;
-      };
-
-  angular.mock.$$annotate = angular.injector.$$annotate;
-  angular.injector.$$annotate = function(fn) {
-    if (typeof fn === 'function' && !fn.$inject) {
-      annotatedFunctions.push(fn);
-    }
-    return angular.mock.$$annotate.apply(this, arguments);
-  };
-
-  /**
-   * @ngdoc function
-   * @name angular.mock.module
-   * @description
-   *
-   * *NOTE*: This function is also published on window for easy access.<br>
-   * *NOTE*: This function is declared ONLY WHEN running tests with jasmine or mocha
-   *
-   * This function registers a module configuration code. It collects the configuration information
-   * which will be used when the injector is created by {@link angular.mock.inject inject}.
-   *
-   * See {@link angular.mock.inject inject} for usage example
-   *
-   * @param {...(string|Function|Object)} fns any number of modules which are represented as string
-   *        aliases or as anonymous module initialization functions. The modules are used to
-   *        configure the injector. The 'ng' and 'ngMock' modules are automatically loaded. If an
-   *        object literal is passed each key-value pair will be registered on the module via
-   *        {@link auto.$provide $provide}.value, the key being the string name (or token) to associate
-   *        with the value on the injector.
-   */
-  var module = window.module = angular.mock.module = function() {
-    var moduleFns = Array.prototype.slice.call(arguments, 0);
-    return wasInjectorCreated() ? workFn() : workFn;
-    /////////////////////
-    function workFn() {
-      if (currentSpec.$injector) {
-        throw new Error('Injector already created, can not register a module!');
-      } else {
-        var fn, modules = currentSpec.$modules || (currentSpec.$modules = []);
-        angular.forEach(moduleFns, function(module) {
-          if (angular.isObject(module) && !angular.isArray(module)) {
-            fn = ['$provide', function($provide) {
-              angular.forEach(module, function(value, key) {
-                $provide.value(key, value);
-              });
-            }];
-          } else {
-            fn = module;
-          }
-          if (currentSpec.$providerInjector) {
-            currentSpec.$providerInjector.invoke(fn);
-          } else {
-            modules.push(fn);
-          }
-        });
-      }
-    }
-  };
-
-  module.$$beforeAllHook = (window.before || window.beforeAll);
-  module.$$afterAllHook = (window.after || window.afterAll);
-
-  // purely for testing ngMock itself
-  module.$$currentSpec = function(to) {
-    if (arguments.length === 0) return to;
-    currentSpec = to;
-  };
-
-  /**
-   * @ngdoc function
-   * @name angular.mock.module.sharedInjector
-   * @description
-   *
-   * *NOTE*: This function is declared ONLY WHEN running tests with jasmine or mocha
-   *
-   * This function ensures a single injector will be used for all tests in a given describe context.
-   * This contrasts with the default behaviour where a new injector is created per test case.
-   *
-   * Use sharedInjector when you want to take advantage of Jasmine's `beforeAll()`, or mocha's
-   * `before()` methods. Call `module.sharedInjector()` before you setup any other hooks that
-   * will create (i.e call `module()`) or use (i.e call `inject()`) the injector.
-   *
-   * You cannot call `sharedInjector()` from within a context already using `sharedInjector()`.
-   *
-   * ## Example
-   *
-   * Typically beforeAll is used to make many assertions about a single operation. This can
-   * cut down test run-time as the test setup doesn't need to be re-run, and enabling focussed
-   * tests each with a single assertion.
-   *
-   * ```js
-   * describe("Deep Thought", function() {
-   *
-   *   module.sharedInjector();
-   *
-   *   beforeAll(module("UltimateQuestion"));
-   *
-   *   beforeAll(inject(function(DeepThought) {
-   *     expect(DeepThought.answer).toBeUndefined();
-   *     DeepThought.generateAnswer();
-   *   }));
-   *
-   *   it("has calculated the answer correctly", inject(function(DeepThought) {
-   *     // Because of sharedInjector, we have access to the instance of the DeepThought service
-   *     // that was provided to the beforeAll() hook. Therefore we can test the generated answer
-   *     expect(DeepThought.answer).toBe(42);
-   *   }));
-   *
-   *   it("has calculated the answer within the expected time", inject(function(DeepThought) {
-   *     expect(DeepThought.runTimeMillennia).toBeLessThan(8000);
-   *   }));
-   *
-   *   it("has double checked the answer", inject(function(DeepThought) {
-   *     expect(DeepThought.absolutelySureItIsTheRightAnswer).toBe(true);
-   *   }));
-   *
-   * });
-   *
-   * ```
-   */
-  module.sharedInjector = function() {
-    if (!(module.$$beforeAllHook && module.$$afterAllHook)) {
-      throw Error('sharedInjector() cannot be used unless your test runner defines beforeAll/afterAll');
-    }
-
-    var initialized = false;
-
-    module.$$beforeAllHook(/** @this */ function() {
-      if (injectorState.shared) {
-        injectorState.sharedError = Error('sharedInjector() cannot be called inside a context that has already called sharedInjector()');
-        throw injectorState.sharedError;
-      }
-      initialized = true;
-      currentSpec = this;
-      injectorState.shared = true;
-    });
-
-    module.$$afterAllHook(function() {
-      if (initialized) {
-        injectorState = new InjectorState();
-        module.$$cleanup();
-      } else {
-        injectorState.sharedError = null;
-      }
-    });
-  };
-
-  module.$$beforeEach = function() {
-    if (injectorState.shared && currentSpec && currentSpec !== this) {
-      var state = currentSpec;
-      currentSpec = this;
-      angular.forEach(['$injector','$modules','$providerInjector', '$injectorStrict'], function(k) {
-        currentSpec[k] = state[k];
-        state[k] = null;
-      });
-    } else {
-      currentSpec = this;
-      originalRootElement = null;
-      annotatedFunctions = [];
-    }
-  };
-
-  module.$$afterEach = function() {
-    if (injectorState.cleanupAfterEach()) {
-      module.$$cleanup();
-    }
-  };
-
-  module.$$cleanup = function() {
-    var injector = currentSpec.$injector;
-
-    annotatedFunctions.forEach(function(fn) {
-      delete fn.$inject;
-    });
-
-    angular.forEach(currentSpec.$modules, function(module) {
-      if (module && module.$$hashKey) {
-        module.$$hashKey = undefined;
-      }
-    });
-
-    currentSpec.$injector = null;
-    currentSpec.$modules = null;
-    currentSpec.$providerInjector = null;
-    currentSpec = null;
-
-    if (injector) {
-      // Ensure `$rootElement` is instantiated, before checking `originalRootElement`
-      var $rootElement = injector.get('$rootElement');
-      var rootNode = $rootElement && $rootElement[0];
-      var cleanUpNodes = !originalRootElement ? [] : [originalRootElement[0]];
-      if (rootNode && (!originalRootElement || rootNode !== originalRootElement[0])) {
-        cleanUpNodes.push(rootNode);
-      }
-      angular.element.cleanData(cleanUpNodes);
-
-      // Ensure `$destroy()` is available, before calling it
-      // (a mocked `$rootScope` might not implement it (or not even be an object at all))
-      var $rootScope = injector.get('$rootScope');
-      if ($rootScope && $rootScope.$destroy) $rootScope.$destroy();
-    }
-
-    // clean up jquery's fragment cache
-    angular.forEach(angular.element.fragments, function(val, key) {
-      delete angular.element.fragments[key];
-    });
-
-    MockXhr.$$lastInstance = null;
-
-    angular.forEach(angular.callbacks, function(val, key) {
-      delete angular.callbacks[key];
-    });
-    angular.callbacks.$$counter = 0;
-  };
-
-  (window.beforeEach || window.setup)(module.$$beforeEach);
-  (window.afterEach || window.teardown)(module.$$afterEach);
-
-  /**
-   * @ngdoc function
-   * @name angular.mock.inject
-   * @description
-   *
-   * *NOTE*: This function is also published on window for easy access.<br>
-   * *NOTE*: This function is declared ONLY WHEN running tests with jasmine or mocha
-   *
-   * The inject function wraps a function into an injectable function. The inject() creates new
-   * instance of {@link auto.$injector $injector} per test, which is then used for
-   * resolving references.
-   *
-   *
-   * ## Resolving References (Underscore Wrapping)
-   * Often, we would like to inject a reference once, in a `beforeEach()` block and reuse this
-   * in multiple `it()` clauses. To be able to do this we must assign the reference to a variable
-   * that is declared in the scope of the `describe()` block. Since we would, most likely, want
-   * the variable to have the same name of the reference we have a problem, since the parameter
-   * to the `inject()` function would hide the outer variable.
-   *
-   * To help with this, the injected parameters can, optionally, be enclosed with underscores.
-   * These are ignored by the injector when the reference name is resolved.
-   *
-   * For example, the parameter `_myService_` would be resolved as the reference `myService`.
-   * Since it is available in the function body as `_myService_`, we can then assign it to a variable
-   * defined in an outer scope.
-   *
-   * ```
-   * // Defined out reference variable outside
-   * var myService;
-   *
-   * // Wrap the parameter in underscores
-   * beforeEach( inject( function(_myService_){
-   *   myService = _myService_;
-   * }));
-   *
-   * // Use myService in a series of tests.
-   * it('makes use of myService', function() {
-   *   myService.doStuff();
-   * });
-   *
-   * ```
-   *
-   * See also {@link angular.mock.module angular.mock.module}
-   *
-   * ## Example
-   * Example of what a typical jasmine tests looks like with the inject method.
-   * ```js
-   *
-   *   angular.module('myApplicationModule', [])
-   *       .value('mode', 'app')
-   *       .value('version', 'v1.0.1');
-   *
-   *
-   *   describe('MyApp', function() {
-   *
-   *     // You need to load modules that you want to test,
-   *     // it loads only the "ng" module by default.
-   *     beforeEach(module('myApplicationModule'));
-   *
-   *
-   *     // inject() is used to inject arguments of all given functions
-   *     it('should provide a version', inject(function(mode, version) {
-   *       expect(version).toEqual('v1.0.1');
-   *       expect(mode).toEqual('app');
-   *     }));
-   *
-   *
-   *     // The inject and module method can also be used inside of the it or beforeEach
-   *     it('should override a version and test the new version is injected', function() {
-   *       // module() takes functions or strings (module aliases)
-   *       module(function($provide) {
-   *         $provide.value('version', 'overridden'); // override version here
-   *       });
-   *
-   *       inject(function(version) {
-   *         expect(version).toEqual('overridden');
-   *       });
-   *     });
-   *   });
-   *
-   * ```
-   *
-   * @param {...Function} fns any number of functions which will be injected using the injector.
-   */
-
-
-
-  var ErrorAddingDeclarationLocationStack = function ErrorAddingDeclarationLocationStack(e, errorForStack) {
-    this.message = e.message;
-    this.name = e.name;
-    if (e.line) this.line = e.line;
-    if (e.sourceId) this.sourceId = e.sourceId;
-    if (e.stack && errorForStack)
-      this.stack = e.stack + '\n' + errorForStack.stack;
-    if (e.stackArray) this.stackArray = e.stackArray;
-  };
-  ErrorAddingDeclarationLocationStack.prototype = Error.prototype;
-
-  window.inject = angular.mock.inject = function() {
-    var blockFns = Array.prototype.slice.call(arguments, 0);
-    var errorForStack = new Error('Declaration Location');
-    // IE10+ and PhanthomJS do not set stack trace information, until the error is thrown
-    if (!errorForStack.stack) {
-      try {
-        throw errorForStack;
-      } catch (e) { /* empty */ }
-    }
-    return wasInjectorCreated() ? WorkFn.call(currentSpec) : WorkFn;
-    /////////////////////
-    function WorkFn() {
-      var modules = currentSpec.$modules || [];
-      var strictDi = !!currentSpec.$injectorStrict;
-      modules.unshift(['$injector', function($injector) {
-        currentSpec.$providerInjector = $injector;
-      }]);
-      modules.unshift('ngMock');
-      modules.unshift('ng');
-      var injector = currentSpec.$injector;
-      if (!injector) {
-        if (strictDi) {
-          // If strictDi is enabled, annotate the providerInjector blocks
-          angular.forEach(modules, function(moduleFn) {
-            if (typeof moduleFn === 'function') {
-              angular.injector.$$annotate(moduleFn);
-            }
-          });
-        }
-        injector = currentSpec.$injector = angular.injector(modules, strictDi);
-        currentSpec.$injectorStrict = strictDi;
-      }
-      for (var i = 0, ii = blockFns.length; i < ii; i++) {
-        if (currentSpec.$injectorStrict) {
-          // If the injector is strict / strictDi, and the spec wants to inject using automatic
-          // annotation, then annotate the function here.
-          injector.annotate(blockFns[i]);
-        }
-        try {
-          injector.invoke(blockFns[i] || angular.noop, this);
-        } catch (e) {
-          if (e.stack && errorForStack) {
-            throw new ErrorAddingDeclarationLocationStack(e, errorForStack);
-          }
-          throw e;
-        } finally {
-          errorForStack = null;
-        }
-      }
-    }
-  };
-
-
-  angular.mock.inject.strictDi = function(value) {
-    value = arguments.length ? !!value : true;
-    return wasInjectorCreated() ? workFn() : workFn;
-
-    function workFn() {
-      if (value !== currentSpec.$injectorStrict) {
-        if (currentSpec.$injector) {
-          throw new Error('Injector already created, can not modify strict annotations');
-        } else {
-          currentSpec.$injectorStrict = value;
-        }
-      }
-    }
-  };
-
-  function InjectorState() {
-    this.shared = false;
-    this.sharedError = null;
-
-    this.cleanupAfterEach = function() {
-      return !this.shared || this.sharedError;
-    };
-  }
-})(window.jasmine || window.mocha);
-
-'use strict';
-
-(function() {
-  /**
-   * Triggers a browser event. Attempts to choose the right event if one is
-   * not specified.
-   *
-   * @param {Object} element Either a wrapped jQuery/jqLite node or a DOMElement
-   * @param {string} eventType Optional event type
-   * @param {Object=} eventData An optional object which contains additional event data (such as x,y
-   * coordinates, keys, etc...) that are passed into the event when triggered
-   */
-  window.browserTrigger = function browserTrigger(element, eventType, eventData) {
-    if (element && !element.nodeName) element = element[0];
-    if (!element) return;
-
-    eventData = eventData || {};
-    var relatedTarget = eventData.relatedTarget || element;
-    var keys = eventData.keys;
-    var x = eventData.x;
-    var y = eventData.y;
-
-    var inputType = (element.type) ? element.type.toLowerCase() : null,
-        nodeName = element.nodeName.toLowerCase();
-    if (!eventType) {
-      eventType = {
-        'text':            'change',
-        'textarea':        'change',
-        'hidden':          'change',
-        'password':        'change',
-        'button':          'click',
-        'submit':          'click',
-        'reset':           'click',
-        'image':           'click',
-        'checkbox':        'click',
-        'radio':           'click',
-        'select-one':      'change',
-        'select-multiple': 'change',
-        '_default_':       'click'
-      }[inputType || '_default_'];
-    }
-
-    if (nodeName === 'option') {
-      element.parentNode.value = element.value;
-      element = element.parentNode;
-      eventType = 'change';
-    }
-
-    keys = keys || [];
-    function pressed(key) {
-      return keys.indexOf(key) !== -1;
-    }
-
-    var evnt;
-    if (/transitionend/.test(eventType)) {
-      if (window.WebKitTransitionEvent) {
-        evnt = new window.WebKitTransitionEvent(eventType, eventData);
-        evnt.initEvent(eventType, false, true);
-      } else {
-        try {
-          evnt = new window.TransitionEvent(eventType, eventData);
-        } catch (e) {
-          evnt = window.document.createEvent('TransitionEvent');
-          evnt.initTransitionEvent(eventType, null, null, null, eventData.elapsedTime || 0);
-        }
-      }
-    } else if (/animationend/.test(eventType)) {
-      if (window.WebKitAnimationEvent) {
-        evnt = new window.WebKitAnimationEvent(eventType, eventData);
-        evnt.initEvent(eventType, false, true);
-      } else {
-        try {
-          evnt = new window.AnimationEvent(eventType, eventData);
-        } catch (e) {
-          evnt = window.document.createEvent('AnimationEvent');
-          evnt.initAnimationEvent(eventType, null, null, null, eventData.elapsedTime || 0);
-        }
-      }
-    } else if (/touch/.test(eventType) && supportsTouchEvents()) {
-      evnt = createTouchEvent(element, eventType, x, y);
-    } else if (/key/.test(eventType)) {
-      evnt = window.document.createEvent('Events');
-      evnt.initEvent(eventType, eventData.bubbles, eventData.cancelable);
-      evnt.view = window;
-      evnt.ctrlKey = pressed('ctrl');
-      evnt.altKey = pressed('alt');
-      evnt.shiftKey = pressed('shift');
-      evnt.metaKey = pressed('meta');
-      evnt.keyCode = eventData.keyCode;
-      evnt.charCode = eventData.charCode;
-      evnt.which = eventData.which;
-    } else {
-      evnt = window.document.createEvent('MouseEvents');
-      x = x || 0;
-      y = y || 0;
-      evnt.initMouseEvent(eventType, true, true, window, 0, x, y, x, y, pressed('ctrl'),
-          pressed('alt'), pressed('shift'), pressed('meta'), 0, relatedTarget);
-    }
-
-    /* we're unable to change the timeStamp value directly so this
-     * is only here to allow for testing where the timeStamp value is
-     * read */
-    evnt.$manualTimeStamp = eventData.timeStamp;
-
-    if (!evnt) return;
-
-    var originalPreventDefault = evnt.preventDefault,
-        appWindow = element.ownerDocument.defaultView,
-        fakeProcessDefault = true,
-        finalProcessDefault,
-        angular = appWindow.angular || {};
-
-    // igor: temporary fix for https://bugzilla.mozilla.org/show_bug.cgi?id=684208
-    angular['ff-684208-preventDefault'] = false;
-    evnt.preventDefault = function() {
-      fakeProcessDefault = false;
-      return originalPreventDefault.apply(evnt, arguments);
-    };
-
-    if (!eventData.bubbles || supportsEventBubblingInDetachedTree() || isAttachedToDocument(element)) {
-      element.dispatchEvent(evnt);
-    } else {
-      triggerForPath(element, evnt);
-    }
-
-    finalProcessDefault = !(angular['ff-684208-preventDefault'] || !fakeProcessDefault);
-
-    delete angular['ff-684208-preventDefault'];
-
-    return finalProcessDefault;
-  };
-
-  function supportsTouchEvents() {
-    if ('_cached' in supportsTouchEvents) {
-      return supportsTouchEvents._cached;
-    }
-    if (!window.document.createTouch || !window.document.createTouchList) {
-      supportsTouchEvents._cached = false;
-      return false;
-    }
-    try {
-      window.document.createEvent('TouchEvent');
-    } catch (e) {
-      supportsTouchEvents._cached = false;
-      return false;
-    }
-    supportsTouchEvents._cached = true;
-    return true;
-  }
-
-  function createTouchEvent(element, eventType, x, y) {
-    var evnt = new window.Event(eventType);
-    x = x || 0;
-    y = y || 0;
-
-    var touch = window.document.createTouch(window, element, Date.now(), x, y, x, y);
-    var touches = window.document.createTouchList(touch);
-
-    evnt.touches = touches;
-
-    return evnt;
-  }
-
-  function supportsEventBubblingInDetachedTree() {
-    if ('_cached' in supportsEventBubblingInDetachedTree) {
-      return supportsEventBubblingInDetachedTree._cached;
-    }
-    supportsEventBubblingInDetachedTree._cached = false;
-    var doc = window.document;
-    if (doc) {
-      var parent = doc.createElement('div'),
-          child = parent.cloneNode();
-      parent.appendChild(child);
-      parent.addEventListener('e', function() {
-        supportsEventBubblingInDetachedTree._cached = true;
-      });
-      var evnt = window.document.createEvent('Events');
-      evnt.initEvent('e', true, true);
-      child.dispatchEvent(evnt);
-    }
-    return supportsEventBubblingInDetachedTree._cached;
-  }
-
-  function triggerForPath(element, evnt) {
-    var stop = false;
-
-    var _stopPropagation = evnt.stopPropagation;
-    evnt.stopPropagation = function() {
-      stop = true;
-      _stopPropagation.apply(evnt, arguments);
-    };
-    patchEventTargetForBubbling(evnt, element);
-    do {
-      element.dispatchEvent(evnt);
-      // eslint-disable-next-line no-unmodified-loop-condition
-    } while (!stop && (element = element.parentNode));
-  }
-
-  function patchEventTargetForBubbling(event, target) {
-    event._target = target;
-    Object.defineProperty(event, 'target', {get: function() { return this._target;}});
-  }
-
-  function isAttachedToDocument(element) {
-    while ((element = element.parentNode)) {
-        if (element === window) {
-            return true;
-        }
-    }
-    return false;
-  }
-})();
-
-
-})(window, window.angular);

+ 0 - 10
public/vendor/angular-mocks/bower.json

@@ -1,10 +0,0 @@
-{
-  "name": "angular-mocks",
-  "version": "1.6.1",
-  "license": "MIT",
-  "main": "./angular-mocks.js",
-  "ignore": [],
-  "dependencies": {
-    "angular": "1.6.1"
-  }
-}

+ 0 - 2
public/vendor/angular-mocks/ngAnimateMock.js

@@ -1,2 +0,0 @@
-require('./angular-mocks');
-module.exports = 'ngAnimateMock';

+ 0 - 2
public/vendor/angular-mocks/ngMock.js

@@ -1,2 +0,0 @@
-require('./angular-mocks');
-module.exports = 'ngMock';

+ 0 - 2
public/vendor/angular-mocks/ngMockE2E.js

@@ -1,2 +0,0 @@
-require('./angular-mocks');
-module.exports = 'ngMockE2E';

+ 0 - 34
public/vendor/angular-mocks/package.json

@@ -1,34 +0,0 @@
-{
-  "name": "angular-mocks",
-  "version": "1.6.1",
-  "description": "AngularJS mocks for testing",
-  "main": "angular-mocks.js",
-  "scripts": {
-    "test": "echo \"Error: no test specified\" && exit 1"
-  },
-  "repository": {
-    "type": "git",
-    "url": "https://github.com/angular/angular.js.git"
-  },
-  "keywords": [
-    "angular",
-    "framework",
-    "browser",
-    "mocks",
-    "testing",
-    "client-side"
-  ],
-  "author": "Angular Core Team <angular-core+npm@google.com>",
-  "license": "MIT",
-  "bugs": {
-    "url": "https://github.com/angular/angular.js/issues"
-  },
-  "homepage": "http://angularjs.org",
-  "jspm": {
-    "shim": {
-      "angular-mocks": {
-        "deps": ["angular"]
-      }
-    }
-  }
-}

+ 0 - 36
public/vendor/angular-native-dragdrop/.bower.json

@@ -1,36 +0,0 @@
-{
-  "name": "angular-native-dragdrop",
-  "version": "1.2.2",
-  "homepage": "http://angular-dragdrop.github.io/angular-dragdrop",
-  "authors": [
-    "ganarajpr"
-  ],
-  "description": "Angular HTML5 Drag and Drop directive written in pure with no dependency on JQuery.",
-  "main": "draganddrop.js",
-  "keywords": [
-    "angular",
-    "drag",
-    "drop",
-    "html5"
-  ],
-  "dependencies": {
-    "angular": "^1.3"
-  },
-  "license": "MIT",
-  "ignore": [
-    "**/.*",
-    "node_modules",
-    "bower_components",
-    "test",
-    "tests"
-  ],
-  "_release": "1.2.2",
-  "_resolution": {
-    "type": "version",
-    "tag": "1.2.2",
-    "commit": "e630636fdc39fef815c1c534340aa16caf76a04c"
-  },
-  "_source": "https://github.com/angular-dragdrop/angular-dragdrop.git",
-  "_target": "1.2.2",
-  "_originalSource": "angular-native-dragdrop"
-}

+ 0 - 25
public/vendor/angular-native-dragdrop/Gulpfile.js

@@ -1,25 +0,0 @@
-/* jshint -W097 */
-'use strict';
-
-/* global require */
-var jshint = require('gulp-jshint');
-var stylish = require('jshint-stylish');
-var uglify = require('gulp-uglify');
-var rename = require('gulp-rename');
-var gulp = require('gulp');
-
-gulp.task('lint', function() {
-    return gulp.src('./draganddrop.js')
-        .pipe(jshint())
-        .pipe(jshint.reporter(stylish));
-});
-gulp.task('compress', function() {
-    return gulp.src('./draganddrop.js')
-        .pipe(uglify())
-        .pipe(rename({
-            extname: '.min.js'
-        }))
-        .pipe(gulp.dest('./'));
-});
-
-gulp.task('default', ['lint', 'compress']);

+ 0 - 10
public/vendor/angular-native-dragdrop/LICENSE

@@ -1,10 +0,0 @@
-
-The MIT License
-
-Copyright (c) 2015 Ganaraj P R, [Nebithi](http://www.nebithi.com)
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 0 - 36
public/vendor/angular-native-dragdrop/README.md

@@ -1,36 +0,0 @@
-#Angular-DragDrop
-[![npm version](http://img.shields.io/npm/v/angular-native-dragdrop.svg?style=flat)](https://npmjs.org/package/angular-native-dragdrop) 
-[![Build status](http://img.shields.io/travis/angular-dragdrop/angular-dragdrop.svg?style=flat)](https://travis-ci.org/angular-dragdrop/angular-dragdrop)
-[![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/ganarajpr/angular-dragdrop?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
-
-Angular-DragDrop is a angular HTML5 Drag and Drop directive written in pure with no dependency on JQuery.
-
-This is based on the work done by Jason Turim. While this [blog post](http://jasonturim.wordpress.com/2013/09/01/angularjs-drag-and-drop/) was the inspiration for creating a native Drag and Drop solution, the intention was to create something that was more generic. 
-
-This implementation is mainly different from the one posted in the blog in the following areas : 
-
-1. Angular-DragDrop does not create an isolate scope. This has huge benefits when it comes to working with other directives. **NOTE :** It also does not pollute the scope with any variables or functions.
-
-2. It does not depend on any kind of an ID attribute ( being either present or generated on the fly ). 
-
-3. It allows one to create channels on which different drag and drop directive combinations can work on in the same page ( more on this later ) . 
-
-Pull requests are welcome.
-
-[Documentation](http://angular-dragdrop.github.io/angular-dragdrop/)
-
-#Looking for Active Contributors.
-
-This repo needs active contributers and maintainers. If you are interested in being one of the people who would like to actively maintain this repo, please let me know. 
-
-
-
-The MIT License
-
-Copyright (c) 2014 Ganaraj P R, [Nebithi](http://www.nebithi.com)
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 0 - 27
public/vendor/angular-native-dragdrop/bower.json

@@ -1,27 +0,0 @@
-{
-  "name": "angular-native-dragdrop",
-  "version": "1.2.1",
-  "homepage": "http://angular-dragdrop.github.io/angular-dragdrop",
-  "authors": [
-    "ganarajpr"
-  ],
-  "description": "Angular HTML5 Drag and Drop directive written in pure with no dependency on JQuery.",
-  "main": "draganddrop.js",
-  "keywords": [
-    "angular",
-    "drag",
-    "drop",
-    "html5"
-  ],
-  "dependencies": {
-    "angular": "^1.3"
-  },
-  "license": "MIT",
-  "ignore": [
-    "**/.*",
-    "node_modules",
-    "bower_components",
-    "test",
-    "tests"
-  ]
-}

+ 0 - 29
public/vendor/angular-native-dragdrop/demo/css/styles.css

@@ -1,29 +0,0 @@
-body {
-    background-color: #f8f7f8;
-}
-
-.heading {
-    border-bottom: 1px solid #b7b7b7;
-    padding-bottom: 10px;
-    margin-bottom: 10px;
-}
-
-.topRow {
-    margin-bottom: 30px;
-}
-
-.on-drag-enter {
-    background-color : #677ba6;
-}
-
-.on-drag-enter-custom {
-    background-color : #d78cc7;
-}
-
-.on-drag-hover {
-    background-color : #3eb352;
-}
-
-.on-drag-hover-custom {
-    background-color : #d7a931;
-}

+ 0 - 178
public/vendor/angular-native-dragdrop/demo/index.html

@@ -1,178 +0,0 @@
-<!DOCTYPE html>
-<html ng-app="app">
-
-  <head>
-    <meta charset="utf-8" />
-    <title>Angular DragDrop (Demo)</title>
-    <script>document.write('<base href="' + document.location + '" />');</script>
-    <link rel="stylesheet" href="css/styles.css" />
-    <script data-require="angular.js@1.4.x" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular.js" data-semver="1.4.3"></script>
-    <script src="http://pc035860.github.io/angular-highlightjs/angular-highlightjs.min.js"></script>
-    <script src="js/app.js"></script>
-    <script src="../draganddrop.js"></script>
-    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet">
-  </head>
-
-  <body >
-    <div class="container">
-        <div class="row topRow">
-            <h4 class="heading">
-                Drag and drop between the two lists.
-            </h4>
-        </div>
-        
-        <h3>Beasts</h3>
-        
-        <p>Left column of beasts is not draggable and accepts both beasts and priests</p>
-        
-        <hr>
-        
-        <div class="row" ng-controller="MainCtrl">
-            <div class="col-xs-6">
-                <ul ui-on-drop="onDrop($event,$data,men)" drag-enter-class="on-drag-enter-custom"
-                    drop-channel="beasts,priests"
-                    drop-validate="dropValidateHandler($drop, $event, $data)">
-                    <li ui-draggable="false" drag="man" drag-channel="beasts"
-                        on-drop-success="dropSuccessHandler($event,$index,men)"
-                        ng-repeat="man in men track by $index">
-                        {{man}}
-                    </li>
-                </ul>
-            </div>
-            <div class="col-xs-6">
-                <ul ui-on-drop="onDrop($event,$data,women)"
-                    drop-channel="beasts"
-                    drop-validate="dropValidateHandler($drop, $event, $data)">
-                    <li ui-draggable="true" drag="woman" drag-channel="beasts"
-                        on-drop-success="dropSuccessHandler($event,$index,women)"
-                        on-drop-failure="dropFailureHandler($event,$index,women)"
-                        ng-repeat="woman in women track by $index">
-                        {{woman}}
-                    </li>
-                </ul>
-            </div>
-        </div>
-        
-        <hr>
-        
-        <h3>Priests</h3>
-        
-        <hr>
-        
-        <div class="row" ng-controller="MainCtrl">
-            <div class="col-xs-6">
-                <ul ui-on-drop="onDrop($event,$data,men)"
-                    drop-channel="priests"
-                    drop-validate="dropValidateHandler($drop, $event, $data)">
-                    <li ui-draggable="true" drag="man"
-                        drag-channel="priests"
-                        on-drop-success="dropSuccessHandler($event,$index,men)"
-                        ng-repeat="man in men track by $index">
-                        {{man}}
-                    </li>
-                </ul>
-            </div>
-            <div class="col-xs-6">
-                <ul ui-on-drop="onDrop($event,$data,women)"
-                    drop-channel="priests"
-                    drop-validate="dropValidateHandler($drop, $event, $data)">
-                    <li ui-draggable="true" drag="woman"
-                        drag-channel="priests"
-                        on-drop-success="dropSuccessHandler($event,$index,women)"
-                        ng-repeat="woman in women track by $index">
-                        {{woman}}
-                    </li>
-                </ul>
-            </div>
-        </div>
-        
-        <hr>
-        
-        <h3>Terrorists</h3>
-        
-        <p>Each terrorist list item accepts a new terrorist. Shows inserting into a particular
-        position in an array.</p>
-        
-        <hr>
-        
-        
-        <div class="row" ng-controller="MainCtrl">
-            <div class="col-xs-6">
-                <ul>
-                    <li ui-draggable="true" drag="man"
-                        drag-channel="terrorists2"
-                        drop-validate="dropValidateHandler($drop, $event, $data)"
-                    drag-hover-class="on-drag-hover-custom"
-                    on-drop-success="dropSuccessHandler($event,$index,men)"
-                    ui-on-drop="onDrop($event,$data,men,$index)" drop-channel="terrorists1"
-                    ng-repeat="man in men track by $index">
-                        {{man}}
-                    </li>
-                </ul>
-            </div>
-            <div class="col-xs-6">
-                <ul>
-                    <li ui-draggable="true" drag="woman"
-                        drag-channel="terrorists1"
-                        drop-validate="dropValidateHandler($drop, $event, $data)"
-                    drag-hover-class="on-drag-hover-custom"
-                    ui-on-drop="onDrop($event,$data,women,$index)" drop-channel="terrorists2"
-                    on-drop-success="dropSuccessHandler($event,$index,women)"
-                    ng-repeat="woman in women track by $index">
-                        {{woman}}
-                    </li>
-                </ul>
-            </div>
-        </div>
-
-        <hr>
-
-        <h3>Custom Drag Image</h3>
-
-        <p>You may specify a drag-image-element-id to use as the drag image. You can use the
-            <a href="http://www.kryogenix.org/code/browser/custom-drag-image.html" target="_blank">ghost image</a>
-            technique for the custom drag image.</p>
-
-        <hr>
-
-
-        <div class="row" ng-controller="MainCtrl">
-            <div class="col-xs-6">
-                <ul>
-                    <li ui-draggable="true" drag="man"
-                        drag-channel="customImage2"
-                        drop-validate="dropValidateHandler($drop, $event, $data)"
-                        drag-hover-class="on-drag-hover-custom"
-                        drag-image-element-id="getCustomDragElementId($index)"
-                        on-drop-success="dropSuccessHandler($event,$index,men)"
-                        ui-on-drop="onDrop($event,$data,men,$index)"
-                        drop-channel="customImage1"
-                        ng-repeat="man in men track by $index">
-                        {{man}}
-                    </li>
-                </ul>
-            </div>
-            <div class="col-xs-6">
-                <ul>
-                    <li ui-draggable="true" drag="woman"
-                        drag-channel="customImage1"
-                        drop-validate="dropValidateHandler($drop, $event, $data)"
-                        drag-hover-class="on-drag-hover-custom"
-                        drag-image-element-id="getCustomDragElementId($index)"
-                        ui-on-drop="onDrop($event,$data,women,$index)"
-                        drop-channel="customImage2"
-                        on-drop-success="dropSuccessHandler($event,$index,women)"
-                        ng-repeat="woman in women track by $index">
-                        {{woman}}
-                    </li>
-                </ul>
-            </div>
-        </div>
-
-        <div id="customDrag0" class="alert alert-info" style="max-width: 200px">Custom drag image #1</div>
-        <div id="customDrag1" class="alert alert-danger" style="position: absolute; top: 0; right: 0; z-index: -2; max-width: 200px">Custom drag image #2</div>
-        <div id="coverUp" style="position: absolute; top: 0; right: 0; z-index: -1; background-color: white; width: 200px; height: 60px"></div>
-    </div>
-  </body>
-
-</html>

+ 0 - 54
public/vendor/angular-native-dragdrop/demo/js/app.js

@@ -1,54 +0,0 @@
-angular.module('app', [
-    'hljs',
-    'ang-drag-drop'
-]).controller('MainCtrl', function($scope) {
-    $scope.men = [
-        'John',
-        'Jack',
-        'Mark',
-        'Ernie',
-        'Mike (Locked)'
-    ];
-
-
-    $scope.women = [
-        'Jane',
-        'Jill',
-        'Betty',
-        'Mary'
-    ];
-
-    $scope.addText = '';
-
-    $scope.dropValidateHandler = function($drop, $event, $data) {
-        if ($data === 'Mike (Locked)') {
-            return false;
-        }
-        if ($drop.element[0] === $event.srcElement.parentNode) {
-            // Don't allow moving to same container
-            return false;
-        }
-        return true;
-    };
-
-    $scope.dropSuccessHandler = function($event, index, array) {
-        array.splice(index, 1);
-    };
-
-    $scope.dropFailureHandler = function($event, index, array) {
-        alert(array[index] + ' could be dropped into left list!')
-    };
-
-    $scope.onDrop = function($event, $data, array, index) {
-        if (index !== undefined) {
-            array.splice(index, 0, $data);
-        } else {
-            array.push($data);
-        }
-    };
-
-    $scope.getCustomDragElementId = function (index) {
-        return 'customDrag' + (index % 2);
-    }
-
-});

+ 0 - 8
public/vendor/angular-native-dragdrop/docs/examples.md

@@ -1,8 +0,0 @@
-#Examples
-
-##Simple Usage
-<iframe style="width: 100%; height: 500px" src="http://embed.plnkr.co/5RLvCpDPoRcEk6u77dBM" frameborder="0" allowfullscreen="allowfullscreen"></iframe>
-
-
-##With drop validation
-<iframe style="width: 100%; height: 500px" src="http://embed.plnkr.co/xRmz4TlCvlJKxybGrhQH" frameborder="0" allowfullscreen="allowfullscreen"></iframe>

+ 0 - 26
public/vendor/angular-native-dragdrop/docs/getting-started.md

@@ -1,26 +0,0 @@
-#Installation
-
-##Download
-Download the file [**draganddrop.min.js**](https://raw.githubusercontent.com/angular-dragdrop/angular-dragdrop/master/draganddrop.min.js) or [**draganddrop.js**](https://raw.githubusercontent.com/angular-dragdrop/angular-dragdrop/master/draganddrop.js).
-
-##Bower
-
-You can also install via Bower using
-
-`bower install angular-native-dragdrop --save`
-
----
-
-#Usage
-
-##Step - 1 **Add script**
-```
-<script src="path/to/draganddrop.min.js"></script>
-```
-
-##Step - 2 **Include in app**
-```
-	myApp = angular.module('myApp','ang-drag-drop');
-```
-
-##Step - 3 ***Profit!!***

+ 0 - 120
public/vendor/angular-native-dragdrop/docs/index.md

@@ -1,120 +0,0 @@
-# Angular Drag and Drop
-**A Native ( without jquery ) Drag and Drop directive for AngularJS using HTML5 Drag and Drop**
-
----
-
-##ui-draggable(expression)
-
-Directive in module ang-drag-drop (since 1.0.5 - old module name ngDragDrop)
-
-The ui-draggable attribute tells Angular that the element is draggable. ui-draggable takes an expression as the attribute value. The expression should evaluate to either true or false. You can toggle the draggability of an element using this expression.
-
-###Additional Attributes
-
-####_**drag**_(variable)
-
-The class used to mark child elements of draggable object to be used as drag handle. Default class name is `drag-handle`
-
-**NOTE**: If attribute is not present drag handle feature is not active.
-
-
-
-####_**drag-handle-class**_(string)
-
-The `drag` property is used to assign the data that needs to be passed along with the dragging element.
-
-
-
-
-####_**on-drop-success**_(function)
-
-The `on-drop-success` attribute takes a function. We can consider this to be an on-drop-success handler function. This can be useful if you need to do some post processing after the dragged element is dropped successfully on the drop site.
-
-**NOTE**: This callback function is only called when the drop succeeds.
-You can request the `drag-end` event ( very similiar to requesting the click event in `ng-click` ) by passing `$event` in the event handler.
-
-
-
-####_**on-drop-failure**_(function)
-
-The `on-drop-failure` attribute takes a function. We can consider this to be an on-drop-failure handler function. This can be useful if you need to do some post processing after the dragged element is dropped unsuccessfully on any drop site.
-
-**NOTE**: This callback function is only called when the drop fails.
-You can request the `drag-end` event ( very similiar to requesting the click event in `ng-click` ) by passing `$event` in the event handler.
-
-
-
-####_**drag-channel**_(string)
-
-The `on-drop-failure` attribute takes a function. We can consider this to be an on-drop-failure handler function. This can be useful if you need to do some post processing after the dragged element is dropped unsuccessfully on any drop site.
-
-**NOTE**: This callback function is only called when the drop fails.
-You can request the `drag-end` event ( very similiar to requesting the click event in `ng-click` ) by passing `$event` in the event handler.
-
-
-
-###Usage
-
-
-
-###Events
-
-On start of dragging an Angular Event `ANGULAR_DRAG_START` is dispatched from the `$rootScope`. The event also carries carries the information about the channel in which the dragging has started.
-
-On end of dragging an Angular Event `ANGULAR_DRAG_END` is dispatched from the `$rootScope`. The event also carries carries the information about the channel in which the dragging has started.
-
-When hovering a draggable element on top of a drop area an Angular Event `ANGULAR_HOVER` is dispatched from the `$rootScope`. The event also carries the information about the channel in which the dragging has started.
-
----
-
-##ui-on-drop(expression)
-
-Directive in module ang-drag-drop (since 1.0.5 - old module name ngDragDrop)
-
-The `ui-on-drop` attribute tells Angular that the element is a drop site. `ui-on-drop` takes a function as the attribute value. The function will be called when a valid dragged element is dropped in that location. A valid dragged element is one which has the same channel as the drop location.
-
-**NOTE** : This callback function is only called when the drop succeeds.
-The `ui-on-drop` callback can request additional parameters. The data that is dragged is available to the callback as $data and its channel as `$channel`. Apart from this the drop event is exposed as `$event`.
-
-###Additional Attributes
-
-####_**drop-channel**_(variable)
-
-The channel that the drop site accepts. The dragged element should have the same channel as this drop site for it to be droppable at this location. It is possible to provide comma separated list of channels.
-
-**NOTE**: Also special value of `drag-channel` attribute is available to accept dragged element with any channel value — *
-
-
-
-####_**drop-validate**_(function)
-
-Extra validation that makes sure that the drop site accepts the dragged element beyond having the same channel. If not defined, no extra validation is made.
-
-**NOTE**: This callback function is called only if the channel condition is met, when the element starts being dragged
-
-
-
-
-####_**drag-enter-class**_(string)
-
-The class that will be added to the the droppable element when a dragged element ( which is droppable ) enters the drop location. The default value for this is `on-drag-enter`
-
-
-
-####_**drag-hover-class**_(string)
-
-The class that will be added to the drop area element when hovering with an element. The default value for this is `on-drag-hover`
-
-
-
-###Usage
-
-
-
-###Events
-
-On start of dragging an Angular Event `ANGULAR_DRAG_START` is dispatched from the `$rootScope`. The event also carries carries the information about the channel in which the dragging has started.
-
-On end of dragging an Angular Event `ANGULAR_DRAG_END` is dispatched from the `$rootScope`. The event also carries carries the information about the channel in which the dragging has started.
-
-When hovering a draggable element on top of a drop area an Angular Event `ANGULAR_HOVER` is dispatched from the `$rootScope`. The event also carries the information about the channel in which the dragging has started.

部分文件因文件數量過多而無法顯示