ahoy_captain 0.1.0 → 0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (157) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +55 -14
  4. data/Rakefile +1 -10
  5. data/app/assets/images/ahoy_captain/apple-touch-icon.png +0 -0
  6. data/app/assets/images/ahoy_captain/favicon-16x16.png +0 -0
  7. data/app/assets/images/ahoy_captain/favicon-32x32.png +0 -0
  8. data/app/assets/images/ahoy_captain/logo.png +0 -0
  9. data/app/assets/images/ahoy_captain/safari-pinned-tab.svg +199 -0
  10. data/app/assets/javascript/ahoy_captain/application.js +4 -0
  11. data/app/assets/javascript/ahoy_captain/controllers/application.js +9 -0
  12. data/app/assets/javascript/ahoy_captain/controllers/application_controller.js +14 -0
  13. data/app/assets/javascript/ahoy_captain/controllers/details_modal_controller.js +18 -0
  14. data/app/assets/javascript/ahoy_captain/controllers/dropdown_label_controller.js +14 -0
  15. data/app/assets/javascript/ahoy_captain/controllers/filter_controller.js +145 -0
  16. data/app/assets/javascript/ahoy_captain/controllers/filter_tag_controller.js +17 -0
  17. data/app/assets/javascript/ahoy_captain/controllers/funnel_chart_controller.js +117 -0
  18. data/app/assets/javascript/ahoy_captain/controllers/index.js +3 -0
  19. data/app/assets/javascript/ahoy_captain/controllers/interval_controller.js +10 -0
  20. data/app/assets/javascript/ahoy_captain/controllers/link_controller.js +43 -0
  21. data/app/assets/javascript/ahoy_captain/controllers/navigation_controller.js +25 -0
  22. data/app/assets/javascript/ahoy_captain/controllers/realtime_controller.js +27 -0
  23. data/app/assets/manifest/ahoy_captain/manifest.js +2 -0
  24. data/app/components/ahoy_captain/dropdown_button_component.html.erb +16 -0
  25. data/app/components/ahoy_captain/dropdown_button_component.rb +14 -0
  26. data/app/components/ahoy_captain/dropdown_link_component.html.erb +19 -0
  27. data/app/components/ahoy_captain/dropdown_link_component.rb +15 -0
  28. data/app/components/ahoy_captain/filter/modal_component.html.erb +13 -0
  29. data/app/components/ahoy_captain/filter/modal_component.rb +13 -0
  30. data/app/components/ahoy_captain/filter/select_component.html.erb +21 -0
  31. data/app/components/ahoy_captain/filter/select_component.rb +32 -0
  32. data/app/components/ahoy_captain/filter/tag_component.html.erb +9 -0
  33. data/app/components/ahoy_captain/filter/tag_component.rb +38 -0
  34. data/app/components/ahoy_captain/filter/tag_container_component.html.erb +5 -0
  35. data/app/components/ahoy_captain/filter/tag_container_component.rb +13 -0
  36. data/app/components/ahoy_captain/sticky_nav_component.html.erb +37 -0
  37. data/app/components/ahoy_captain/sticky_nav_component.rb +5 -0
  38. data/app/components/ahoy_captain/table_component.html.erb +49 -0
  39. data/app/components/ahoy_captain/table_component.rb +28 -0
  40. data/app/components/ahoy_captain/tile_component.html.erb +12 -0
  41. data/app/components/ahoy_captain/tile_component.rb +16 -0
  42. data/app/components/ahoy_captain/tooltip_component.html.erb +3 -0
  43. data/app/components/ahoy_captain/tooltip_component.rb +18 -0
  44. data/app/controllers/ahoy_captain/application_controller.rb +86 -0
  45. data/app/controllers/ahoy_captain/campaigns_controller.rb +27 -0
  46. data/app/controllers/ahoy_captain/cities_controller.rb +24 -0
  47. data/app/controllers/ahoy_captain/countries_controller.rb +24 -0
  48. data/app/controllers/ahoy_captain/devices_controller.rb +23 -0
  49. data/app/controllers/ahoy_captain/entry_pages_controller.rb +21 -0
  50. data/app/controllers/ahoy_captain/exit_pages_controller.rb +20 -0
  51. data/app/controllers/ahoy_captain/filters/base_controller.rb +17 -0
  52. data/app/controllers/ahoy_captain/filters/locations_controller.rb +11 -0
  53. data/app/controllers/ahoy_captain/filters/operating_systems/names_controller.rb +13 -0
  54. data/app/controllers/ahoy_captain/filters/operating_systems/versions_controller.rb +13 -0
  55. data/app/controllers/ahoy_captain/filters/pages/actions_controller.rb +13 -0
  56. data/app/controllers/ahoy_captain/filters/pages/entry_pages_controller.rb +14 -0
  57. data/app/controllers/ahoy_captain/filters/pages/exit_pages_controller.rb +16 -0
  58. data/app/controllers/ahoy_captain/filters/screens_controller.rb +11 -0
  59. data/app/controllers/ahoy_captain/filters/sources_controller.rb +11 -0
  60. data/app/controllers/ahoy_captain/filters/utms_controller.rb +10 -0
  61. data/app/controllers/ahoy_captain/funnels_controller.rb +8 -0
  62. data/app/controllers/ahoy_captain/goals_controller.rb +7 -0
  63. data/app/controllers/ahoy_captain/realtimes_controller.rb +7 -0
  64. data/app/controllers/ahoy_captain/regions_controller.rb +24 -0
  65. data/app/controllers/ahoy_captain/roots_controller.rb +6 -0
  66. data/app/controllers/ahoy_captain/sources_controller.rb +24 -0
  67. data/app/controllers/ahoy_captain/stats/base_controller.rb +67 -0
  68. data/app/controllers/ahoy_captain/stats/bounce_rates_controller.rb +11 -0
  69. data/app/controllers/ahoy_captain/stats/total_pageviews_controller.rb +9 -0
  70. data/app/controllers/ahoy_captain/stats/total_visits_controller.rb +9 -0
  71. data/app/controllers/ahoy_captain/stats/unique_visitors_controller.rb +9 -0
  72. data/app/controllers/ahoy_captain/stats/views_per_visits_controller.rb +19 -0
  73. data/app/controllers/ahoy_captain/stats/visit_durations_controller.rb +9 -0
  74. data/app/controllers/ahoy_captain/stats_controller.rb +7 -0
  75. data/app/controllers/ahoy_captain/top_pages_controller.rb +26 -0
  76. data/app/decorators/ahoy_captain/application_decorator.rb +34 -0
  77. data/app/decorators/ahoy_captain/campaign_decorator.rb +19 -0
  78. data/app/decorators/ahoy_captain/city_decorator.rb +12 -0
  79. data/app/decorators/ahoy_captain/country_decorator.rb +28 -0
  80. data/app/decorators/ahoy_captain/device_decorator.rb +16 -0
  81. data/app/decorators/ahoy_captain/entry_page_decorator.rb +7 -0
  82. data/app/decorators/ahoy_captain/exit_page_decorator.rb +7 -0
  83. data/app/decorators/ahoy_captain/page_decorator.rb +16 -0
  84. data/app/decorators/ahoy_captain/region_decorator.rb +12 -0
  85. data/app/decorators/ahoy_captain/source_decorator.rb +20 -0
  86. data/app/decorators/ahoy_captain/top_page_decorator.rb +7 -0
  87. data/app/helpers/ahoy_captain/application_helper.rb +32 -0
  88. data/app/models/ahoy_captain/current.rb +9 -0
  89. data/app/models/ahoy_captain/rangeable.rb +10 -0
  90. data/app/models/ahoy_captain/url_helpers.rb +6 -0
  91. data/app/models/ahoy_captain/widget.rb +15 -0
  92. data/app/models/concerns/ahoy_captain/range_options.rb +21 -0
  93. data/app/presenters/ahoy_captain/dashboard_presenter.rb +84 -0
  94. data/app/presenters/ahoy_captain/funnel_presenter.rb +68 -0
  95. data/app/presenters/ahoy_captain/goals_presenter.rb +69 -0
  96. data/app/queries/ahoy_captain/application_query.rb +121 -0
  97. data/app/queries/ahoy_captain/entry_pages_query.rb +17 -0
  98. data/app/queries/ahoy_captain/event_query.rb +35 -0
  99. data/app/queries/ahoy_captain/exit_pages_query.rb +17 -0
  100. data/app/queries/ahoy_captain/stats/average_views_per_visit_query.rb +13 -0
  101. data/app/queries/ahoy_captain/stats/average_visit_duration_query.rb +11 -0
  102. data/app/queries/ahoy_captain/stats/bounce_rates_query.rb +17 -0
  103. data/app/queries/ahoy_captain/stats/total_pageviews_query.rb +9 -0
  104. data/app/queries/ahoy_captain/stats/total_visitors_query.rb +9 -0
  105. data/app/queries/ahoy_captain/stats/unique_visitors_query.rb +9 -0
  106. data/app/queries/ahoy_captain/stats/views_per_visit_query.rb +17 -0
  107. data/app/queries/ahoy_captain/stats/visit_duration_query.rb +16 -0
  108. data/app/queries/ahoy_captain/visit_query.rb +32 -0
  109. data/app/views/ahoy_captain/campaigns/index.html+details.erb +4 -0
  110. data/app/views/ahoy_captain/campaigns/index.html.erb +3 -0
  111. data/app/views/ahoy_captain/cities/index.html+details.erb +4 -0
  112. data/app/views/ahoy_captain/cities/index.html.erb +3 -0
  113. data/app/views/ahoy_captain/countries/index.html+details.erb +5 -0
  114. data/app/views/ahoy_captain/countries/index.html.erb +3 -0
  115. data/app/views/ahoy_captain/devices/index.html+details.erb +4 -0
  116. data/app/views/ahoy_captain/devices/index.html.erb +3 -0
  117. data/app/views/ahoy_captain/entry_pages/index.html+details.erb +4 -0
  118. data/app/views/ahoy_captain/entry_pages/index.html.erb +3 -0
  119. data/app/views/ahoy_captain/exit_pages/index.html+details.erb +4 -0
  120. data/app/views/ahoy_captain/exit_pages/index.html.erb +3 -0
  121. data/app/views/ahoy_captain/funnels/index.html.erb +7 -0
  122. data/app/views/ahoy_captain/funnels/show.html.erb +6 -0
  123. data/app/views/ahoy_captain/goals/index.html.erb +39 -0
  124. data/app/views/ahoy_captain/layouts/application.html.erb +127 -0
  125. data/app/views/ahoy_captain/realtimes/show.html.erb +9 -0
  126. data/app/views/ahoy_captain/regions/index.html+details.erb +4 -0
  127. data/app/views/ahoy_captain/regions/index.html.erb +3 -0
  128. data/app/views/ahoy_captain/roots/show.html.erb +179 -0
  129. data/app/views/ahoy_captain/sources/index.html+details.erb +4 -0
  130. data/app/views/ahoy_captain/sources/index.html.erb +3 -0
  131. data/app/views/ahoy_captain/stats/base/index.html.erb +14 -0
  132. data/app/views/ahoy_captain/stats/show.html.erb +57 -0
  133. data/app/views/ahoy_captain/top_pages/index.html+details.erb +4 -0
  134. data/app/views/ahoy_captain/top_pages/index.html.erb +3 -0
  135. data/config/routes.rb +56 -0
  136. data/lib/ahoy_captain/active_record.rb +108 -0
  137. data/lib/ahoy_captain/ahoy/event_methods.rb +114 -0
  138. data/lib/ahoy_captain/ahoy/visit_methods.rb +23 -0
  139. data/lib/ahoy_captain/configuration.rb +43 -0
  140. data/lib/ahoy_captain/engine.rb +24 -0
  141. data/lib/ahoy_captain/funnels.rb +44 -0
  142. data/lib/ahoy_captain/goals.rb +43 -0
  143. data/lib/ahoy_captain/period_collection.rb +115 -0
  144. data/lib/ahoy_captain/railtie.rb +7 -0
  145. data/lib/ahoy_captain/version.rb +1 -3
  146. data/lib/ahoy_captain.rb +50 -4
  147. data/lib/generators/ahoy_captain/install_generator.rb +18 -0
  148. data/lib/generators/ahoy_captain/migration_generator.rb +21 -0
  149. data/lib/generators/ahoy_captain/templates/config.rb.tt +138 -0
  150. data/lib/generators/ahoy_captain/templates/migration.rb.tt +7 -0
  151. metadata +410 -17
  152. data/.rspec +0 -3
  153. data/.rubocop.yml +0 -13
  154. data/CHANGELOG.md +0 -5
  155. data/Gemfile +0 -12
  156. data/ahoy_captain.gemspec +0 -37
  157. data/sig/ahoy_captain.rbs +0 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 103045d516173e382d01f7df721da62cc163c19676d35476a4fe189558c19efb
4
- data.tar.gz: 39edc8bf45c65c2e84a875745c6b32bcc7ad55994504138f93c891b4f6367393
3
+ metadata.gz: 42069eea6d1d5b65765678fa1d352cf462048c09ad5bbe69715f0f147f0a3856
4
+ data.tar.gz: cd0fab7a2a262b89f5688374cb566b482f0a9909b5358db5b02c9d3480f4ed21
5
5
  SHA512:
6
- metadata.gz: 5e8ae65c4ab67133f3b2ad2b14cc56117f5f37f224030f5ac194c4e8dc4a92c34f91cf628548a19fc042e8478b58278d50506e3166bee9b4d1027ff34b377352
7
- data.tar.gz: 415250e00fad914f40911bea06b7a97d047d52b7987bea59a8d0153a0d0cbf175145f7670db5a53ff89cffd58311624b5f291ea2274838c023d51dbc743859fa
6
+ metadata.gz: 464affe93d6d8ebfec8baf55f989a8402a6e265247259894c4f01a22b32047bc3cda4238c3e22ea06a7e6dae088353f23ed1a94cc65899ea24c7096b49a0d031
7
+ data.tar.gz: 22b2352c2a7c9a21b8999292ee1bff64b87bc3306dd6ecb9f7e6f0c787306a3ec2132850c8954f15bac01e830f624a961bee868d7e3c8c5d3718d3b0b6946954
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2023 Partytray
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md CHANGED
@@ -1,31 +1,72 @@
1
1
  # AhoyCaptain
2
2
 
3
- TODO: Delete this and the text below, and describe your gem
3
+ <img src="logo.png" style="max-width:100px" />
4
4
 
5
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/ahoy_captain`. To experiment with that code, run `bin/console` for an interactive prompt.
5
+ A full-featured, mountable analytics dashboard for your Rails app, shamelessly inspired by Plausible Analytics, powered by the Ahoy gem.
6
+
7
+ <a href="https://github.com/joshmn/ahoy_captain/blob/main/ss.jpg"><img src="ss.jpg" style="max-width:300px" /></a>
8
+ ## Notice
9
+
10
+ While this is fine to use in production, it was only built against a PostgreSQL instance. Some of the queries are certainly broken.
11
+
12
+ ## Some assumptions
13
+
14
+ Some hardcoded stuff as of writing; this will be more fully-featured in due time.
6
15
 
7
16
  ## Installation
8
17
 
9
- TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
18
+ ### 1. Do the bundle
19
+
20
+ Drop it in:
21
+
22
+ ```bash
23
+ $ bundle add ahoy_captain
24
+ ```
25
+
26
+ ### 2. Install it
27
+
28
+ ```bash
29
+ $ rails g ahoy_captain:install
30
+ ```
31
+
32
+ ### 3. Star this repo
33
+
34
+ No, seriously, I need all the internet clout I can get.
35
+
36
+ ### 4. Analyze your nightmares
10
37
 
11
- Install the gem and add to the application's Gemfile by executing:
38
+ If you have a large dataset (> 1GB) you probably want some indexes. `rails g ahoy_captain:migration`
12
39
 
13
- $ bundle add UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG
40
+ ## Features
14
41
 
15
- If bundler is not being used to manage dependencies, install the gem by executing:
42
+ * Top sources
43
+ * Top pages, landing pages, and exit pages
44
+ * UTM reporting
45
+ * Top locations, by countries, regions, and cities
46
+ * Top devices, by browser, OS, and device type
47
+ * Goal tracking
48
+ * Funnels
49
+ * Filter by:
50
+ * Page
51
+ * Location
52
+ * Device type
53
+ * OS
54
+ * UTM tags
16
55
 
17
- $ gem install UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG
56
+ ## Coming soon ™️
18
57
 
19
- ## Usage
58
+ * Date comparison
59
+ * More filters
60
+ * CSV exports
20
61
 
21
- TODO: Write usage instructions here
62
+ ## Contributors
22
63
 
23
- ## Development
64
+ This was built during the Rails Hackathon in July 2023 with [afogel](https://github.com/afogel) and [dnoetz](https://github.com/dnoetz).
24
65
 
25
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
66
+ ## Contributions
26
67
 
27
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
68
+ Please and thank you in advance!
28
69
 
29
- ## Contributing
70
+ ## License
30
71
 
31
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/ahoy_captain.
72
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile CHANGED
@@ -1,12 +1,3 @@
1
- # frozen_string_literal: true
1
+ require "bundler/setup"
2
2
 
3
3
  require "bundler/gem_tasks"
4
- require "rspec/core/rake_task"
5
-
6
- RSpec::Core::RakeTask.new(:spec)
7
-
8
- require "rubocop/rake_task"
9
-
10
- RuboCop::RakeTask.new
11
-
12
- task default: %i[spec rubocop]
@@ -0,0 +1,199 @@
1
+ <?xml version="1.0" standalone="no"?>
2
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
3
+ "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
4
+ <svg version="1.0" xmlns="http://www.w3.org/2000/svg"
5
+ width="819.000000pt" height="819.000000pt" viewBox="0 0 819.000000 819.000000"
6
+ preserveAspectRatio="xMidYMid meet">
7
+ <metadata>
8
+ Created by potrace 1.14, written by Peter Selinger 2001-2017
9
+ </metadata>
10
+ <g transform="translate(0.000000,819.000000) scale(0.100000,-0.100000)"
11
+ fill="#000000" stroke="none">
12
+ <path d="M3933 8112 c-7 -5 -35 -7 -63 -6 -27 1 -50 -2 -50 -7 0 -4 -12 -7
13
+ -27 -7 -16 0 -39 -4 -53 -9 -14 -5 -37 -9 -52 -8 -16 0 -28 -3 -28 -8 0 -4 -4
14
+ -5 -10 -2 -5 3 -10 1 -10 -5 0 -6 -5 -8 -10 -5 -6 4 -18 1 -27 -7 -14 -11 -16
15
+ -11 -8 2 5 9 0 8 -13 -4 -12 -11 -22 -16 -22 -11 0 4 -6 2 -14 -4 -11 -9 -15
16
+ -9 -19 1 -4 11 -6 10 -6 0 -1 -7 -5 -10 -11 -7 -5 3 -10 1 -10 -5 0 -6 -4 -9
17
+ -9 -5 -5 3 -12 1 -16 -5 -4 -6 -11 -8 -16 -5 -5 4 -9 1 -9 -6 0 -6 -8 -9 -20
18
+ -6 -11 3 -20 1 -20 -4 0 -5 -9 -9 -20 -9 -11 0 -20 -4 -20 -10 0 -5 -9 -10
19
+ -19 -10 -11 0 -23 -5 -26 -10 -4 -6 -11 -8 -16 -5 -5 4 -9 1 -9 -5 0 -6 -4 -9
20
+ -9 -5 -5 3 -17 -2 -25 -11 -12 -11 -16 -12 -16 -2 0 9 -2 10 -8 2 -4 -7 -26
21
+ -20 -49 -30 -24 -10 -43 -22 -43 -26 0 -4 -6 -5 -13 -2 -7 2 -23 -5 -35 -16
22
+ -12 -11 -24 -20 -27 -20 -29 1 -35 -1 -29 -12 5 -7 2 -9 -9 -5 -8 3 -21 -1
23
+ -27 -8 -7 -8 -16 -12 -21 -9 -5 3 -9 0 -9 -6 0 -6 -4 -9 -8 -6 -5 3 -19 -4
24
+ -30 -15 -12 -10 -24 -19 -27 -19 -28 1 -35 -1 -30 -10 3 -5 -1 -11 -9 -11 -33
25
+ -3 -37 -4 -45 -17 -5 -8 -11 -13 -15 -13 -20 3 -26 0 -21 -9 3 -6 -1 -7 -9 -4
26
+ -9 3 -23 -1 -31 -10 -9 -9 -23 -16 -31 -16 -8 0 -14 -4 -14 -9 0 -7 -36 -22
27
+ -49 -20 -2 0 -7 -3 -10 -8 -11 -16 -64 -46 -56 -32 5 8 -4 3 -18 -10 -15 -14
28
+ -27 -21 -27 -17 0 4 -12 -2 -26 -13 -15 -11 -31 -18 -37 -14 -6 4 -8 0 -4 -11
29
+ 5 -13 3 -16 -11 -12 -12 4 -29 -3 -47 -18 -16 -14 -32 -23 -37 -20 -4 3 -8 0
30
+ -8 -6 0 -6 -4 -9 -8 -6 -5 3 -19 -4 -31 -15 -12 -12 -25 -19 -28 -16 -3 3 -14
31
+ -3 -26 -13 -11 -11 -24 -17 -29 -14 -4 3 -8 1 -8 -4 0 -4 -16 -14 -35 -22 -19
32
+ -8 -35 -17 -35 -21 0 -4 -13 -12 -30 -18 -16 -5 -30 -15 -30 -20 0 -6 -7 -8
33
+ -15 -5 -8 4 -15 1 -15 -5 0 -6 -9 -11 -20 -11 -10 0 -23 -7 -28 -16 -5 -9 -13
34
+ -17 -18 -17 -5 -1 -8 -6 -6 -10 1 -5 -2 -6 -7 -3 -5 4 -12 2 -16 -4 -3 -5 -15
35
+ -10 -26 -10 -10 0 -19 -5 -19 -11 0 -5 -5 -7 -10 -4 -5 3 -10 2 -10 -4 0 -5
36
+ -16 -15 -34 -22 -19 -7 -32 -17 -29 -23 4 -5 -1 -7 -10 -3 -10 3 -17 2 -17 -3
37
+ 0 -5 -11 -12 -25 -16 -14 -3 -22 -10 -19 -15 3 -5 -4 -9 -16 -9 -11 0 -23 -7
38
+ -26 -15 -4 -8 -14 -15 -24 -15 -10 0 -21 -7 -24 -16 -4 -9 -9 -14 -13 -11 -3
39
+ 4 -15 -4 -27 -16 -12 -12 -25 -25 -31 -27 -5 -3 -9 -9 -7 -13 1 -5 -2 -6 -8
40
+ -2 -5 3 -13 -1 -16 -9 -3 -9 -9 -14 -14 -11 -4 2 -13 -7 -20 -20 -7 -13 -17
41
+ -22 -21 -20 -4 3 -10 -4 -14 -15 -3 -11 -11 -20 -16 -20 -5 0 -9 -7 -9 -16 0
42
+ -8 -4 -13 -9 -10 -5 3 -14 -8 -21 -24 -7 -16 -17 -27 -23 -23 -5 3 -7 2 -4 -4
43
+ 4 -5 -5 -25 -18 -43 -13 -18 -22 -36 -20 -40 3 -5 -2 -10 -10 -14 -8 -3 -12
44
+ -11 -9 -19 3 -8 -2 -20 -11 -27 -13 -11 -13 -15 -3 -21 10 -7 9 -9 -3 -9 -11
45
+ 0 -15 -8 -14 -25 2 -14 0 -25 -4 -25 -6 0 -12 -38 -13 -80 -1 -8 -5 -21 -11
46
+ -27 -6 -9 -6 -13 2 -13 7 0 10 -4 6 -9 -11 -19 -13 -91 -3 -84 7 4 8 1 3 -8
47
+ -11 -17 -16 -59 -6 -59 4 0 7 -9 8 -20 1 -13 -2 -18 -10 -13 -9 5 -9 3 -1 -8
48
+ 6 -8 12 -21 12 -29 1 -24 7 -44 16 -59 6 -10 5 -12 -3 -7 -8 6 -8 1 1 -18 6
49
+ -14 15 -26 19 -26 4 0 6 -9 4 -20 -2 -11 1 -20 6 -20 6 0 7 -4 4 -10 -3 -5 -1
50
+ -10 4 -10 6 0 11 -9 11 -20 0 -11 5 -20 10 -20 6 0 10 -7 10 -15 0 -8 5 -15
51
+ 10 -15 6 0 10 -7 10 -15 0 -8 5 -15 10 -15 6 0 10 -7 10 -15 0 -8 4 -15 8 -15
52
+ 5 0 15 -11 22 -25 7 -14 20 -25 27 -25 8 0 12 -3 10 -7 -3 -5 8 -19 25 -33 16
53
+ -14 38 -33 49 -42 10 -10 22 -18 25 -18 10 0 33 -27 29 -34 -3 -3 -2 -4 1 -1
54
+ 4 2 16 -1 28 -7 11 -7 24 -11 29 -10 4 1 7 -2 7 -7 0 -10 27 -21 44 -20 5 0
55
+ 12 -3 15 -8 9 -12 54 -36 47 -25 -3 6 12 2 33 -9 22 -10 46 -19 54 -19 7 0 17
56
+ -3 21 -7 6 -7 29 -13 54 -15 6 -1 12 -4 12 -7 0 -3 14 -12 30 -19 17 -8 28
57
+ -17 25 -22 -3 -4 2 -13 9 -19 8 -7 17 -30 21 -52 4 -21 12 -39 18 -39 7 0 5
58
+ -5 -3 -10 -9 -6 -10 -10 -3 -10 7 0 10 -13 9 -35 -2 -19 1 -32 5 -29 5 3 9 -6
59
+ 9 -20 0 -32 1 -35 17 -66 10 -19 10 -26 0 -32 -9 -6 -9 -8 1 -8 7 0 10 -4 7
60
+ -10 -3 -5 -2 -10 2 -10 8 0 12 -15 15 -57 1 -7 5 -13 9 -13 4 0 10 -12 14 -27
61
+ 6 -26 56 -83 72 -83 20 0 43 -12 43 -22 0 -6 3 -9 6 -5 14 14 41 -26 39 -59
62
+ -1 -19 3 -34 9 -34 6 0 6 -4 -1 -11 -14 -14 -5 -42 10 -32 6 4 7 1 2 -8 -12
63
+ -19 -12 -63 0 -71 6 -5 5 -8 -2 -8 -7 0 -10 -6 -7 -14 3 -8 0 -17 -5 -21 -6
64
+ -3 -9 -11 -5 -16 11 -18 -5 -54 -29 -65 -13 -6 -40 -10 -60 -9 -20 0 -37 -2
65
+ -37 -6 0 -4 -12 -6 -26 -6 -15 0 -29 -5 -33 -10 -3 -4 -18 -8 -33 -8 -15 1
66
+ -25 -2 -23 -6 3 -3 -14 -11 -36 -18 -26 -8 -37 -16 -33 -24 4 -7 4 -9 -1 -5
67
+ -4 4 -14 3 -22 -3 -7 -6 -13 -8 -13 -3 -1 5 -7 -2 -15 -16 -8 -14 -14 -19 -15
68
+ -12 0 7 -10 2 -22 -10 -13 -13 -22 -26 -21 -30 2 -5 -2 -8 -8 -8 -12 0 -69
69
+ -60 -69 -73 0 -4 -5 -5 -12 -1 -6 4 -8 3 -4 -4 3 -6 -1 -17 -9 -25 -8 -9 -15
70
+ -20 -15 -26 0 -6 -5 -11 -11 -11 -5 0 -7 -4 -4 -10 3 -6 1 -10 -5 -10 -6 0 -8
71
+ -4 -5 -10 3 -5 1 -10 -6 -10 -7 0 -9 -3 -6 -7 4 -3 -1 -15 -10 -26 -13 -15
72
+ -14 -18 -3 -12 13 8 13 7 0 -10 -8 -10 -15 -21 -15 -24 0 -3 -2 -5 -5 -4 -5 1
73
+ -11 -51 -14 -119 0 -16 -4 -28 -7 -28 -3 0 -4 -16 -2 -36 3 -27 1 -34 -9 -28
74
+ -10 6 -10 4 -2 -7 5 -8 11 -27 13 -44 3 -47 6 -59 15 -74 7 -10 5 -12 -3 -6
75
+ -8 4 -7 -4 3 -24 9 -17 14 -31 10 -31 -4 0 -2 -6 4 -12 11 -14 12 -17 12 -49
76
+ 0 -11 4 -18 9 -15 11 7 23 -22 15 -35 -3 -5 0 -9 5 -9 6 0 11 -6 11 -14 0 -8
77
+ 4 -16 8 -18 4 -1 18 -20 31 -40 12 -21 27 -38 32 -38 5 0 9 -7 9 -17 0 -9 2
78
+ -14 5 -11 3 3 28 -17 54 -43 27 -27 54 -49 59 -49 5 0 13 -7 16 -16 3 -8 10
79
+ -12 16 -9 5 3 10 1 10 -4 0 -6 9 -20 20 -31 12 -12 18 -25 15 -30 -3 -4 -1
80
+ -10 4 -12 9 -3 18 -50 26 -128 2 -14 3 -29 4 -33 1 -5 6 -5 13 -1 6 4 8 3 4
81
+ -4 -9 -15 16 -102 27 -95 6 3 7 1 3 -6 -4 -6 -2 -18 4 -26 7 -8 9 -15 6 -15
82
+ -4 0 0 -7 8 -16 9 -8 14 -20 11 -25 -4 -5 -1 -9 4 -9 6 0 11 -7 11 -15 0 -8 5
83
+ -15 11 -15 5 0 7 -5 4 -10 -3 -6 1 -13 9 -16 9 -3 13 -10 10 -15 -3 -5 -1 -9
84
+ 5 -9 5 0 13 -10 17 -22 4 -13 14 -34 23 -48 18 -27 24 -166 9 -187 -5 -7 -5
85
+ -14 2 -18 8 -5 8 -11 -1 -21 -9 -11 -9 -14 0 -14 8 0 9 -5 2 -19 -6 -10 -7
86
+ -21 -4 -25 11 -10 12 -37 2 -30 -5 3 -4 -7 1 -21 13 -34 13 -38 -2 -29 -7 5
87
+ -8 3 -2 -7 4 -8 9 -32 11 -54 1 -22 3 -57 4 -77 1 -24 6 -35 13 -31 6 4 5 -1
88
+ -3 -10 -9 -12 -10 -17 -2 -17 7 0 10 -4 7 -8 -3 -4 0 -26 5 -48 6 -21 10 -47
89
+ 10 -57 0 -9 4 -17 9 -17 4 0 7 -8 6 -17 -3 -17 33 -132 44 -143 3 -3 6 -14 8
90
+ -24 2 -11 10 -29 17 -40 8 -12 14 -25 15 -31 3 -32 13 -54 23 -48 6 4 8 0 4
91
+ -10 -3 -9 -2 -19 3 -22 6 -3 15 -15 21 -27 9 -16 8 -19 -3 -12 -7 4 -5 -2 5
92
+ -13 9 -11 17 -24 17 -30 0 -6 7 -14 16 -17 8 -3 12 -10 9 -16 -3 -5 -1 -10 4
93
+ -10 6 0 11 -6 11 -13 0 -7 9 -23 20 -35 12 -12 17 -22 13 -22 -4 0 4 -10 18
94
+ -23 14 -12 24 -28 22 -34 -3 -7 2 -13 11 -13 9 0 15 -6 14 -12 -2 -7 3 -12 10
95
+ -10 8 1 11 -2 8 -6 -3 -5 4 -20 15 -33 62 -72 109 -119 119 -119 6 0 9 -4 6
96
+ -8 -3 -5 4 -15 16 -23 11 -8 31 -23 43 -34 58 -51 75 -64 75 -58 0 3 8 -3 17
97
+ -13 22 -24 73 -57 73 -47 0 4 7 -2 15 -13 8 -10 15 -15 15 -10 0 5 9 -1 19
98
+ -13 11 -13 24 -20 30 -17 5 3 17 -2 27 -11 10 -10 20 -18 23 -18 3 -1 33 -14
99
+ 66 -30 33 -16 65 -29 70 -30 6 0 13 -3 16 -8 3 -4 19 -9 36 -10 17 -1 29 -5
100
+ 26 -9 -2 -5 4 -8 15 -8 10 0 22 -3 26 -7 4 -4 19 -9 34 -11 15 -2 29 -7 32
101
+ -11 3 -4 20 -7 38 -5 18 1 30 -1 27 -6 -2 -4 5 -8 18 -8 23 -1 62 -9 62 -13 0
102
+ -1 11 -2 25 -3 14 -1 31 -5 38 -10 7 -4 19 -5 27 -2 8 3 15 1 15 -5 0 -6 4 -8
103
+ 9 -5 11 7 72 -54 65 -66 -3 -4 -1 -8 4 -8 11 0 13 -10 16 -105 2 -44 -1 -88
104
+ -6 -99 -8 -14 -7 -17 3 -11 8 6 10 4 4 -6 -11 -18 -13 -79 -2 -79 4 0 3 -10
105
+ -4 -21 -8 -15 -8 -19 0 -15 7 5 11 -4 11 -23 0 -17 4 -31 10 -31 5 0 7 -7 4
106
+ -15 -4 -8 -1 -15 5 -15 6 0 11 -3 10 -7 -2 -16 14 -53 22 -53 5 0 9 -9 9 -20
107
+ 0 -10 8 -24 18 -29 9 -6 14 -11 10 -11 -12 0 65 -80 97 -100 52 -31 127 -52
108
+ 212 -56 73 -4 85 -2 103 16 18 18 20 29 15 78 -4 31 -2 94 4 139 21 148 74
109
+ 274 193 458 115 177 133 270 89 440 l-23 90 22 57 c12 32 26 58 32 58 6 0 87
110
+ -24 182 -54 283 -90 672 -194 683 -182 3 3 23 69 44 148 172 629 215 761 320
111
+ 994 l60 131 -16 129 c-22 186 -21 196 20 253 38 53 102 196 82 184 -9 -5 -9
112
+ -3 0 9 7 8 10 18 6 21 -3 4 -1 7 5 7 6 0 14 25 17 55 4 30 10 53 14 51 4 -3 7
113
+ 20 7 50 -1 30 3 54 7 54 5 0 5 5 2 10 -3 6 -1 10 4 10 6 0 11 6 11 14 0 15 62
114
+ 66 80 66 6 0 9 4 5 9 -3 5 3 12 13 15 23 8 113 92 105 100 -3 3 2 8 11 12 9 3
115
+ 13 10 10 15 -3 5 2 9 10 9 9 0 15 3 14 8 -3 14 22 62 32 62 6 0 9 4 5 9 -3 5
116
+ 1 16 9 24 8 8 12 17 9 21 -3 3 -1 12 5 19 7 8 9 17 5 20 -3 4 -1 7 6 7 8 0 11
117
+ 7 7 15 -3 8 -2 15 2 15 7 0 12 24 15 68 0 6 4 12 8 12 4 0 6 8 4 18 -1 9 2 27
118
+ 6 40 6 15 5 22 -3 22 -7 0 -3 7 8 16 10 8 13 12 5 8 -11 -5 -13 -2 -9 14 3 12
119
+ 1 23 -6 26 -8 3 -8 5 0 5 8 1 9 12 4 35 -4 19 -5 37 -2 40 2 3 -1 12 -8 21 -7
120
+ 8 -9 15 -5 15 4 0 1 10 -7 23 -8 12 -13 32 -12 45 1 12 -3 22 -8 22 -6 0 -8 7
121
+ -4 15 3 8 1 15 -4 15 -6 0 -10 6 -10 14 0 7 -7 19 -15 26 -9 7 -13 15 -11 18
122
+ 3 2 -2 11 -10 19 -8 8 -11 20 -7 26 3 7 3 9 -2 5 -10 -9 -41 32 -37 47 2 6 1
123
+ 7 -3 3 -9 -8 -35 14 -28 25 2 4 -1 7 -7 7 -7 0 -25 14 -41 31 -16 17 -29 29
124
+ -29 26 0 -4 -12 5 -26 18 -15 14 -33 25 -40 25 -8 0 -14 5 -14 11 0 5 -4 8 -9
125
+ 5 -4 -3 -17 1 -27 9 -10 8 -27 15 -36 15 -10 0 -18 5 -18 11 0 6 -9 9 -20 6
126
+ -11 -3 -20 -1 -20 5 0 5 -4 7 -9 3 -5 -3 -12 -2 -15 3 -2 4 -25 8 -50 7 -25 0
127
+ -46 3 -46 8 0 4 -4 6 -8 3 -15 -9 -32 10 -34 37 -1 15 -5 27 -9 27 -8 0 -5 42
128
+ 4 58 5 6 4 12 -1 12 -5 0 -9 7 -9 15 0 8 5 15 11 15 6 0 4 5 -4 10 -8 5 -10
129
+ 10 -4 10 6 0 9 18 8 43 -3 67 5 94 35 107 14 6 27 11 29 11 1 -1 8 3 15 9 6 6
130
+ 24 20 40 31 15 11 25 24 22 29 -3 5 -1 10 5 12 10 3 19 31 24 71 1 10 9 23 17
131
+ 28 12 7 12 9 -1 9 -9 0 -11 3 -5 8 9 7 12 18 17 67 1 11 7 23 13 27 6 5 5 8
132
+ -4 8 -10 0 -10 6 4 26 9 15 12 23 5 19 -9 -5 -9 -2 -1 14 6 12 16 30 22 42 8
133
+ 17 8 19 -3 13 -10 -6 -10 -5 -2 7 6 8 10 24 9 36 -1 12 3 20 8 16 5 -3 7 8 5
134
+ 23 -2 19 1 28 10 27 6 -2 12 3 12 10 0 30 52 57 150 79 8 1 17 6 20 10 3 3 19
135
+ 8 35 12 55 9 75 18 68 29 -4 6 2 8 15 5 12 -4 22 -3 22 1 0 3 9 12 21 19 17
136
+ 11 20 11 14 0 -3 -7 3 -3 14 10 11 13 25 21 30 18 5 -3 12 1 15 10 3 8 11 12
137
+ 17 8 7 -4 10 -3 7 2 -3 5 0 15 8 22 8 7 14 10 14 6 0 -4 10 5 23 19 12 15 28
138
+ 27 35 27 7 0 12 4 12 9 0 6 11 23 25 38 36 40 37 43 73 101 17 28 29 52 25 52
139
+ -4 0 2 7 13 15 10 8 15 15 11 15 -5 0 1 13 12 28 15 22 17 32 9 40 -9 9 -9 12
140
+ 0 12 7 0 12 9 12 20 0 11 3 20 8 20 4 0 7 15 6 32 -1 18 4 37 10 41 8 6 7 10
141
+ -2 14 -10 4 -10 8 -1 19 7 9 8 19 2 27 -8 11 -10 29 -3 42 7 14 5 31 -5 37 -7
142
+ 5 -6 8 1 8 12 0 12 29 -1 49 -6 10 -4 12 5 6 9 -6 11 -4 5 4 -4 7 -9 29 -12
143
+ 49 -3 21 -6 45 -7 55 -1 10 -7 22 -12 25 -5 4 -9 17 -8 29 1 12 -3 20 -7 17
144
+ -5 -3 -9 3 -9 13 0 10 -7 26 -15 36 -7 11 -11 25 -8 33 3 8 -2 17 -11 20 -9 3
145
+ -16 12 -16 20 0 8 -5 14 -12 14 -6 0 -9 3 -5 6 6 7 4 10 -42 63 -17 19 -27 39
146
+ -24 44 3 6 1 7 -5 3 -6 -3 -16 3 -22 15 -7 11 -14 19 -16 16 -3 -3 -20 10 -39
147
+ 28 -18 19 -36 31 -39 28 -3 -4 -6 1 -6 10 0 10 -3 17 -7 16 -18 -3 -52 14 -47
148
+ 22 3 5 -1 9 -9 9 -8 0 -26 11 -41 25 -29 27 -41 32 -30 13 4 -7 -6 -1 -23 15
149
+ -16 15 -37 27 -46 27 -9 0 -17 5 -17 10 0 6 -7 10 -16 10 -9 0 -12 5 -8 12 4
150
+ 7 3 8 -5 4 -6 -4 -25 2 -41 14 -17 12 -30 18 -30 13 0 -4 -4 -3 -8 2 -13 19
151
+ -76 60 -68 44 4 -7 1 -6 -7 3 -8 10 -17 15 -20 11 -4 -3 -9 2 -13 11 -3 9 -10
152
+ 14 -14 11 -5 -3 -11 -1 -15 5 -3 5 -15 10 -26 10 -11 0 -18 4 -15 9 5 8 -11
153
+ 19 -36 26 -5 2 -11 4 -15 5 -5 1 -12 2 -16 1 -4 -1 -10 6 -13 14 -3 8 -10 12
154
+ -15 9 -5 -3 -9 0 -9 6 0 6 -5 8 -10 5 -6 -3 -13 1 -16 9 -3 9 -15 16 -25 16
155
+ -10 0 -19 5 -19 10 0 6 -8 10 -18 10 -11 0 -29 9 -42 20 -13 11 -28 20 -35 20
156
+ -6 0 -20 8 -31 18 -10 9 -28 19 -39 20 -11 2 -21 8 -23 13 -2 5 -10 9 -18 9
157
+ -8 0 -14 5 -14 10 0 6 -9 10 -20 10 -11 0 -20 5 -20 10 0 6 -7 10 -16 10 -9 0
158
+ -12 5 -8 12 4 7 3 8 -4 4 -6 -4 -28 4 -49 16 -21 13 -51 30 -68 38 -16 8 -33
159
+ 19 -37 25 -4 5 -8 6 -8 1 0 -5 -9 -1 -20 9 -11 10 -20 14 -20 10 0 -4 -8 -1
160
+ -17 7 -10 8 -33 21 -50 29 -18 8 -33 18 -33 22 0 5 -4 6 -9 2 -5 -3 -12 -1
161
+ -16 5 -4 6 -11 8 -16 5 -5 -4 -9 1 -9 10 0 10 -5 13 -12 9 -6 -4 -17 1 -24 10
162
+ -7 10 -14 16 -16 15 -6 -4 -48 13 -48 20 0 4 -10 8 -22 8 -13 1 -24 7 -26 13
163
+ -2 5 -12 10 -23 10 -10 0 -19 5 -19 12 0 6 -3 8 -7 5 -3 -4 -15 0 -25 10 -10
164
+ 9 -21 13 -24 10 -4 -3 -13 1 -21 9 -8 8 -19 12 -24 9 -5 -4 -9 -1 -9 5 0 7 -7
165
+ 10 -15 6 -8 -3 -15 -1 -15 4 0 6 -7 10 -15 10 -8 0 -15 5 -15 10 0 6 -13 10
166
+ -29 10 -16 0 -34 7 -41 15 -7 8 -16 12 -21 9 -5 -2 -21 3 -36 13 -15 10 -28
167
+ 17 -30 16 -10 -9 -33 -1 -27 9 5 7 2 9 -8 5 -9 -3 -21 0 -28 6 -10 9 -11 9 -5
168
+ -3 7 -12 6 -13 -6 -4 -8 6 -27 15 -44 19 -16 5 -37 10 -45 12 -8 2 -21 5 -27
169
+ 5 -7 1 -13 4 -13 8 0 4 -22 6 -49 6 -27 -1 -47 2 -45 6 2 4 0 9 -5 12 -5 4
170
+ -13 1 -17 -5 -5 -8 -9 -8 -14 1 -4 7 -28 14 -52 14 -24 1 -49 5 -56 10 -7 4
171
+ -12 2 -12 -6 0 -10 -2 -10 -9 1 -6 9 -10 10 -16 2 -5 -9 -10 -9 -21 0 -11 9
172
+ -14 9 -14 -1 0 -10 -3 -10 -15 0 -12 10 -15 10 -15 0 0 -9 -3 -10 -13 -1 -10
173
+ 8 -17 7 -27 -2 -10 -9 -11 -9 -6 1 8 13 -19 13 -41 -1 -6 -4 -15 -3 -18 3 -5
174
+ 8 -11 7 -21 -1 -11 -9 -14 -9 -14 1 0 10 -3 10 -15 0 -11 -9 -15 -9 -15 -1 0
175
+ 9 -5 9 -21 0 -15 -8 -19 -8 -15 0 8 13 6 14 -11 3z m-139 -5820 c3 -5 -1 -9
176
+ -9 -9 -8 0 -12 4 -9 9 3 4 7 8 9 8 2 0 6 -4 9 -8z m14 -32 c-7 -10 -14 -17
177
+ -17 -15 -4 4 18 35 25 35 2 0 -1 -9 -8 -20z m62 8 c0 -11 -12 -10 -30 2 -12 7
178
+ -10 9 8 7 12 0 22 -5 22 -9z m-17 -22 c15 2 16 0 6 -12 -7 -8 -7 -14 -1 -14 5
179
+ 0 17 10 26 23 13 16 14 17 7 2 -6 -13 -5 -28 5 -47 8 -15 12 -28 8 -28 -4 0 0
180
+ -8 7 -17 12 -16 12 -17 -2 -6 -12 10 -20 9 -37 -3 -12 -8 -22 -20 -22 -26 0
181
+ -6 8 -4 18 5 12 11 22 13 32 7 8 -5 10 -10 4 -10 -6 0 -16 -10 -23 -22 -11
182
+ -23 -11 -23 -5 2 7 25 6 25 -14 7 -12 -11 -26 -16 -33 -11 -9 5 -10 2 -5 -12
183
+ 5 -14 4 -16 -4 -8 -9 9 -9 17 -1 32 15 27 14 39 -1 24 -17 -17 -31 -15 -24 3
184
+ 3 8 -2 19 -12 24 -11 7 -12 11 -4 11 7 0 10 5 7 10 -4 6 -10 8 -15 5 -5 -3
185
+ -12 -1 -15 5 -4 6 -1 9 6 8 16 -4 19 24 4 35 -6 5 -4 7 5 4 8 -2 14 -10 13
186
+ -16 -2 -6 5 -11 14 -11 9 0 13 3 10 6 -3 4 -1 16 4 27 6 11 14 15 18 10 3 -6
187
+ 14 -9 24 -7z m-73 -101 c0 -2 -9 -3 -19 -2 -11 0 -18 5 -15 9 4 6 34 0 34 -7z
188
+ m86 -88 c1 -9 -2 -15 -7 -11 -5 3 -7 9 -4 14 3 4 0 11 -5 13 -6 3 -5 4 1 3 7
189
+ -1 13 -10 15 -19z"/>
190
+ <path d="M3815 2231 c-3 -3 5 -11 17 -18 13 -7 16 -13 8 -12 -8 1 -26 1 -39 0
191
+ -38 -2 -14 -16 30 -17 22 -1 38 1 35 4 -3 3 -1 11 4 18 8 9 6 12 -7 10 -10 0
192
+ -17 4 -16 10 2 11 -23 15 -32 5z"/>
193
+ <path d="M3871 2193 c5 -28 -1 -35 -18 -22 -10 8 -14 9 -10 2 4 -6 3 -14 -2
194
+ -17 -5 -3 -11 1 -15 9 -3 8 -12 12 -21 9 -11 -5 -13 -10 -5 -19 5 -6 6 -16 2
195
+ -21 -4 -4 0 -3 10 4 11 9 18 10 23 3 9 -15 65 28 58 45 -5 14 -24 20 -22 7z"/>
196
+ <path d="M3830 2110 c0 -5 5 -10 11 -10 5 0 7 5 4 10 -3 6 -8 10 -11 10 -2 0
197
+ -4 -4 -4 -10z"/>
198
+ </g>
199
+ </svg>
@@ -0,0 +1,4 @@
1
+ import "@hotwired/turbo-rails"
2
+ import "controllers"
3
+ import "chartkick"
4
+ import "Chart.bundle"
@@ -0,0 +1,9 @@
1
+ import { Application } from "@hotwired/stimulus"
2
+
3
+ const application = Application.start()
4
+
5
+ // Configure Stimulus development experience
6
+ application.debug = false
7
+ window.Stimulus = application
8
+
9
+ export { application }
@@ -0,0 +1,14 @@
1
+ import {Controller} from "@hotwired/stimulus"
2
+
3
+ export default class extends Controller {
4
+ connect() {
5
+ if(new URLSearchParams(window.location.search).get("period") === 'realtime') {
6
+ this.element.querySelectorAll('turbo-frame').forEach(frame => {
7
+ setInterval(() => {
8
+ frame.reload()
9
+ }, 1000 * 30);
10
+ })
11
+ }
12
+ }
13
+
14
+ }
@@ -0,0 +1,18 @@
1
+ import {Controller} from "@hotwired/stimulus"
2
+
3
+ export default class extends Controller {
4
+ static values = {
5
+ target: String
6
+ }
7
+
8
+ connect() {
9
+ this.modal = document.querySelector('#detailsModal');
10
+ this.turboFrame = document.querySelector('#detailsModal turbo-frame');
11
+ }
12
+
13
+ openModal(e) {
14
+ e.preventDefault();
15
+ this.modal.showModal();
16
+ this.turboFrame.src = document.querySelector(this.targetValue).src;
17
+ }
18
+ }
@@ -0,0 +1,14 @@
1
+ import {Controller} from "@hotwired/stimulus"
2
+
3
+ export default class extends Controller {
4
+ static targets = ["label", "close"]
5
+
6
+ setLabel(event) {
7
+ this.labelTarget.innerText = event.target.innerText;
8
+ this.closeTarget.classList.add('hidden');
9
+ }
10
+
11
+ removeHidden(event) {
12
+ this.closeTarget.classList.remove('hidden');
13
+ }
14
+ }
@@ -0,0 +1,145 @@
1
+ import {Controller} from "@hotwired/stimulus"
2
+ import SlimSelect from 'slim-select'
3
+
4
+ export default class extends Controller {
5
+ static values = {
6
+ url: String,
7
+ column: String,
8
+ };
9
+ static targets = ["select", 'predicate'];
10
+ connect() {
11
+ this.selectTargets.forEach(async (target) => {
12
+ const url = target.dataset.filterUrlValue;
13
+ const optionsSearch = this.fetchOptions(url, target);
14
+ const select = await new SlimSelect({
15
+ select: target,
16
+ data: [],
17
+ settings: {
18
+ contentPosition: 'relative',
19
+ contentLocation: target.closest('fieldset'),
20
+ searchText: 'Sorry, no results found',
21
+ searchPlaceholder: 'Type to populate results',
22
+ placeholderText: `Search for ${target.dataset.filterColumnValue}`,
23
+ searchHighlight: true
24
+ },
25
+ events: {
26
+ beforeOpen: async () => {
27
+ if (!this.#hasData(target) & !this.#hasSelections(target) ) {
28
+ const data = await optionsSearch();
29
+ target.slim.setData(data);
30
+ }
31
+ },
32
+ search: async (search, currentData) => {
33
+ const data = await optionsSearch(search);
34
+ const filteredData = data.filter(item => !currentData.some((selectedItem) => selectedItem.text == item.text ))
35
+ return filteredData;
36
+ },
37
+ beforeChange: this.#beforeChange(target, url),
38
+ }
39
+ });
40
+ const json = JSON.parse(target.dataset.filterSelected)
41
+ if(json.length) {
42
+ select.setData(json.map(item => ({ "text": item, "value": item })))
43
+ select.setSelected(json)
44
+ }
45
+ })
46
+ }
47
+
48
+ fetchOptions(url, target) {
49
+ return async (search) => {
50
+ const query = this.#buildQueryFilters(search, target);
51
+ const response = await fetch(`${url}?${query.toString()}`);
52
+ const data = await response.json();
53
+ return data;
54
+ }
55
+ }
56
+
57
+ #buildQueryFilters(search, target) {
58
+ const query = new URLSearchParams(window.location.search);
59
+ const otherFilters = this.selectTargets.filter(filter => filter != target);
60
+ otherFilters.forEach(function(filter) {
61
+ filter.slim.getSelected().forEach(val => {
62
+ const filterName = `${filter.name}[]`
63
+ if(query.has(filterName)) {
64
+ console.log("has the filter")
65
+ if(!query.get(`${filter.name}[]`).includes(val)) {
66
+ query.append(filterName, value)
67
+ }
68
+ } else {
69
+ query.append(filterName, value)
70
+ }
71
+ })
72
+ });
73
+ query.set(`q[${target.dataset.filterColumnValue}_i_cont]`, search || "");
74
+ return query;
75
+ }
76
+
77
+ #beforeChange(target, url) {
78
+ return () => {
79
+ const otherFilters = this.selectTargets.filter(filter => filter != target);
80
+ otherFilters.forEach(async target => {
81
+ if (this.#hasData(target)) {
82
+ const selected = target.slim.getSelected()
83
+ target.slim.setData(selected.map(text => ({ text })))
84
+ // setting data triggers a slim render, so need to also set selected again
85
+ target.slim.setSelected(selected)
86
+ }
87
+ })
88
+ return true;
89
+ }
90
+ }
91
+
92
+ #hasData(target) {
93
+ return target.slim.getData().length !== 0;
94
+ }
95
+
96
+ #hasSelections(target) {
97
+ return target.slim.getSelected().length !== 0;
98
+ }
99
+
100
+ #filtersForQuery() {
101
+ const predicates = this.predicateTargets.map(el => ({name: el.name, column_predicate: el.value}));
102
+ const filterValues = this.selectTargets.map(el => ({name: el.name, selections: el.slim.getSelected()}));
103
+ const mergedData = predicates.map(predicate => {
104
+ const matchingFilter = filterValues.find(filter => filter.name === predicate.name);
105
+ if (matchingFilter.selections.length > 0) {
106
+ return {...predicate, ...matchingFilter}
107
+ }
108
+ }).filter(el => el !== undefined);
109
+ return mergedData;
110
+ }
111
+
112
+ resetFilters(event) {
113
+ this.selectTargets.forEach(el => {
114
+ el.slim.setSelected([])
115
+ })
116
+ this.applyFilters(event)
117
+ }
118
+
119
+ applyFilters(e) {
120
+ e.preventDefault();
121
+ const searchParams = new URLSearchParams(window.location.search);
122
+ const filters = this.#filtersForQuery();
123
+ filters.forEach(filter => {
124
+ const name = `q[${filter.column_predicate}][]`
125
+ const selectedValues = searchParams.getAll(name)
126
+ filter.selections.forEach(selection => {
127
+ if(selectedValues) {
128
+ if(!selectedValues.includes(selection)) {
129
+ searchParams.append(name, selection)
130
+ }
131
+ } else {
132
+ searchParams.append(name, selection)
133
+ }
134
+ })
135
+ });
136
+ ['input[name="start_date"]', 'input[name="end_date"]'].forEach(selector => {
137
+ const el = document.querySelector(selector)
138
+ if(el.value.length) {
139
+ searchParams.delete("period")
140
+ }
141
+ searchParams.set(el.name, el.value);
142
+ });
143
+ Turbo.visit(window.location.pathname.replace(/\/$/, "") + `?${searchParams.toString()}`)
144
+ }
145
+ }
@@ -0,0 +1,17 @@
1
+ import {Controller} from "@hotwired/stimulus"
2
+
3
+ export default class extends Controller {
4
+ static targets = ["delete"];
5
+ static values = {
6
+ columnPredicate: String,
7
+ category: String
8
+ }
9
+
10
+ remove(event) {
11
+ event.preventDefault();
12
+ this.dispatch('remove', {detail: {
13
+ paramKey: `q[${this.columnPredicateValue}]`,
14
+ paramValue: this.categoryValue
15
+ }})
16
+ }
17
+ }