pageflow 15.6.1 → 15.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (274) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +253 -11
  3. data/README.md +1 -2
  4. data/admins/pageflow/entry.rb +28 -57
  5. data/admins/pageflow/membership.rb +5 -1
  6. data/admins/pageflow/user.rb +1 -3
  7. data/app/assets/images/pageflow/admin/icons/buttons/editor.svg +2 -0
  8. data/app/assets/images/pageflow/admin/icons/buttons/preview.svg +2 -0
  9. data/app/assets/images/pageflow/admin/icons/buttons/show_public.svg +2 -0
  10. data/app/assets/images/pageflow/admin/icons/delete.svg +9 -0
  11. data/app/assets/images/pageflow/admin/icons/done.svg +6 -0
  12. data/app/assets/images/pageflow/admin/icons/edit.svg +7 -0
  13. data/app/assets/images/pageflow/admin/icons/editor.svg +2 -0
  14. data/app/assets/images/pageflow/admin/icons/folder.svg +8 -0
  15. data/app/assets/images/pageflow/admin/icons/folder_active.svg +8 -0
  16. data/app/assets/images/pageflow/admin/icons/info.svg +3 -0
  17. data/app/assets/images/pageflow/admin/icons/new.svg +5 -0
  18. data/app/assets/images/pageflow/admin/icons/preview.svg +2 -0
  19. data/app/assets/images/pageflow/admin/icons/published.svg +2 -0
  20. data/app/assets/images/pageflow/admin/icons/published_with_password.svg +2 -0
  21. data/app/assets/images/pageflow/admin/icons/show_public.svg +2 -0
  22. data/app/assets/images/pageflow/editor/blank_entry/logo.png +0 -0
  23. data/app/assets/images/pageflow/themes/default/embed_opt_in.svg +3 -0
  24. data/app/assets/images/pageflow/themes/default/embed_opt_out_info.svg +16 -0
  25. data/app/assets/javascripts/pageflow/admin/entries.js +9 -57
  26. data/app/assets/javascripts/pageflow/dist/editor.js +11890 -0
  27. data/app/assets/javascripts/pageflow/dist/frontend.js +5800 -0
  28. data/app/assets/javascripts/pageflow/dist/react-client.js +22 -0
  29. data/app/assets/javascripts/pageflow/dist/react-server.js +19 -0
  30. data/app/assets/javascripts/pageflow/dist/ui.js +127 -10
  31. data/app/assets/stylesheets/pageflow/admin/active_admin_patches.scss +18 -16
  32. data/app/assets/stylesheets/pageflow/admin/badge_list.scss +25 -35
  33. data/app/assets/stylesheets/pageflow/admin/columns.scss +13 -11
  34. data/app/assets/stylesheets/pageflow/admin/embed_code.scss +1 -4
  35. data/app/assets/stylesheets/pageflow/admin/entries/folders.scss +44 -19
  36. data/app/assets/stylesheets/pageflow/admin/entries.scss +15 -31
  37. data/app/assets/stylesheets/pageflow/admin/features.scss +3 -1
  38. data/app/assets/stylesheets/pageflow/admin/forms.scss +14 -12
  39. data/app/assets/stylesheets/pageflow/admin/hint.scss +8 -14
  40. data/app/assets/stylesheets/pageflow/admin/icon_button.scss +25 -14
  41. data/app/assets/stylesheets/pageflow/admin/icon_link.scss +31 -15
  42. data/app/assets/stylesheets/pageflow/admin/publication_state_indicator.scss +14 -7
  43. data/app/assets/stylesheets/pageflow/admin/status_tags.scss +5 -4
  44. data/app/assets/stylesheets/pageflow/admin/tabs_view.scss +36 -34
  45. data/app/assets/stylesheets/pageflow/admin/tooltip_bubble.scss +15 -11
  46. data/app/assets/stylesheets/pageflow/admin.scss +12 -0
  47. data/app/assets/stylesheets/pageflow/editor/background_positioning.scss +15 -61
  48. data/app/assets/stylesheets/pageflow/editor/base.scss +28 -13
  49. data/app/assets/stylesheets/pageflow/editor/blank_entry.scss +6 -6
  50. data/app/assets/stylesheets/pageflow/editor/change_theme.scss +28 -71
  51. data/app/assets/stylesheets/pageflow/editor/composables.scss +2 -2
  52. data/app/assets/stylesheets/pageflow/editor/confirm_encoding.scss +18 -36
  53. data/app/assets/stylesheets/pageflow/editor/confirm_upload.scss +12 -39
  54. data/app/assets/stylesheets/pageflow/editor/dialogs.scss +39 -12
  55. data/app/assets/stylesheets/pageflow/editor/disabled_atmo_indicator.scss +3 -3
  56. data/app/assets/stylesheets/pageflow/editor/drop_down_button.scss +7 -10
  57. data/app/assets/stylesheets/pageflow/editor/emulation_mode_button.scss +3 -8
  58. data/app/assets/stylesheets/pageflow/editor/failures.scss +3 -3
  59. data/app/assets/stylesheets/pageflow/editor/file_import.scss +22 -38
  60. data/app/assets/stylesheets/pageflow/editor/file_meta_data.scss +3 -7
  61. data/app/assets/stylesheets/pageflow/editor/file_settings_dialog.scss +4 -24
  62. data/app/assets/stylesheets/pageflow/editor/file_stages.scss +10 -11
  63. data/app/assets/stylesheets/pageflow/editor/file_thumbnails.scss +4 -8
  64. data/app/assets/stylesheets/pageflow/editor/files.scss +7 -6
  65. data/app/assets/stylesheets/pageflow/editor/files_explorer.scss +13 -30
  66. data/app/assets/stylesheets/pageflow/editor/files_gallery.scss +15 -11
  67. data/app/assets/stylesheets/pageflow/editor/filtered_files.scss +6 -7
  68. data/app/assets/stylesheets/pageflow/editor/help.scss +19 -14
  69. data/app/assets/stylesheets/pageflow/editor/help_image.scss +1 -1
  70. data/app/assets/stylesheets/pageflow/editor/info_box.scss +19 -0
  71. data/app/assets/stylesheets/pageflow/editor/inputs/file_input.scss +7 -11
  72. data/app/assets/stylesheets/pageflow/editor/inputs/file_processing_state_display.scss +0 -2
  73. data/app/assets/stylesheets/pageflow/editor/inputs/reference.scss +7 -11
  74. data/app/assets/stylesheets/pageflow/editor/list.scss +13 -10
  75. data/app/assets/stylesheets/pageflow/editor/loading.scss +1 -1
  76. data/app/assets/stylesheets/pageflow/editor/locked.scss +9 -5
  77. data/app/assets/stylesheets/pageflow/editor/menu.scss +5 -5
  78. data/app/assets/stylesheets/pageflow/editor/notifications.scss +15 -14
  79. data/app/assets/stylesheets/pageflow/editor/other_entry_item.scss +7 -3
  80. data/app/assets/stylesheets/pageflow/editor/outline.scss +57 -19
  81. data/app/assets/stylesheets/pageflow/editor/page_links.scss +10 -8
  82. data/app/assets/stylesheets/pageflow/editor/page_selection.scss +2 -29
  83. data/app/assets/stylesheets/pageflow/editor/publish_entry.scss +5 -6
  84. data/app/assets/stylesheets/pageflow/editor/quotas.scss +2 -3
  85. data/app/assets/stylesheets/pageflow/editor/select_button.scss +12 -6
  86. data/app/assets/stylesheets/pageflow/editor/sidebar_footer.scss +1 -5
  87. data/app/assets/stylesheets/pageflow/editor/storyline_picker.scss +6 -2
  88. data/app/assets/stylesheets/pageflow/editor/text_tracks.scss +6 -22
  89. data/app/assets/stylesheets/pageflow/editor/widgets.scss +2 -2
  90. data/app/assets/stylesheets/pageflow/editor/wysihtml5.scss +35 -29
  91. data/app/assets/stylesheets/pageflow/mixins/background_icons.scss +3 -3
  92. data/app/assets/stylesheets/pageflow/mixins/buttons.scss +50 -68
  93. data/app/assets/stylesheets/pageflow/themes/default/base.scss +2 -0
  94. data/app/assets/stylesheets/pageflow/themes/default/consent/bar.scss +156 -0
  95. data/app/assets/stylesheets/pageflow/themes/default/consent/vendor_list.scss +62 -0
  96. data/app/assets/stylesheets/pageflow/themes/default/consent.scss +2 -0
  97. data/app/assets/stylesheets/pageflow/themes/default/logo/alignment.scss +7 -0
  98. data/app/assets/stylesheets/pageflow/themes/default/page/shadow.scss +44 -0
  99. data/app/assets/stylesheets/pageflow/themes/default/third_party_embed_consent.scss +103 -0
  100. data/app/assets/stylesheets/pageflow/ui/forms.scss +79 -84
  101. data/app/assets/stylesheets/pageflow/ui/functions.scss +56 -0
  102. data/app/assets/stylesheets/pageflow/ui/input/check_box_group_input.scss +2 -3
  103. data/app/assets/stylesheets/pageflow/ui/input/color_input.scss +6 -6
  104. data/app/assets/stylesheets/pageflow/ui/input/extended_select_input.scss +63 -50
  105. data/app/assets/stylesheets/pageflow/ui/normalize/forms.scss +153 -0
  106. data/app/assets/stylesheets/pageflow/ui/normalize.scss +278 -0
  107. data/app/assets/stylesheets/pageflow/ui/properties.scss +44 -0
  108. data/app/assets/stylesheets/pageflow/ui/table_cells/delete_row_table_cell.scss +1 -1
  109. data/app/assets/stylesheets/pageflow/ui/table_view.scss +14 -18
  110. data/app/assets/stylesheets/pageflow/ui/tabs_view.scss +8 -11
  111. data/app/assets/stylesheets/pageflow/ui/tooltip.scss +6 -8
  112. data/app/assets/stylesheets/pageflow/ui/validation_error_messages.scss +6 -0
  113. data/app/assets/stylesheets/pageflow/ui.scss +4 -0
  114. data/app/controllers/pageflow/edit_locks_controller.rb +3 -1
  115. data/app/helpers/pageflow/admin/entries_helper.rb +2 -12
  116. data/app/helpers/pageflow/embed_code_helper.rb +1 -1
  117. data/app/helpers/pageflow/entries_helper.rb +41 -2
  118. data/app/helpers/pageflow/file_background_images_helper.rb +1 -1
  119. data/app/helpers/pageflow/page_types_helper.rb +1 -1
  120. data/app/helpers/pageflow/themes_helper.rb +1 -1
  121. data/app/jobs/pageflow/poll_meta_data_from_zencoder_job.rb +1 -1
  122. data/app/jobs/pageflow/poll_zencoder_job.rb +1 -9
  123. data/app/models/pageflow/audio_file.rb +17 -0
  124. data/app/models/pageflow/audio_file_url_templates.rb +4 -1
  125. data/app/models/pageflow/customized_theme.rb +37 -0
  126. data/app/models/pageflow/draft_entry.rb +6 -59
  127. data/app/models/pageflow/entry.rb +8 -0
  128. data/app/models/pageflow/entry_at_revision.rb +68 -0
  129. data/app/models/pageflow/entry_role_query.rb +44 -24
  130. data/app/models/pageflow/image_file_css_background_image_urls.rb +8 -2
  131. data/app/models/pageflow/published_entry.rb +9 -56
  132. data/app/models/pageflow/theme_customization.rb +46 -0
  133. data/app/models/pageflow/theme_customization_file.rb +58 -0
  134. data/app/models/pageflow/video_file.rb +11 -1
  135. data/app/models/pageflow/video_file_url_templates.rb +1 -0
  136. data/app/policies/pageflow/entry_policy.rb +4 -0
  137. data/app/state_machines/pageflow/media_encoding_state_machine.rb +23 -4
  138. data/app/views/admin/entries/_form.html.erb +50 -0
  139. data/app/views/admin/entries/entry_type_name_input.html.erb +5 -0
  140. data/app/views/admin/features/_form.html.erb +1 -1
  141. data/app/views/admin/memberships/_form.html.erb +2 -3
  142. data/app/views/admin/users/_form.html.erb +1 -1
  143. data/app/views/admin/users/invitation.html.erb +2 -2
  144. data/app/views/components/pageflow/admin/entry_user_badge_list.rb +10 -9
  145. data/app/views/components/pageflow/admin/members_tab.rb +5 -1
  146. data/app/views/components/pageflow/admin/revisions_tab.rb +13 -3
  147. data/app/views/components/pageflow/admin/timestamp.rb +20 -0
  148. data/app/views/components/pageflow/admin/user_account_badge_list.rb +5 -6
  149. data/app/views/pageflow/admin/entries/_cannot_add_user.html.erb +9 -5
  150. data/app/views/pageflow/admin/entries/_entry_type_name_input.html.erb +7 -0
  151. data/app/views/pageflow/audio_files/_audio_file.json.jbuilder +13 -0
  152. data/app/views/pageflow/editor/encoding_confirmations/_intro.html.erb +0 -0
  153. data/app/views/pageflow/editor/encoding_confirmations/check.json.jbuilder +14 -2
  154. data/config/initializers/paperclip.rb +16 -1
  155. data/config/locales/de.yml +24 -28
  156. data/config/locales/en.yml +19 -10
  157. data/db/migrate/20210528073122_create_pageflow_theme_customizations.rb +11 -0
  158. data/db/migrate/20210531090654_create_pageflow_theme_customization_files.rb +15 -0
  159. data/db/migrate/20210531102228_add_selected_file_ids_to_theme_customizations.rb +5 -0
  160. data/db/migrate/20211020085902_add_canonical_entry_url_prefix_to_themings.rb +5 -0
  161. data/db/migrate/20220503150010_add_peak_data_to_audio_files.rb +5 -0
  162. data/db/migrate/20220705084830_add_trailing_slash_in_canonical_urls_to_themings.rb +5 -0
  163. data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/editor.js +256 -124
  164. data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/frontend.js +566 -167
  165. data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/react-client.js +5 -5
  166. data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/react-server.js +4 -4
  167. data/entry_types/paged/app/helpers/pageflow_paged/third_party_embed_consent_helper.rb +38 -0
  168. data/entry_types/paged/app/views/pageflow_paged/third_party_embed_consent/_opt_in.html.erb +12 -0
  169. data/entry_types/paged/app/views/pageflow_paged/third_party_embed_consent/_opt_out_info.html.erb +10 -0
  170. data/entry_types/paged/config/initializers/features.rb +2 -0
  171. data/entry_types/paged/config/locales/new/video_contain.de.yml +7 -0
  172. data/entry_types/paged/config/locales/new/video_contain.en.yml +7 -0
  173. data/entry_types/scrolled/app/helpers/pageflow_scrolled/editor/seed_html_helper.rb +3 -0
  174. data/entry_types/scrolled/app/helpers/pageflow_scrolled/entry_json_seed_helper.rb +1 -0
  175. data/entry_types/scrolled/app/helpers/pageflow_scrolled/packs_helper.rb +58 -0
  176. data/entry_types/scrolled/app/helpers/pageflow_scrolled/react_server_side_rendering_helper.rb +37 -3
  177. data/entry_types/scrolled/app/helpers/pageflow_scrolled/themes_helper.rb +92 -6
  178. data/entry_types/scrolled/app/helpers/pageflow_scrolled/webpack_public_path_helper.rb +20 -0
  179. data/entry_types/scrolled/app/models/pageflow_scrolled/content_element.rb +7 -0
  180. data/entry_types/scrolled/app/views/pageflow_scrolled/editor/entries/_head.html.erb +5 -2
  181. data/entry_types/scrolled/app/views/pageflow_scrolled/editor/entries/_seed.json.jbuilder +5 -1
  182. data/entry_types/scrolled/app/views/pageflow_scrolled/entries/show.html.erb +5 -4
  183. data/entry_types/scrolled/app/views/pageflow_scrolled/entry_json_seed/_entry.json.jbuilder +16 -0
  184. data/entry_types/scrolled/app/views/pageflow_scrolled/entry_json_seed/_theme.json.jbuilder +13 -2
  185. data/entry_types/scrolled/app/views/pageflow_scrolled/entry_json_seed/_widget.json.jbuilder +2 -0
  186. data/entry_types/scrolled/config/locales/de.yml +1 -8
  187. data/entry_types/scrolled/config/locales/en.yml +1 -9
  188. data/entry_types/scrolled/config/locales/new/before_after_slider.de.yml +8 -0
  189. data/entry_types/scrolled/config/locales/new/before_after_slider.en.yml +8 -0
  190. data/entry_types/scrolled/config/locales/new/center_ragged.de.yml +8 -0
  191. data/entry_types/scrolled/config/locales/new/center_ragged.en.yml +9 -0
  192. data/entry_types/scrolled/config/locales/new/consent.de.yml +25 -0
  193. data/entry_types/scrolled/config/locales/new/consent.en.yml +24 -0
  194. data/entry_types/scrolled/config/locales/new/content_element_categories.de.yml +39 -0
  195. data/entry_types/scrolled/config/locales/new/content_element_categories.en.yml +39 -0
  196. data/entry_types/scrolled/config/locales/new/default_transition.de.yml +14 -0
  197. data/entry_types/scrolled/config/locales/new/default_transition.en.yml +14 -0
  198. data/entry_types/scrolled/config/locales/new/header_line_breaks.de.yml +28 -0
  199. data/entry_types/scrolled/config/locales/new/header_line_breaks.en.yml +27 -0
  200. data/entry_types/scrolled/config/locales/new/header_size.de.yml +17 -0
  201. data/entry_types/scrolled/config/locales/new/header_size.en.yml +17 -0
  202. data/entry_types/scrolled/config/locales/new/iframe_embed.de.yml +39 -0
  203. data/entry_types/scrolled/config/locales/new/iframe_embed.en.yml +39 -0
  204. data/entry_types/scrolled/config/locales/new/inline_loops.de.yml +26 -0
  205. data/entry_types/scrolled/config/locales/new/inline_loops.en.yml +26 -0
  206. data/entry_types/scrolled/config/locales/new/portrait_inline_image.de.yml +9 -0
  207. data/entry_types/scrolled/config/locales/new/portrait_inline_image.en.yml +9 -0
  208. data/entry_types/scrolled/config/locales/new/section_width.de.yml +10 -0
  209. data/entry_types/scrolled/config/locales/new/section_width.en.yml +10 -0
  210. data/entry_types/scrolled/config/locales/new/typography_variants.de.yml +7 -0
  211. data/entry_types/scrolled/config/locales/new/typography_variants.en.yml +7 -0
  212. data/entry_types/scrolled/config/locales/new/video_embed_poster.de.yml +8 -0
  213. data/entry_types/scrolled/config/locales/new/video_embed_poster.en.yml +8 -0
  214. data/entry_types/scrolled/config/locales/new/waveform_styles.de.yml +11 -0
  215. data/entry_types/scrolled/config/locales/new/waveform_styles.en.yml +12 -0
  216. data/entry_types/scrolled/config/locales/new/widgets.de.yml +6 -0
  217. data/entry_types/scrolled/config/locales/new/widgets.en.yml +6 -0
  218. data/entry_types/scrolled/lib/generators/pageflow_scrolled/install/install_generator.rb +29 -5
  219. data/entry_types/scrolled/lib/generators/pageflow_scrolled/install/templates/themes_plugin.rb.tt +8 -6
  220. data/entry_types/scrolled/lib/pageflow_scrolled/additional_packs.rb +37 -0
  221. data/entry_types/scrolled/lib/pageflow_scrolled/additional_seed_data.rb +57 -0
  222. data/entry_types/scrolled/lib/pageflow_scrolled/configuration.rb +49 -0
  223. data/entry_types/scrolled/lib/pageflow_scrolled/plugin.rb +8 -0
  224. data/entry_types/scrolled/lib/pageflow_scrolled/react_widget_type.rb +42 -0
  225. data/entry_types/scrolled/lib/pageflow_scrolled.rb +17 -1
  226. data/entry_types/scrolled/package/contentElements-editor.js +323 -89
  227. data/entry_types/scrolled/package/contentElements-frontend.css +1 -1
  228. data/entry_types/scrolled/package/contentElements-frontend.js +719 -529
  229. data/entry_types/scrolled/package/editor.js +459 -261
  230. data/entry_types/scrolled/package/frontend/{EditableText-7093fd0e.js → EditableInlineText.module-b9923660.js} +284 -362
  231. data/entry_types/scrolled/package/frontend/{i18n-4dc6c377.js → PhonePlatformContext-9fb97827.js} +199 -106
  232. data/entry_types/scrolled/package/frontend/{Viewer-e49e7807.js → Viewer-e2290ea0.js} +196 -79
  233. data/entry_types/scrolled/package/frontend/{Wavesurfer-0adf5667.js → Wavesurfer-7d9cf1b7.js} +16 -58
  234. data/entry_types/scrolled/package/frontend/{components-6a6793ca.js → components-6ab26015.js} +664 -671
  235. data/entry_types/scrolled/package/frontend/{getPrototypeOf-63c7c8e8.js → createSuper-d0f30da3.js} +34 -5
  236. data/entry_types/scrolled/package/frontend/index.css +1 -9
  237. data/entry_types/scrolled/package/frontend/index.js +1919 -2386
  238. data/entry_types/scrolled/package/frontend/{useBrowserFeature-91a4c29d.js → usePhonePlatform-2857c22b.js} +9 -8
  239. data/entry_types/scrolled/package/frontend-server.js +3 -6
  240. data/entry_types/scrolled/package/package.json +16 -8
  241. data/entry_types/scrolled/package/testHelpers.js +456 -0
  242. data/entry_types/scrolled/package/values/breakpoints.module.css +9 -0
  243. data/entry_types/scrolled/package/values/colors.module.css +5 -0
  244. data/entry_types/scrolled/package/widgets/defaultNavigation.css +9 -0
  245. data/entry_types/scrolled/package/widgets/defaultNavigation.js +612 -0
  246. data/entry_types/scrolled/spec/fixtures/image.svg +1 -0
  247. data/lib/pageflow/ability_mixin.rb +4 -0
  248. data/lib/pageflow/built_in_widget_type.rb +4 -0
  249. data/lib/pageflow/built_in_widget_types_plugin.rb +7 -0
  250. data/lib/pageflow/configuration.rb +34 -1
  251. data/lib/pageflow/entry_type.rb +11 -2
  252. data/lib/pageflow/entry_type_configuration.rb +2 -0
  253. data/lib/pageflow/file_type.rb +24 -0
  254. data/lib/pageflow/paperclip_processors/audio_waveform.rb +42 -0
  255. data/lib/pageflow/paperclip_processors/noop.rb +10 -0
  256. data/lib/pageflow/theme_customizations.rb +61 -0
  257. data/lib/pageflow/user_mixin.rb +6 -0
  258. data/lib/pageflow/version.rb +1 -1
  259. data/lib/pageflow.rb +9 -0
  260. data/package/config/jest/index.js +2 -1
  261. data/package/config/postcss/scaleFunctions.js +71 -0
  262. data/package/editor.js +95 -85
  263. data/package/frontend.js +521 -161
  264. data/package/package.json +5 -3
  265. data/package/testHelpers.js +26 -5
  266. data/package/ui.js +124 -11
  267. data/spec/factories/draft_entries.rb +19 -1
  268. data/spec/factories/entries.rb +4 -0
  269. data/spec/factories/published_entries.rb +6 -0
  270. data/spec/fixtures/audio.ogg +0 -0
  271. metadata +123 -15
  272. data/app/views/admin/entries/_not_allowed_to_see_entry_types.json.jbuilder +0 -2
  273. data/app/views/admin/entries/entry_types.json.jbuilder +0 -4
  274. data/package/config/jest/transformers/upwardBabel.js +0 -5
@@ -352,6 +352,24 @@ this.pageflow._uiGlobalInterop = (function (exports, Marionette, _, $, I18n$1, B
352
352
 
353
353
  BaseObject.extend = Backbone.Model.extend;
354
354
 
355
+ var serverSideValidation = {
356
+ initialize: function initialize() {
357
+ var _this = this;
358
+
359
+ this.validationErrors = {};
360
+ this.listenTo(this, 'error', function (model, request) {
361
+ if (request.status === 422) {
362
+ _this.validationErrors = JSON.parse(request.responseText).errors;
363
+
364
+ _this.trigger('invalid');
365
+ }
366
+ });
367
+ this.listenTo(this, 'sync', function () {
368
+ _this.validationErrors = {};
369
+ });
370
+ }
371
+ };
372
+
355
373
  var CollectionView = Marionette.View.extend({
356
374
  initialize: function initialize() {
357
375
  this.rendered = false;
@@ -752,10 +770,12 @@ this.pageflow._uiGlobalInterop = (function (exports, Marionette, _, $, I18n$1, B
752
770
  this.configure();
753
771
  },
754
772
  configure: function configure() {},
755
- tab: function tab(name, callback) {
773
+ tab: function tab(name, callbackOrOptions, callback) {
774
+ callback = callback || callbackOrOptions;
775
+ var options = callback ? callbackOrOptions : {};
756
776
  this.tabsView.tab(name, _.bind(function () {
757
777
  var tabView = new ConfigurationEditorTabView({
758
- model: this.model,
778
+ model: options.model || this.model,
759
779
  placeholderModel: this.options.placeholderModel,
760
780
  tab: name,
761
781
  attributeTranslationKeyPrefixes: this.options.attributeTranslationKeyPrefixes
@@ -896,7 +916,7 @@ this.pageflow._uiGlobalInterop = (function (exports, Marionette, _, $, I18n$1, B
896
916
  var TableHeaderCellView = TableCellView.extend({
897
917
  tagName: 'th',
898
918
  render: function render() {
899
- this.$el.text(this.attributeTranslation('column_header'));
919
+ this.$el.text(this.options.column.headerText || this.attributeTranslation('column_header'));
900
920
  this.$el.data('columnName', this.options.column.name);
901
921
  return this;
902
922
  }
@@ -1273,6 +1293,7 @@ this.pageflow._uiGlobalInterop = (function (exports, Marionette, _, $, I18n$1, B
1273
1293
  return this.getAttributeBoundOption('disabled');
1274
1294
  },
1275
1295
  updateDisabled: function updateDisabled() {
1296
+ this.$el.toggleClass('input-disabled', !!this.isDisabled());
1276
1297
  this.updateInlineHelp();
1277
1298
 
1278
1299
  if (this.ui.input) {
@@ -1500,6 +1521,31 @@ this.pageflow._uiGlobalInterop = (function (exports, Marionette, _, $, I18n$1, B
1500
1521
  }
1501
1522
  };
1502
1523
 
1524
+ var viewWithValidationErrorMessages = {
1525
+ onRender: function onRender() {
1526
+ this.listenTo(this.model, 'invalid sync', this.updateValidationErrorMessages);
1527
+ this.updateValidationErrorMessages();
1528
+ },
1529
+ updateValidationErrorMessages: function updateValidationErrorMessages() {
1530
+ var _this = this;
1531
+
1532
+ var errors = this.model.validationErrors && this.model.validationErrors[this.options.propertyName] || [];
1533
+
1534
+ if (errors.length) {
1535
+ this.validationErrorList = this.validationErrorList || $('<ul class="validation_error_messages" />').appendTo(this.el);
1536
+ this.validationErrorList.html('');
1537
+ errors.forEach(function (error) {
1538
+ return _this.validationErrorList.append("<li>".concat(error, "</li>"));
1539
+ });
1540
+ this.$el.addClass('invalid');
1541
+ } else if (this.validationErrorList) {
1542
+ this.validationErrorList.remove();
1543
+ this.validationErrorList = null;
1544
+ this.$el.removeClass('invalid');
1545
+ }
1546
+ }
1547
+ };
1548
+
1503
1549
  function template$6(data) {
1504
1550
  var __p = '';
1505
1551
  __p += '<label>\n <span class="name"></span>\n <span class="inline_help"></span>\n</label>\n<input type="text" dir="auto" />\n';
@@ -1527,7 +1573,7 @@ this.pageflow._uiGlobalInterop = (function (exports, Marionette, _, $, I18n$1, B
1527
1573
  */
1528
1574
 
1529
1575
  var TextInputView = Marionette.ItemView.extend({
1530
- mixins: [inputView, inputWithPlaceholderText],
1576
+ mixins: [inputView, inputWithPlaceholderText, viewWithValidationErrorMessages],
1531
1577
  template: template$6,
1532
1578
  ui: {
1533
1579
  input: 'input'
@@ -2027,9 +2073,9 @@ this.pageflow._uiGlobalInterop = (function (exports, Marionette, _, $, I18n$1, B
2027
2073
  ((__t = ( I18n.t('pageflow.ui.templates.inputs.text_area_input.link_type.url') )) == null ? '' : __t) +
2028
2074
  '\n </label>\n <label>\n <input type="radio" name="link_type" class="fragment_link_radio_button">\n ' +
2029
2075
  ((__t = ( I18n.t('pageflow.ui.templates.inputs.text_area_input.link_type.page_link') )) == null ? '' : __t) +
2030
- '\n </label>\n </div>\n <div class="url_link_panel">\n <label>\n ' +
2076
+ '\n </label>\n </div>\n <div class="url_link_panel">\n <label>\n <span>\n ' +
2031
2077
  ((__t = ( I18n.t('pageflow.ui.templates.inputs.text_area_input.target') )) == null ? '' : __t) +
2032
- '\n </label>\n <input type="text" class="display_url">\n <div class="open_in_new_tab_section">\n <label>\n <input type="checkbox" class="open_in_new_tab">\n ' +
2078
+ '\n </span>\n </label>\n <input type="text" class="display_url">\n <div class="open_in_new_tab_section">\n <label>\n <input type="checkbox" class="open_in_new_tab">\n ' +
2033
2079
  ((__t = ( I18n.t('pageflow.ui.templates.inputs.text_area_input.open_in_new_tab') )) == null ? '' : __t) +
2034
2080
  '\n </label>\n <span class="inline_help">\n ' +
2035
2081
  ((__t = ( I18n.t('pageflow.ui.templates.inputs.text_area_input.open_in_new_tab_help') )) == null ? '' : __t) +
@@ -2363,6 +2409,15 @@ this.pageflow._uiGlobalInterop = (function (exports, Marionette, _, $, I18n$1, B
2363
2409
  supportedHosts: function supportedHosts() {
2364
2410
  return this.options.supportedHosts;
2365
2411
  },
2412
+ // Host names used to be expected to include protocols. Remove
2413
+ // protocols for backwards compatilbity. Since supportedHosts
2414
+ // is supposed to be overridden in subclasses, we do it in a
2415
+ // separate method.
2416
+ supportedHostsWithoutLegacyProtocols: function supportedHostsWithoutLegacyProtocols() {
2417
+ return _.map(this.supportedHosts(), function (host) {
2418
+ return host.replace(/^https?:\/\//, '');
2419
+ });
2420
+ },
2366
2421
  validate: function validate(success) {
2367
2422
  var view = this;
2368
2423
  var options = this.options;
@@ -2399,23 +2454,26 @@ this.pageflow._uiGlobalInterop = (function (exports, Marionette, _, $, I18n$1, B
2399
2454
  }
2400
2455
 
2401
2456
  function hasSupportedHost(url) {
2402
- return _.any(view.supportedHosts(), function (host) {
2403
- return url.match(new RegExp('^' + host));
2457
+ return _.any(view.supportedHostsWithoutLegacyProtocols(), function (host) {
2458
+ return url.match(new RegExp('^https?://' + host));
2404
2459
  });
2405
2460
  }
2406
2461
 
2407
2462
  function displayValidationError(message) {
2408
2463
  view.$el.addClass('invalid');
2464
+ view.ui.input.attr('aria-invalid', 'true');
2409
2465
  view.ui.validation.removeClass('pending').addClass('failed').html(message).show();
2410
2466
  }
2411
2467
 
2412
2468
  function displayValidationPending(message) {
2413
2469
  view.$el.removeClass('invalid');
2470
+ view.ui.input.removeAttr('aria-invalid');
2414
2471
  view.ui.validation.removeClass('failed').addClass('pending').html(message).show();
2415
2472
  }
2416
2473
 
2417
2474
  function resetValidationError(message) {
2418
2475
  view.$el.removeClass('invalid');
2476
+ view.ui.input.attr('aria-invalid', 'false');
2419
2477
  view.ui.validation.hide();
2420
2478
  }
2421
2479
  }
@@ -2650,6 +2708,15 @@ this.pageflow._uiGlobalInterop = (function (exports, Marionette, _, $, I18n$1, B
2650
2708
  * Ignore the attribute value if the input is disabled and display
2651
2709
  * an unchecked check box.
2652
2710
  *
2711
+ * @param {boolean} [options.displayCheckedIfDisabled=false]
2712
+ * Ignore the attribute value if the input is disabled and display
2713
+ * an checked check box.
2714
+ *
2715
+ * @param {string} [options.storeInverted]
2716
+ * Display checked by default and store true in given attribute when
2717
+ * unchecked. The property name passed to `input` is only used for
2718
+ * translations.
2719
+ *
2653
2720
  * @class
2654
2721
  */
2655
2722
 
@@ -2675,7 +2742,13 @@ this.pageflow._uiGlobalInterop = (function (exports, Marionette, _, $, I18n$1, B
2675
2742
  },
2676
2743
  save: function save() {
2677
2744
  if (!this.isDisabled()) {
2678
- this.model.set(this.options.propertyName, this.ui.input.is(':checked'));
2745
+ var value = this.ui.input.is(':checked');
2746
+
2747
+ if (this.options.storeInverted) {
2748
+ this.model.set(this.options.storeInverted, !value);
2749
+ } else {
2750
+ this.model.set(this.options.propertyName, value);
2751
+ }
2679
2752
  }
2680
2753
  },
2681
2754
  load: function load() {
@@ -2686,12 +2759,49 @@ this.pageflow._uiGlobalInterop = (function (exports, Marionette, _, $, I18n$1, B
2686
2759
  displayValue: function displayValue() {
2687
2760
  if (this.isDisabled() && this.options.displayUncheckedIfDisabled) {
2688
2761
  return false;
2762
+ } else if (this.isDisabled() && this.options.displayCheckedIfDisabled) {
2763
+ return true;
2764
+ } else if (this.options.storeInverted) {
2765
+ return !this.model.get(this.options.storeInverted);
2689
2766
  } else {
2690
2767
  return this.model.get(this.options.propertyName);
2691
2768
  }
2692
2769
  }
2693
2770
  });
2694
2771
 
2772
+ /**
2773
+ * Render a separator in a {@link ConfigurationEditorView} tab.
2774
+ *
2775
+ * @example
2776
+ *
2777
+ * this.view(SeparatorView);
2778
+ *
2779
+ * @class
2780
+ */
2781
+
2782
+ var SeparatorView = Marionette.View.extend({
2783
+ className: 'separator'
2784
+ });
2785
+
2786
+ /**
2787
+ * Render an input that is only a label. Can be used to render
2788
+ * additional inline help.
2789
+ *
2790
+ * See {@link inputView} for further options
2791
+ *
2792
+ * @class
2793
+ */
2794
+
2795
+ var LabelOnlyView = Marionette.ItemView.extend({
2796
+ mixins: [inputView],
2797
+ template: function template() {
2798
+ return "\n <label>\n <span class=\"name\"></span>\n <span class=\"inline_help\"></span>\n </label>\n ";
2799
+ },
2800
+ ui: {
2801
+ label: 'label'
2802
+ }
2803
+ });
2804
+
2695
2805
  /**
2696
2806
  * A table cell mapping column attribute values to a list of
2697
2807
  * translations.
@@ -2911,7 +3021,10 @@ this.pageflow._uiGlobalInterop = (function (exports, Marionette, _, $, I18n$1, B
2911
3021
  }
2912
3022
  }
2913
3023
  };
2914
- Cocktail.mixin(Marionette.View, subviewContainer);
3024
+
3025
+ if (!Marionette.View.prototype.appendSubview) {
3026
+ Cocktail.mixin(Marionette.View, subviewContainer);
3027
+ }
2915
3028
 
2916
3029
  var tooltipContainer = {
2917
3030
  events: {
@@ -2968,10 +3081,12 @@ this.pageflow._uiGlobalInterop = (function (exports, Marionette, _, $, I18n$1, B
2968
3081
  exports.ExtendedSelectInputView = ExtendedSelectInputView;
2969
3082
  exports.IconTableCellView = IconTableCellView;
2970
3083
  exports.JsonInputView = JsonInputView;
3084
+ exports.LabelOnlyView = LabelOnlyView;
2971
3085
  exports.Object = BaseObject;
2972
3086
  exports.PresenceTableCellView = PresenceTableCellView;
2973
3087
  exports.ProxyUrlInputView = ProxyUrlInputView;
2974
3088
  exports.SelectInputView = SelectInputView;
3089
+ exports.SeparatorView = SeparatorView;
2975
3090
  exports.SliderInputView = SliderInputView;
2976
3091
  exports.SortableCollectionView = SortableCollectionView;
2977
3092
  exports.TableCellView = TableCellView;
@@ -2989,8 +3104,10 @@ this.pageflow._uiGlobalInterop = (function (exports, Marionette, _, $, I18n$1, B
2989
3104
  exports.i18nUtils = i18nUtils;
2990
3105
  exports.inputView = inputView;
2991
3106
  exports.inputWithPlaceholderText = inputWithPlaceholderText;
3107
+ exports.serverSideValidation = serverSideValidation;
2992
3108
  exports.subviewContainer = subviewContainer;
2993
3109
  exports.tooltipContainer = tooltipContainer;
3110
+ exports.viewWithValidationErrorMessages = viewWithValidationErrorMessages;
2994
3111
 
2995
3112
  return exports;
2996
3113
 
@@ -1,21 +1,23 @@
1
- #wrapper {
2
- overflow: hidden;
3
- position: relative;
4
- }
5
-
6
- .action_items {
7
- form,
8
- div {
9
- display: inline;
1
+ @if not $pageflow-custom-admin-theme {
2
+ #wrapper {
3
+ overflow: hidden;
4
+ position: relative;
10
5
  }
11
6
 
12
- input[type="submit"] {
13
- @include light-button;
14
- padding: 12px 17px 10px;
15
- margin: 0;
7
+ .action_items {
8
+ form,
9
+ div {
10
+ display: inline;
11
+ }
12
+
13
+ input[type="submit"] {
14
+ @include light-button;
15
+ padding: 12px 17px 10px;
16
+ margin: 0;
17
+ }
16
18
  }
17
- }
18
19
 
19
- .blank_slate_container {
20
- margin: 10px 0;
20
+ .blank_slate_container {
21
+ margin: 10px 0;
22
+ }
21
23
  }
@@ -1,47 +1,37 @@
1
+ $pageflow-badge-color: #555 !default;
2
+ $pageflow-badge-background-color: #fff !default;
3
+ $pageflow-badge-border-color: #aaa !default;
4
+ $pageflow-badge-border-radius: null !default;
5
+ $pageflow-badge-margin: -4px 0 !default;
6
+
1
7
  .badge_list {
2
- margin: -4px 0;
8
+ margin: $pageflow-badge-margin;
3
9
  padding: 0;
4
10
 
5
11
  li {
6
- float: left;
7
12
  position: relative;
8
13
  list-style: none;
9
-
10
- margin: 1px;
11
- line-height: 22px;
12
- padding: 0 5px;
13
-
14
- background-color: #fff;
15
- border: solid 1px #aaa;
16
- text-align: center;
17
-
18
- &:hover .tooltip {
19
- opacity: 1;
20
- }
21
- }
22
-
23
- .abbreviation, .name {
24
- font-size: 11px;
25
- font-weight: bold;
26
- color: #555;
14
+ float: left;
27
15
  }
28
16
 
29
- .tooltip {
30
- position: absolute;
31
- top: -1px;
32
- left: -1px;
33
- padding: 0 5px;
34
-
35
- white-space: nowrap;
36
- z-index: 1;
37
-
38
- background-color: #fff;
39
- border: solid 1px #aaa;
40
- opacity: 0;
17
+ .abbreviation {
18
+ padding: 0 8px;
19
+ display: inline-block;
20
+ text-decoration: none;
21
+ margin: 2px;
22
+ background-color: $pageflow-badge-background-color;
23
+ border: solid 1px $pageflow-badge-border-color;
24
+ color: $pageflow-badge-color;
25
+ border-radius: $pageflow-badge-border-radius;
26
+ text-align: center;
27
+ font-size: 0.7rem;
41
28
  }
29
+ }
42
30
 
43
- a {
44
- text-decoration: none;
45
- font-weight: bold;
31
+ .entry_user_badge_list {
32
+ .abbreviation {
33
+ width: 2em;
34
+ line-height: 2em;
35
+ padding: 0;
46
36
  }
47
37
  }
@@ -1,15 +1,17 @@
1
- .columns {
2
- width: 100%;
1
+ @if not $pageflow-custom-admin-theme {
2
+ .columns {
3
+ width: 100%;
3
4
 
4
- > div {
5
- width: 49%;
6
- }
5
+ > div {
6
+ width: 49%;
7
+ }
7
8
 
8
- > div:first-child {
9
- float: left;
10
- }
9
+ > div:first-child {
10
+ float: left;
11
+ }
11
12
 
12
- > div:last-child {
13
- float: right;
13
+ > div:last-child {
14
+ float: right;
15
+ }
14
16
  }
15
- }
17
+ }
@@ -1,13 +1,10 @@
1
1
  .embed_code {
2
- $hint-color: #9c9c9c;
3
-
4
2
  input {
5
3
  width: 100%;
6
4
  }
7
5
 
8
6
  p {
9
7
  margin: 5px 0 0 0;
10
- color: $hint-color;
11
- font-style: italic;
8
+ color: $pageflow-hint-color;
12
9
  }
13
10
  }
@@ -1,4 +1,14 @@
1
+ $pageflow-folders-icon-directory: "pageflow/admin/icons" !default;
2
+
3
+ $pageflow-active-folder-background-color: #75a1c2 !default;
4
+ $pageflow-active-folder-color: #fff !default;
5
+ $pageflow-folder-drop-target-background-color: #fff !default;
6
+ $pageflow-folder-drop-target-outline-color: #000 !default;
7
+ $pageflow-folders-heading-color: #888 !default;
8
+
1
9
  #folders_sidebar_section {
10
+ $icon-dir: $pageflow-folders-icon-directory;
11
+
2
12
  position: relative;
3
13
 
4
14
  ul {
@@ -11,12 +21,32 @@
11
21
  position: relative;
12
22
  }
13
23
 
24
+ .name,
25
+ .edit_folder,
26
+ .new,
27
+ .delete,
28
+ .edit_toggle {
29
+ background-size: $pageflow-icon-size $pageflow-icon-size;
30
+ background-repeat: no-repeat;
31
+ background-position: center;
32
+ }
33
+
34
+ .edit_folder,
35
+ .delete,
36
+ .edit_toggle {
37
+ background-position: center;
38
+ }
39
+
40
+ .name,
41
+ .new {
42
+ background-position: 7px center;
43
+ }
44
+
14
45
  .folders {
15
46
  margin-bottom: 10px;
16
47
 
17
48
  .name {
18
- @include background-icon-left;
19
- @include folder-icon;
49
+ background-image: image-url("#{$icon-dir}/folder.svg");
20
50
 
21
51
  display: block;
22
52
  padding: 5px 10px 5px 35px;
@@ -26,16 +56,14 @@
26
56
  }
27
57
 
28
58
  span.name {
29
- @include highlight-gradient;
30
- @include background-icon-color(#eee);
31
- color: #fff;
32
- font-weight: bold;
59
+ background-image: image-url("#{$icon-dir}/folder_active.svg");
60
+ background-color: $pageflow-active-folder-background-color;
61
+ color: $pageflow-active-folder-color;
33
62
  cursor: default;
34
63
  }
35
64
 
36
65
  .edit_folder,
37
66
  .delete {
38
- @include background-icon-center(#444);
39
67
  @include hide-text;
40
68
 
41
69
  position: absolute;
@@ -45,21 +73,21 @@
45
73
  }
46
74
 
47
75
  .edit_folder {
48
- @include pencil-icon;
76
+ background-image: image-url("#{$icon-dir}/edit.svg");
49
77
  right: 30px;
50
78
  }
51
79
 
52
80
  .delete {
53
- @include trash-icon;
81
+ background-image: image-url("#{$icon-dir}/delete.svg");
54
82
  right: 0;
55
83
  }
56
84
 
57
85
  .drop_target {
58
- background-color: #fff;
86
+ background-color: $pageflow-folder-drop-target-background-color;
59
87
  }
60
88
 
61
89
  .drop_over {
62
- outline: solid 1px #444;
90
+ outline: solid 1px $pageflow-folder-drop-target-outline-color;
63
91
  }
64
92
  }
65
93
 
@@ -67,12 +95,11 @@
67
95
  margin: 0px 0 5px 0;
68
96
  font-size: 13px;
69
97
  font-weight: bold;
70
- color: #888;
98
+ color: $pageflow-folders-heading-color;
71
99
  }
72
100
 
73
101
  .new {
74
- @include background-icon-left;
75
- @include plus-circled-icon;
102
+ background-image: image-url("#{$icon-dir}/new.svg");
76
103
 
77
104
  display: block;
78
105
  padding: 5px 10px 5px 35px;
@@ -101,8 +128,6 @@
101
128
  }
102
129
 
103
130
  .edit_toggle {
104
- @include background-icon-center(#444);
105
-
106
131
  position: absolute;
107
132
  right: 0;
108
133
  top: 0;
@@ -110,11 +135,11 @@
110
135
  height: 30px;
111
136
 
112
137
  &.done {
113
- @include check-icon;
138
+ background-image: image-url("#{$icon-dir}/done.svg");
114
139
  }
115
140
 
116
141
  &.edit {
117
- @include pencil-icon;
142
+ background-image: image-url("#{$icon-dir}/edit.svg");
118
143
  }
119
144
  }
120
145
  }
@@ -124,4 +149,4 @@
124
149
  background-color: #eee;
125
150
  padding: 15px;
126
151
  z-index: 1000;
127
- }
152
+ }
@@ -1,9 +1,10 @@
1
- .admin_entries {
2
- $published-color: #BEEBB8;
1
+ $pageflow-revision-color: #888 !default;
2
+ $pageflow-published-revision-background-color: #beebb8 !default;
3
3
 
4
+ .admin_entries {
4
5
  table.revisions {
5
- tr {
6
- color: #888;
6
+ td {
7
+ color: $pageflow-revision-color;
7
8
  }
8
9
 
9
10
  a.show {
@@ -13,49 +14,32 @@
13
14
  .published td {
14
15
  // scss-lint:disable ImportantRule
15
16
  // Win against overly specific Active Admin rule
16
- background-color: $published-color !important;
17
+ background-color: $pageflow-published-revision-background-color !important;
17
18
  // scss-lint:enable ImportantRule
18
- color: #000;
19
+ color: inherit;
19
20
  }
20
- }
21
-
22
- .snapshot {
23
- float: right;
24
- }
25
-
26
- td > span[title] {
27
- border-bottom: 1px #aaa dotted;
28
- cursor: default;
29
- }
30
21
 
31
- span.password_protected {
32
- @include background-icon-center($font-size: 13px, $color: #555);
33
- @include lock-icon;
34
- display: inline-block;
35
- width: 20px;
36
- height: 20px;
37
- vertical-align: bottom;
38
- border-bottom: none;
22
+ .publication_state_indicator {
23
+ height: 17px;
24
+ }
39
25
  }
40
26
 
41
27
  .legend {
42
- float: left;
43
- position: relative;
44
- padding: 0 15px 0 22px;
28
+ display: flex;
29
+ align-items: center;
30
+ color: $pageflow-hint-color;
45
31
 
46
32
  &:before {
47
33
  content: " ";
48
- position: absolute;
49
34
  display: block;
50
- left: 0;
51
- top: 0;
52
35
  width: 14px;
53
36
  height: 14px;
54
37
  border: solid 1px #999;
38
+ margin-right: 10px;
55
39
  }
56
40
 
57
41
  &.published:before {
58
- background-color: $published-color;
42
+ background-color: $pageflow-published-revision-background-color;
59
43
  }
60
44
  }
61
45
 
@@ -1,5 +1,7 @@
1
+ $pageflow-feature-select-width: 200px !default;
2
+
1
3
  form.features {
2
4
  select {
3
- width: 200px;
5
+ width: $pageflow-feature-select-width;
4
6
  }
5
7
  }
@@ -1,18 +1,20 @@
1
- form {
2
- fieldset p {
3
- margin-left: 20%;
4
- max-width: 550px;
5
- }
1
+ @if not $pageflow-custom-admin-theme {
2
+ form {
3
+ fieldset p {
4
+ margin-left: 20%;
5
+ max-width: 550px;
6
+ }
6
7
 
7
- .inline_help_item {
8
- padding: 5px 10px;
9
- }
8
+ .inline_help_item {
9
+ padding: 5px 10px;
10
+ }
10
11
 
11
- fieldset.actions .link_action {
12
- padding: 5px;
12
+ fieldset.actions .link_action {
13
+ padding: 5px;
13
14
 
14
- &.cancel {
15
- padding: 0;
15
+ &.cancel {
16
+ padding: 0;
17
+ }
16
18
  }
17
19
  }
18
20
  }