spina-conferences-primer_theme-fork 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +33 -0
  4. data/Rakefile +36 -0
  5. data/app/assets/config/spina_conferences_primer_theme_manifest.js +9 -0
  6. data/app/assets/javascripts/spina/conferences/primer_theme/application.js +7 -0
  7. data/app/assets/javascripts/spina/conferences/primer_theme/controllers/slideshow_controller.es6 +68 -0
  8. data/app/assets/stylesheets/spina/conferences/primer_theme/_custom.sass +0 -0
  9. data/app/assets/stylesheets/spina/conferences/primer_theme/_custom_variables.sass +0 -0
  10. data/app/assets/stylesheets/spina/conferences/primer_theme/application.sass +44 -0
  11. data/app/controllers/spina/conferences/primer_theme/conferences/application_controller.rb +16 -0
  12. data/app/controllers/spina/conferences/primer_theme/conferences/conferences_controller.rb +72 -0
  13. data/app/controllers/spina/conferences/primer_theme/conferences/presentations_controller.rb +42 -0
  14. data/app/controllers/spina/conferences/primer_theme/journal/application_controller.rb +13 -0
  15. data/app/controllers/spina/conferences/primer_theme/journal/articles_controller.rb +69 -0
  16. data/app/controllers/spina/conferences/primer_theme/journal/issues_controller.rb +53 -0
  17. data/app/helpers/spina/conferences/primer_theme/application_helper.rb +36 -0
  18. data/app/helpers/spina/conferences/primer_theme/asset_helper.rb +51 -0
  19. data/app/helpers/spina/conferences/primer_theme/navigations_helper.rb +24 -0
  20. data/app/views/conferences_primer_theme/pages/about.html.haml +92 -0
  21. data/app/views/conferences_primer_theme/pages/committee.html.haml +42 -0
  22. data/app/views/conferences_primer_theme/pages/embedded_form.html.haml +9 -0
  23. data/app/views/conferences_primer_theme/pages/events.html.haml +59 -0
  24. data/app/views/conferences_primer_theme/pages/homepage.html.haml +34 -0
  25. data/app/views/conferences_primer_theme/pages/information.html.haml +5 -0
  26. data/app/views/conferences_primer_theme/pages/show.html.haml +2 -0
  27. data/app/views/layouts/conferences_primer_theme/application.html.haml +7 -0
  28. data/app/views/layouts/spina/conferences/primer_theme/application.html.haml +40 -0
  29. data/app/views/layouts/spina/conferences/primer_theme/conferences/conferences.html.haml +5 -0
  30. data/app/views/layouts/spina/conferences/primer_theme/conferences/presentations.html.haml +6 -0
  31. data/app/views/layouts/spina/conferences/primer_theme/journal/articles.html.haml +2 -0
  32. data/app/views/layouts/spina/conferences/primer_theme/journal/issues.html.haml +2 -0
  33. data/app/views/spina/admin/layout_partables/texts/_form.html.haml +4 -0
  34. data/app/views/spina/application/_cookies.html.haml +7 -0
  35. data/app/views/spina/application/_current_conference_alert.html.haml +4 -0
  36. data/app/views/spina/application/_footer.html.haml +17 -0
  37. data/app/views/spina/application/_footer_content.html.haml +13 -0
  38. data/app/views/spina/application/_list_item.html.haml +1 -0
  39. data/app/views/spina/application/_logo.html.haml +1 -0
  40. data/app/views/spina/application/_mobile_navigation_item.html.haml +12 -0
  41. data/app/views/spina/application/_mobile_navigation_items.html.haml +4 -0
  42. data/app/views/spina/application/_navigation.html.haml +11 -0
  43. data/app/views/spina/application/_navigation_item.html.haml +11 -0
  44. data/app/views/spina/application/_text.html.haml +1 -0
  45. data/app/views/spina/conferences/primer_theme/conferences/conferences/_conference.html.haml +15 -0
  46. data/app/views/spina/conferences/primer_theme/conferences/conferences/_event.html.haml +12 -0
  47. data/app/views/spina/conferences/primer_theme/conferences/conferences/_events.html.haml +16 -0
  48. data/app/views/spina/conferences/primer_theme/conferences/conferences/_header.html.haml +47 -0
  49. data/app/views/spina/conferences/primer_theme/conferences/conferences/_institution.html.haml +5 -0
  50. data/app/views/spina/conferences/primer_theme/conferences/conferences/_institution_logo.html.haml +4 -0
  51. data/app/views/spina/conferences/primer_theme/conferences/conferences/_presentation.html.haml +15 -0
  52. data/app/views/spina/conferences/primer_theme/conferences/conferences/_presentation_type.html.haml +5 -0
  53. data/app/views/spina/conferences/primer_theme/conferences/conferences/_presentations.html.haml +29 -0
  54. data/app/views/spina/conferences/primer_theme/conferences/conferences/_submission_flash.html.haml +10 -0
  55. data/app/views/spina/conferences/primer_theme/conferences/conferences/index.html.haml +23 -0
  56. data/app/views/spina/conferences/primer_theme/conferences/conferences/show.html.haml +37 -0
  57. data/app/views/spina/conferences/primer_theme/conferences/kaminari/_gap.html.haml +2 -0
  58. data/app/views/spina/conferences/primer_theme/conferences/kaminari/_next_page.html.haml +9 -0
  59. data/app/views/spina/conferences/primer_theme/conferences/kaminari/_page.html.haml +10 -0
  60. data/app/views/spina/conferences/primer_theme/conferences/kaminari/_paginator.html.haml +17 -0
  61. data/app/views/spina/conferences/primer_theme/conferences/kaminari/_prev_page.html.haml +9 -0
  62. data/app/views/spina/conferences/primer_theme/conferences/presentations/_abstract.html.haml +2 -0
  63. data/app/views/spina/conferences/primer_theme/conferences/presentations/_attachment.html.haml +5 -0
  64. data/app/views/spina/conferences/primer_theme/conferences/presentations/_attachments.html.haml +5 -0
  65. data/app/views/spina/conferences/primer_theme/conferences/presentations/show.html.haml +18 -0
  66. data/app/views/spina/conferences/primer_theme/journal/articles/_authorship.html.haml +6 -0
  67. data/app/views/spina/conferences/primer_theme/journal/articles/_metadata.html.haml +36 -0
  68. data/app/views/spina/conferences/primer_theme/journal/articles/show.html.haml +70 -0
  69. data/app/views/spina/conferences/primer_theme/journal/issues/_article.html.haml +15 -0
  70. data/app/views/spina/conferences/primer_theme/journal/issues/_issue.html.haml +17 -0
  71. data/app/views/spina/conferences/primer_theme/journal/issues/_issue_cover.html.haml +6 -0
  72. data/app/views/spina/conferences/primer_theme/journal/issues/index.html.haml +30 -0
  73. data/app/views/spina/conferences/primer_theme/journal/issues/show.html.haml +27 -0
  74. data/config/initializers/assets.rb +13 -0
  75. data/config/initializers/mime_types.rb +1 -0
  76. data/config/initializers/primer.rb +3 -0
  77. data/config/initializers/themes/conferences_primer_theme.rb +237 -0
  78. data/config/locales/en.rb +16 -0
  79. data/config/locales/en.yml +198 -0
  80. data/config/routes.rb +17 -0
  81. data/db/migrate/20210206170704_change_current_conference_alert_to_text.rb +46 -0
  82. data/lib/spina/conferences/primer_theme.rb +17 -0
  83. data/lib/spina/conferences/primer_theme/breadcrumbs/builder.rb +34 -0
  84. data/lib/spina/conferences/primer_theme/engine.rb +13 -0
  85. data/lib/spina/conferences/primer_theme/version.rb +9 -0
  86. data/lib/tasks/spina/conferences/primer_theme_tasks.rake +6 -0
  87. metadata +396 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 2b41b19868f22af87cabcb8fa4e7870af8ce7279fd1eab85aa18a04216fe144c
4
+ data.tar.gz: aee63eea74ed9866b3e372787deb9b99d43e7f9b950e00f1c7c6a7c6c695704a
5
+ SHA512:
6
+ metadata.gz: eb0c0e9ecfabed0e692ccb9912f8d4f2eef62b0ebe3d2b296b9ffd23556181334c34d6fc5115704292d631855137ee3634c9f3db18da23e96ac6c3403f25a5ca
7
+ data.tar.gz: 95380eba88dadb590eb19b7167169226c592a0c3d6e81b2f15117046f8e61689d228f6fdd1245c46014bdcb7fab3200a30e245859be89427f6fd8bb5f4f5b84c
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2019 Justin Malčić
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 ADDED
@@ -0,0 +1,33 @@
1
+ # PrimerTheme
2
+ A plugin for `Spina` that provides a frontend for `Spina::Admin::Conferences` using components from `Primer::ViewComponents`.
3
+
4
+ ## Usage
5
+ See the README for `Spina::Admin::Conferences`. Also make sure to ensure compilation of the assets for this gem by adding the following line to your
6
+ `manifest.js` file.
7
+
8
+ ```js
9
+ //= link spina_conferences_primer_theme_manifest.js
10
+ ```
11
+
12
+ ## Installation
13
+ Add this line to your application's Gemfile:
14
+
15
+ ```ruby
16
+ gem 'spina-conferences-primer_theme'
17
+ ```
18
+
19
+ And then execute:
20
+ ```bash
21
+ $ bundle
22
+ ```
23
+
24
+ Or install it yourself as:
25
+ ```bash
26
+ $ gem install spina-conferences-primer_theme
27
+ ```
28
+
29
+ ## Contributing
30
+ Contributions welcome, open an issue first please.
31
+
32
+ ## License
33
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ require 'bundler/setup'
5
+ rescue LoadError
6
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
7
+ end
8
+
9
+ require 'rdoc/task'
10
+
11
+ RDoc::Task.new(:rdoc) do |rdoc|
12
+ rdoc.rdoc_dir = 'rdoc'
13
+ rdoc.title = 'Spina::Conferences::PrimerTheme'
14
+ rdoc.options << '--line-numbers'
15
+ rdoc.rdoc_files.include('README.md')
16
+ rdoc.rdoc_files.include('lib/**/*.rb')
17
+ end
18
+
19
+ APP_RAKEFILE = File.expand_path('test/dummy/Rakefile', __dir__)
20
+ load 'rails/tasks/engine.rake'
21
+
22
+ load 'rails/tasks/statistics.rake'
23
+
24
+ require 'bundler/gem_tasks'
25
+
26
+ require 'rake/testtask'
27
+
28
+ Rake::TestTask.new(:test) do |t|
29
+ t.libs << 'test'
30
+ t.pattern = 'test/**/*_test.rb'
31
+ t.verbose = false
32
+ t.warning = false
33
+ t.ruby_opts = %w[-W:no-deprecated]
34
+ end
35
+
36
+ task default: :test
@@ -0,0 +1,9 @@
1
+ //= link_directory ../stylesheets/spina/conferences/primer_theme .css
2
+ //= link_directory ../javascripts/spina/conferences/primer_theme .js
3
+ //= link spina_admin_conferences_manifest.js
4
+ //= link spina_admin_journal_manifest.js
5
+
6
+ //= link @github/details-dialog-element/dist/index.js
7
+ //= link @github/include-fragment-element/dist/index.js
8
+ //= link @github/filter-input-element/dist/index.js
9
+ //= link @github/details-menu-element/dist/index.js
@@ -0,0 +1,7 @@
1
+ //= require stimulus
2
+ //= require_directory ./controllers
3
+
4
+ // eslint-disable-next-line no-undef
5
+ const application = Stimulus.Application.start()
6
+ // eslint-disable-next-line no-undef
7
+ application.register('slideshow', SlideshowController)
@@ -0,0 +1,68 @@
1
+ // eslint-disable-next-line no-unused-vars,no-undef
2
+ class SlideshowController extends Stimulus.Controller {
3
+ static get targets() {
4
+ return [
5
+ /**
6
+ * @private
7
+ * @property {HTMLElement[]} slideTargets - The slide elements.
8
+ */
9
+ 'slide'
10
+ ]
11
+ }
12
+
13
+ /**
14
+ * Returns the incrementer.
15
+ * @private
16
+ * @return {Number} The value for the incrementer.
17
+ */
18
+ get incrementer() {
19
+ return Number.parseInt(this.data.get('incrementer'))
20
+ }
21
+
22
+ /**
23
+ * Sets the incrementer.
24
+ * @private
25
+ * @param value {Number} The new value for the incrementer.
26
+ */
27
+ set incrementer(value) {
28
+ this.data.set('incrementer', value.toString())
29
+ this.showSlide()
30
+ }
31
+
32
+ /**
33
+ * Hook to start advancing the slides if necessary.
34
+ * @private
35
+ */
36
+ connect() {
37
+ this.showSlide()
38
+ if (this.data.has('advance')) {
39
+ setInterval(() => this.next(), 10000)
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Shows the slide that corresponds to the incrementer and hides all others.
45
+ * @private
46
+ */
47
+ showSlide() {
48
+ this.slideTargets.forEach((target, index) => target.hidden = index !== this.incrementer)
49
+ }
50
+
51
+ /**
52
+ * Increases the incrementer if there are more slides, or sets it to 0 if showing the last slide.
53
+ * @private
54
+ */
55
+ next() {
56
+ const incrementer = this.incrementer
57
+ this.incrementer = incrementer === this.slideTargets.length - 1 ? 0 : incrementer + 1
58
+ }
59
+
60
+ /**
61
+ * Decreases the incrementer if there are more slides, or sets it to 0 if showing the first slide.
62
+ * @private
63
+ */
64
+ previous() {
65
+ const incrementer = this.incrementer
66
+ this.incrementer = incrementer === 0 ? this.slideTargets.length - 1 : incrementer - 1
67
+ }
68
+ }
@@ -0,0 +1,44 @@
1
+ @import "@github/details-dialog-element/dist"
2
+
3
+ @import spina/conferences/primer_theme/custom
4
+ @import "@primer/css"
5
+ @import "@primer/octicons"
6
+ @import spina/conferences/primer_theme/custom_variables
7
+
8
+ @font-face
9
+ font-family: Inter
10
+ font-style: normal
11
+ font-weight: $font-weight-normal
12
+ src: local("Inter"), local("Inter-Regular"), asset-url("@primer/css/fonts/Inter-Regular.woff") format("woff")
13
+ font-display: swap
14
+
15
+
16
+ @font-face
17
+ font-family: Inter
18
+ font-style: normal
19
+ font-weight: $font-weight-semibold
20
+ src: local("Inter Medium"), local("Inter-Medium"), asset-url("@primer/css/fonts/Inter-Medium.woff") format("woff")
21
+ font-display: swap
22
+
23
+ @font-face
24
+ font-family: Inter
25
+ font-style: normal
26
+ font-weight: $font-weight-bold
27
+ src: local("Inter Bold"), local("Inter-Bold"), asset-url("@primer/css/fonts/Inter-Bold.woff") format("woff")
28
+ font-display: swap
29
+
30
+ .admin
31
+ @import "spina/admin_editing"
32
+ #admin_bar
33
+ font-family: 'Metropolis', sans-serif
34
+
35
+ .turbo-progress-bar
36
+ background-color: white
37
+
38
+ address
39
+ font-style: normal
40
+
41
+ #form_frame
42
+ border: none
43
+ width: 100%
44
+ height: 70vh
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spina
4
+ module Conferences
5
+ module PrimerTheme
6
+ module Conferences
7
+ # Base class from which controllers inherit
8
+ class ApplicationController < Spina::ApplicationController
9
+ def cookies_info
10
+ render partial: 'cookies'
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spina
4
+ module Conferences
5
+ module PrimerTheme
6
+ module Conferences
7
+ # User-facing controller for conferences, serving both html and ics
8
+ class ConferencesController < ApplicationController
9
+ before_action :set_conference, :set_tab, :set_presentation_type, :set_presentations, :set_breadcrumb, only: :show
10
+ before_action :set_metadata
11
+
12
+ def index
13
+ @conferences = Admin::Conferences::Conference.sorted.includes(institutions: [:logo])
14
+ respond_to do |format|
15
+ format.html
16
+ format.ics { render body: @conferences.to_ics }
17
+ end
18
+ end
19
+
20
+ def show
21
+ add_breadcrumb @conference.name
22
+ respond_to do |format|
23
+ format.html
24
+ format.ics { render body: @conference.to_ics }
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def set_conference
31
+ @conference = Admin::Conferences::Conference.includes(:events, :institutions,
32
+ presentation_types: [:translations],
33
+ presentations: [session: [:room], presenters: [:institution]])
34
+ .find(params[:id])
35
+ @conference.view_context = view_context
36
+ rescue ActiveRecord::RecordNotFound
37
+ send_file Rails.root.join('public/404.html'), type: 'text/html; charset=utf-8', status: 404
38
+ end
39
+
40
+ def set_tab
41
+ @tab = params[:tab] || 'information'
42
+ end
43
+
44
+ def set_presentation_type
45
+ if params[:presentation_type].present?
46
+ @presentation_type = @conference.presentation_types
47
+ .includes(presentations: [session: [:room], presenters: [:institution]])
48
+ .find(params[:presentation_type])
49
+ end
50
+ end
51
+
52
+ def set_presentations
53
+ @presentations = if @presentation_type.present?
54
+ @presentation_type.presentations.page(params[:page]).per(15)
55
+ else
56
+ @conference.presentations.page(params[:page]).per(15)
57
+ end
58
+ end
59
+
60
+ def set_breadcrumb
61
+ add_breadcrumb Admin::Conferences::Conference.model_name.human.pluralize, frontend_conferences_path
62
+ end
63
+
64
+ def set_metadata
65
+ @title = @conference.present? ? @conference.name : Admin::Conferences::Conference.model_name.human(count: 0)
66
+ @description = @conference.present? && @conference.has_content?(:text) ? helpers.strip_tags(@conference.content(:text)) : ''
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spina
4
+ module Conferences
5
+ module PrimerTheme
6
+ module Conferences
7
+ # User-facing controller for presentations, serving both html and ics
8
+ class PresentationsController < ApplicationController
9
+ before_action :set_presentation, :set_conference, :set_breadcrumb
10
+
11
+ def show
12
+ add_breadcrumb @presentation.name
13
+ respond_to do |format|
14
+ format.html
15
+ format.ics { head :not_found }
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def set_presentation
22
+ @presentation = Admin::Conferences::Presentation.includes(:presenters, attachments: [attachment_type: [:translations]])
23
+ .find(params[:id])
24
+ rescue ActiveRecord::RecordNotFound
25
+ send_file Rails.root.join('public/404.html'), type: 'text/html; charset=utf-8', status: 404
26
+ end
27
+
28
+ def set_conference
29
+ @conference = Admin::Conferences::Conference.find(params[:conference_id]) if params[:conference_id].present?
30
+ end
31
+
32
+ def set_breadcrumb
33
+ return if @conference.blank?
34
+
35
+ add_breadcrumb Admin::Conferences::Conference.model_name.human.pluralize, frontend_conferences_path
36
+ add_breadcrumb @conference.name, frontend_conference_path(@conference)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spina
4
+ module Conferences
5
+ module PrimerTheme
6
+ module Journal
7
+ # Base class from which controllers related to the journal plugin inherit
8
+ class ApplicationController < Spina::ApplicationController
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spina
4
+ module Conferences
5
+ module PrimerTheme
6
+ module Journal
7
+ # User-facing controller for journal articles
8
+ class ArticlesController < ApplicationController
9
+ before_action :set_article, :set_issue, :set_journal, :set_licence, :set_breadcrumb, :set_metadata, :require_admin_for_invisible_article
10
+
11
+ def show
12
+ respond_to do |format|
13
+ format.html { add_breadcrumb @article.title }
14
+ format.pdf do
15
+ if @article.has_content?(:attachment)
16
+ redirect_to main_app.url_for(@article.content(:attachment))
17
+ else
18
+ send_file Rails.root.join('public/404.html'), type: 'text/html; charset=utf-8', status: 404
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def set_article
27
+ @article = Admin::Journal::Article.includes(affiliations: [:institution]).find(params[:id])
28
+ rescue ActiveRecord::RecordNotFound
29
+ send_file Rails.root.join('public/404.html'), type: 'text/html; charset=utf-8', status: 404
30
+ end
31
+
32
+ def set_issue
33
+ @issue = Admin::Journal::Issue.includes(:volume, :articles).find(params[:issue_id])
34
+ rescue ActiveRecord::RecordNotFound
35
+ send_file Rails.root.join('public/404.html'), type: 'text/html; charset=utf-8', status: 404
36
+ end
37
+
38
+ def set_journal
39
+ @journal = @issue.volume.journal
40
+ end
41
+
42
+ def set_licence
43
+ @licence = @article.licence
44
+ end
45
+
46
+ def set_breadcrumb
47
+ return if @issue.blank?
48
+
49
+ add_breadcrumb @journal.name, frontend_issues_path
50
+ add_breadcrumb Admin::Journal::Issue.model_name.human.pluralize, frontend_issues_path
51
+ add_breadcrumb t('spina.conferences.primer_theme.journal.volume_issue', volume_number: @issue.volume.number, issue_number: @issue.number),
52
+ frontend_issue_path(@issue.id)
53
+ end
54
+
55
+ def set_metadata
56
+ @title = @article.title
57
+ @description = @article.content(:abstract).try(:to_plain_text)
58
+ end
59
+
60
+ def require_admin_for_invisible_article
61
+ raise ActiveRecord::RecordNotFound unless current_spina_user.present? || @article.visible?
62
+ rescue ActiveRecord::RecordNotFound # TODO: proper 404 handler
63
+ send_file Rails.root.join('public/404.html'), type: 'text/html; charset=utf-8', status: 404
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end