pageflow 17.0.5 → 17.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (636) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +342 -41
  3. data/README.md +3 -5
  4. data/admins/pageflow/accounts.rb +22 -20
  5. data/admins/pageflow/entry.rb +76 -26
  6. data/admins/pageflow/entry_templates.rb +10 -9
  7. data/admins/pageflow/folder.rb +1 -1
  8. data/admins/pageflow/membership.rb +15 -15
  9. data/admins/pageflow/revisions.rb +5 -5
  10. data/admins/pageflow/site_root_entry.rb +64 -0
  11. data/admins/pageflow/sites.rb +12 -3
  12. data/admins/pageflow/translations.rb +75 -0
  13. data/admins/pageflow/user.rb +21 -35
  14. data/app/assets/images/pageflow/admin/icons/comment.svg +1 -0
  15. data/app/assets/javascripts/pageflow/admin/entries.js +30 -5
  16. data/app/assets/javascripts/pageflow/dist/ui.js +4228 -900
  17. data/app/assets/javascripts/pageflow/editor/vendor.js +0 -1
  18. data/app/assets/javascripts/pageflow/ui.js +0 -1
  19. data/app/assets/stylesheets/pageflow/admin/entries/index_table.scss +2 -1
  20. data/app/assets/stylesheets/pageflow/admin/entry_comments_indicator.scss +45 -0
  21. data/app/assets/stylesheets/pageflow/admin/permalink_input.scss +8 -0
  22. data/app/assets/stylesheets/pageflow/admin.scss +1 -0
  23. data/app/assets/stylesheets/pageflow/animations/spin.scss +9 -0
  24. data/app/assets/stylesheets/pageflow/animations.scss +1 -0
  25. data/app/assets/stylesheets/pageflow/editor/background_positioning.scss +27 -5
  26. data/app/assets/stylesheets/pageflow/editor/base.scss +11 -1
  27. data/app/assets/stylesheets/pageflow/editor/drop_down_button.scss +20 -2
  28. data/app/assets/stylesheets/pageflow/editor/edit_configuration_view.scss +5 -0
  29. data/app/assets/stylesheets/pageflow/editor/file_folders.scss +127 -0
  30. data/app/assets/stylesheets/pageflow/editor/file_meta_data.scss +5 -2
  31. data/app/assets/stylesheets/pageflow/editor/file_meta_data_overlay.scss +45 -0
  32. data/app/assets/stylesheets/pageflow/editor/file_preview.scss +138 -0
  33. data/app/assets/stylesheets/pageflow/editor/file_references.scss +68 -0
  34. data/app/assets/stylesheets/pageflow/editor/file_settings_dialog.scss +1 -1
  35. data/app/assets/stylesheets/pageflow/editor/file_stage_icons.scss +17 -0
  36. data/app/assets/stylesheets/pageflow/editor/file_stages.scss +25 -32
  37. data/app/assets/stylesheets/pageflow/editor/file_thumbnails.scss +31 -48
  38. data/app/assets/stylesheets/pageflow/editor/file_type_pills.scss +73 -0
  39. data/app/assets/stylesheets/pageflow/editor/files.scss +242 -50
  40. data/app/assets/stylesheets/pageflow/editor/filtered_files.scss +161 -9
  41. data/app/assets/stylesheets/pageflow/editor/folder_breadcrumb.scss +40 -0
  42. data/app/assets/stylesheets/pageflow/editor/info_box.scss +23 -3
  43. data/app/assets/stylesheets/pageflow/editor/inputs/edit_defaults_button.scss +15 -0
  44. data/app/assets/stylesheets/pageflow/editor/inputs.scss +1 -0
  45. data/app/assets/stylesheets/pageflow/editor/list.scss +89 -37
  46. data/app/assets/stylesheets/pageflow/editor/list_search_field.scss +52 -0
  47. data/app/assets/stylesheets/pageflow/editor/manage_files.scss +9 -0
  48. data/app/assets/stylesheets/pageflow/editor/menu.scss +14 -0
  49. data/app/assets/stylesheets/pageflow/editor/move_to_folder_dialog.scss +83 -0
  50. data/app/assets/stylesheets/pageflow/editor/outline.scss +0 -13
  51. data/app/assets/stylesheets/pageflow/editor/sortable.scss +12 -0
  52. data/app/assets/stylesheets/pageflow/mixins/buttons.scss +4 -4
  53. data/app/assets/stylesheets/pageflow/mixins/pageflow.scss +9 -5
  54. data/app/assets/stylesheets/pageflow/ui/color_picker.scss +230 -0
  55. data/app/assets/stylesheets/pageflow/ui/forms.scss +38 -4
  56. data/app/assets/stylesheets/pageflow/ui/functions.scss +12 -2
  57. data/app/assets/stylesheets/pageflow/ui/input/color_input.scss +9 -8
  58. data/app/assets/stylesheets/pageflow/ui/input/file_name_input.scss +37 -0
  59. data/app/assets/stylesheets/pageflow/ui/input/text_area_input.scss +15 -0
  60. data/app/assets/stylesheets/pageflow/ui/overrides.scss +8 -0
  61. data/app/assets/stylesheets/pageflow/ui/properties.scss +34 -0
  62. data/app/assets/stylesheets/pageflow/ui/tabs_view.scss +8 -3
  63. data/app/assets/stylesheets/pageflow/ui.scss +4 -1
  64. data/app/controllers/concerns/pageflow/controller_delegation.rb +1 -1
  65. data/app/controllers/concerns/pageflow/edit_locking.rb +17 -10
  66. data/app/controllers/concerns/pageflow/entry_password_protection.rb +1 -0
  67. data/app/controllers/concerns/pageflow/public_https_mode.rb +2 -1
  68. data/app/controllers/concerns/pageflow/quota_verification.rb +5 -4
  69. data/app/controllers/pageflow/admin/initial_passwords_controller.rb +1 -0
  70. data/app/controllers/pageflow/application_controller.rb +19 -19
  71. data/app/controllers/pageflow/chapters_controller.rb +3 -2
  72. data/app/controllers/pageflow/edit_locks_controller.rb +3 -2
  73. data/app/controllers/pageflow/editor/encoding_confirmations_controller.rb +1 -0
  74. data/app/controllers/pageflow/editor/entries_controller.rb +3 -1
  75. data/app/controllers/pageflow/editor/entry_publications_controller.rb +1 -0
  76. data/app/controllers/pageflow/editor/file_folders_controller.rb +80 -0
  77. data/app/controllers/pageflow/editor/file_import_controller.rb +4 -3
  78. data/app/controllers/pageflow/editor/files_controller.rb +20 -10
  79. data/app/controllers/pageflow/editor/oembeds_controller.rb +21 -0
  80. data/app/controllers/pageflow/editor/widgets_controller.rb +1 -0
  81. data/app/controllers/pageflow/entries_controller.rb +85 -21
  82. data/app/controllers/pageflow/feeds_controller.rb +1 -1
  83. data/app/controllers/pageflow/files_controller.rb +1 -0
  84. data/app/controllers/pageflow/pages_controller.rb +4 -2
  85. data/app/controllers/pageflow/review/comment_thread_reads_controller.rb +30 -0
  86. data/app/controllers/pageflow/review/comment_threads_controller.rb +60 -0
  87. data/app/controllers/pageflow/review/comments_controller.rb +53 -0
  88. data/app/controllers/pageflow/revisions_controller.rb +5 -1
  89. data/app/controllers/pageflow/sitemaps_controller.rb +1 -1
  90. data/app/controllers/pageflow/storylines_controller.rb +1 -0
  91. data/app/helpers/pageflow/admin/cutoff_modes_helper.rb +12 -0
  92. data/app/helpers/pageflow/admin/entries_helper.rb +41 -1
  93. data/app/helpers/pageflow/admin/entry_translations_helper.rb +18 -0
  94. data/app/helpers/pageflow/admin/features_helper.rb +1 -1
  95. data/app/helpers/pageflow/admin/form_helper.rb +3 -3
  96. data/app/helpers/pageflow/admin/locales_helper.rb +10 -2
  97. data/app/helpers/pageflow/admin/memberships_helper.rb +32 -32
  98. data/app/helpers/pageflow/admin/revisions_helper.rb +1 -1
  99. data/app/helpers/pageflow/admin/sites_helper.rb +22 -0
  100. data/app/helpers/pageflow/admin/users_helper.rb +7 -5
  101. data/app/helpers/pageflow/admin/widgets_helper.rb +5 -5
  102. data/app/helpers/pageflow/asset_urls_helper.rb +1 -1
  103. data/app/helpers/pageflow/audio_files_helper.rb +5 -2
  104. data/app/helpers/pageflow/background_image_helper.rb +7 -6
  105. data/app/helpers/pageflow/common_entry_seed_helper.rb +7 -7
  106. data/app/helpers/pageflow/editor/config_helper.rb +3 -1
  107. data/app/helpers/pageflow/editor/files_helper.rb +1 -1
  108. data/app/helpers/pageflow/embed_code_helper.rb +3 -3
  109. data/app/helpers/pageflow/entries_helper.rb +52 -46
  110. data/app/helpers/pageflow/entry_json_seed_helper.rb +7 -6
  111. data/app/helpers/pageflow/feeds_helper.rb +2 -2
  112. data/app/helpers/pageflow/file_background_images_helper.rb +2 -2
  113. data/app/helpers/pageflow/file_thumbnails_helper.rb +4 -3
  114. data/app/helpers/pageflow/files_helper.rb +5 -4
  115. data/app/helpers/pageflow/folders_helper.rb +8 -5
  116. data/app/helpers/pageflow/help_entries_helper.rb +2 -2
  117. data/app/helpers/pageflow/hreflang_links_helper.rb +37 -0
  118. data/app/helpers/pageflow/info_box_helper.rb +7 -11
  119. data/app/helpers/pageflow/media_query_helper.rb +3 -3
  120. data/app/helpers/pageflow/navigation_bar_helper.rb +1 -1
  121. data/app/helpers/pageflow/overview_helper.rb +1 -1
  122. data/app/helpers/pageflow/page_types_helper.rb +5 -5
  123. data/app/helpers/pageflow/pages_helper.rb +29 -15
  124. data/app/helpers/pageflow/public_i18n_helper.rb +10 -9
  125. data/app/helpers/pageflow/quota_helper.rb +4 -4
  126. data/app/helpers/pageflow/render_json_helper.rb +6 -3
  127. data/app/helpers/pageflow/revision_file_helper.rb +2 -1
  128. data/app/helpers/pageflow/sites_helper.rb +3 -3
  129. data/app/helpers/pageflow/social_share_helper.rb +24 -10
  130. data/app/helpers/pageflow/social_share_links_helper.rb +12 -9
  131. data/app/helpers/pageflow/structured_data_helper.rb +5 -2
  132. data/app/helpers/pageflow/stub_page_configuration.rb +7 -3
  133. data/app/helpers/pageflow/text_direction_helper.rb +1 -1
  134. data/app/helpers/pageflow/themes_helper.rb +2 -4
  135. data/app/helpers/pageflow/video_files_helper.rb +29 -13
  136. data/app/helpers/pageflow/widgets_helper.rb +8 -6
  137. data/app/inputs/pageflow_permalink_input.rb +8 -3
  138. data/app/jobs/pageflow/application_job.rb +1 -0
  139. data/app/jobs/pageflow/entry_export_import/upload_and_publish_file_job.rb +1 -1
  140. data/app/jobs/pageflow/poll_meta_data_from_zencoder_job.rb +1 -0
  141. data/app/jobs/pageflow/poll_zencoder_job.rb +3 -2
  142. data/app/jobs/pageflow/process_image_or_text_track_job.rb +1 -0
  143. data/app/jobs/pageflow/prune_auto_snapshots_job.rb +1 -0
  144. data/app/jobs/pageflow/request_meta_data_from_zencoder_job.rb +1 -0
  145. data/app/jobs/pageflow/submit_file_to_zencoder_job.rb +1 -0
  146. data/app/mailers/pageflow/user_mailer.rb +1 -0
  147. data/app/models/concerns/pageflow/auto_generated_perma_id.rb +20 -1
  148. data/app/models/concerns/pageflow/entry_publication_states.rb +7 -3
  149. data/app/models/concerns/pageflow/feature_target.rb +8 -4
  150. data/app/models/concerns/pageflow/output_source.rb +3 -3
  151. data/app/models/concerns/pageflow/permalinkable.rb +2 -1
  152. data/app/models/concerns/pageflow/reusable_file.rb +24 -24
  153. data/app/models/concerns/pageflow/serialization_blacklist.rb +3 -5
  154. data/app/models/concerns/pageflow/suspendable.rb +5 -3
  155. data/app/models/concerns/pageflow/theme_referencer.rb +1 -1
  156. data/app/models/concerns/pageflow/translatable.rb +62 -0
  157. data/app/models/concerns/pageflow/translated_attributes.rb +101 -0
  158. data/app/models/concerns/pageflow/uploadable_file.rb +6 -6
  159. data/app/models/pageflow/account.rb +6 -5
  160. data/app/models/pageflow/account_member_query.rb +1 -1
  161. data/app/models/pageflow/account_role_query.rb +1 -1
  162. data/app/models/pageflow/application_query.rb +1 -1
  163. data/app/models/pageflow/application_record.rb +1 -1
  164. data/app/models/pageflow/audio_file.rb +4 -4
  165. data/app/models/pageflow/audio_file_url_templates.rb +1 -1
  166. data/app/models/pageflow/authentication_token.rb +2 -2
  167. data/app/models/pageflow/chapter.rb +6 -1
  168. data/app/models/pageflow/chapter_scaffold.rb +2 -4
  169. data/app/models/pageflow/cname_site_request_scope.rb +1 -1
  170. data/app/models/pageflow/comment.rb +30 -0
  171. data/app/models/pageflow/comment_thread.rb +65 -0
  172. data/app/models/pageflow/comment_thread_read.rb +22 -0
  173. data/app/models/pageflow/customized_theme.rb +9 -8
  174. data/app/models/pageflow/draft_entry.rb +49 -9
  175. data/app/models/pageflow/edit_lock.rb +19 -21
  176. data/app/models/pageflow/encoding_confirmation.rb +2 -2
  177. data/app/models/pageflow/entries_feed.rb +2 -2
  178. data/app/models/pageflow/entry.rb +46 -30
  179. data/app/models/pageflow/entry_at_revision.rb +14 -2
  180. data/app/models/pageflow/entry_comment_summary.rb +114 -0
  181. data/app/models/pageflow/entry_duplicate.rb +8 -5
  182. data/app/models/pageflow/entry_publication.rb +2 -2
  183. data/app/models/pageflow/entry_role_query.rb +4 -4
  184. data/app/models/pageflow/entry_template.rb +4 -4
  185. data/app/models/pageflow/entry_title_or_account_name_query.rb +2 -2
  186. data/app/models/pageflow/entry_translation_group.rb +42 -0
  187. data/app/models/pageflow/file_folder.rb +74 -0
  188. data/app/models/pageflow/file_reuse.rb +5 -4
  189. data/app/models/pageflow/file_usage.rb +18 -3
  190. data/app/models/pageflow/folder.rb +2 -2
  191. data/app/models/pageflow/home_button.rb +7 -7
  192. data/app/models/pageflow/image_file.rb +44 -25
  193. data/app/models/pageflow/image_file_url_templates.rb +4 -4
  194. data/app/models/pageflow/invitation_form.rb +2 -1
  195. data/app/models/pageflow/legal_info.rb +38 -0
  196. data/app/models/pageflow/managed_user_query.rb +1 -1
  197. data/app/models/pageflow/membership.rb +5 -5
  198. data/app/models/pageflow/nested_revision_component_copy.rb +263 -0
  199. data/app/models/pageflow/null_user.rb +1 -1
  200. data/app/models/pageflow/overview_button.rb +1 -1
  201. data/app/models/pageflow/page.rb +7 -3
  202. data/app/models/pageflow/permalink.rb +24 -2
  203. data/app/models/pageflow/permalink_directory.rb +7 -0
  204. data/app/models/pageflow/permalink_redirect.rb +7 -0
  205. data/app/models/pageflow/positioned_file.rb +5 -5
  206. data/app/models/pageflow/potential_entry_translations.rb +55 -0
  207. data/app/models/pageflow/potential_memberships.rb +5 -4
  208. data/app/models/pageflow/published_entry.rb +62 -11
  209. data/app/models/pageflow/revision.rb +29 -16
  210. data/app/models/pageflow/roles.rb +14 -18
  211. data/app/models/pageflow/site.rb +46 -3
  212. data/app/models/pageflow/site_root_entry_form.rb +27 -0
  213. data/app/models/pageflow/sitemaps.rb +10 -1
  214. data/app/models/pageflow/storyline.rb +5 -2
  215. data/app/models/pageflow/storyline_scaffold.rb +1 -1
  216. data/app/models/pageflow/text_track_file.rb +1 -1
  217. data/app/models/pageflow/text_track_file_url_templates.rb +1 -1
  218. data/app/models/pageflow/theme_customization_file.rb +3 -2
  219. data/app/models/pageflow/thumbnail_file_resolver.rb +1 -1
  220. data/app/models/pageflow/url_template.rb +1 -1
  221. data/app/models/pageflow/used_file.rb +13 -3
  222. data/app/models/pageflow/user_name_query.rb +2 -2
  223. data/app/models/pageflow/video_file.rb +22 -15
  224. data/app/models/pageflow/video_file_url_templates.rb +6 -4
  225. data/app/models/pageflow/widget.rb +5 -4
  226. data/app/models/pageflow/with_file_usage_extension.rb +1 -1
  227. data/app/models/pageflow/zencoder_attachment.rb +8 -8
  228. data/app/policies/pageflow/account_policy.rb +3 -1
  229. data/app/policies/pageflow/admin/admin_only_tab_policy.rb +1 -0
  230. data/app/policies/pageflow/admin/entry_tab_policy.rb +1 -0
  231. data/app/policies/pageflow/application_policy.rb +2 -0
  232. data/app/policies/pageflow/entry_policy.rb +7 -1
  233. data/app/policies/pageflow/entry_template_policy.rb +2 -1
  234. data/app/policies/pageflow/file_policy.rb +4 -7
  235. data/app/policies/pageflow/folder_policy.rb +18 -8
  236. data/app/policies/pageflow/membership_policy.rb +6 -4
  237. data/app/policies/pageflow/site_policy.rb +10 -3
  238. data/app/policies/pageflow/user_policy.rb +6 -4
  239. data/app/state_machines/pageflow/image_and_text_track_processing_state_machine.rb +3 -2
  240. data/app/state_machines/pageflow/media_encoding_state_machine.rb +5 -4
  241. data/app/views/admin/accounts/_entry_template_details.html.arb +2 -2
  242. data/app/views/admin/entries/_attributes_table.html.arb +6 -5
  243. data/app/views/admin/entries/_form.html.erb +2 -1
  244. data/app/views/admin/entries/_permalink_inputs.html.erb +9 -2
  245. data/app/views/admin/entry_templates/_form.html.erb +1 -3
  246. data/app/views/admin/site_root_entry/choose.html.erb +19 -0
  247. data/app/views/admin/sites/_fields.html.erb +14 -1
  248. data/app/views/admin/translations/_form.html.erb +31 -0
  249. data/app/views/admin/users/_attributes_table.html.arb +13 -0
  250. data/app/views/components/pageflow/admin/add_membership_button.rb +13 -12
  251. data/app/views/components/pageflow/admin/custom_scopes_renderer.rb +1 -0
  252. data/app/views/components/pageflow/admin/embed_code_field.rb +1 -0
  253. data/app/views/components/pageflow/admin/embedded_index_table.rb +9 -10
  254. data/app/views/components/pageflow/admin/entries_tab.rb +1 -0
  255. data/app/views/components/pageflow/admin/entry_publication_state_indicator.rb +6 -5
  256. data/app/views/components/pageflow/admin/entry_templates_tab.rb +2 -1
  257. data/app/views/components/pageflow/admin/entry_translations_tab.rb +102 -0
  258. data/app/views/components/pageflow/admin/entry_user_badge_list.rb +1 -0
  259. data/app/views/components/pageflow/admin/extensible_attributes_table.rb +9 -8
  260. data/app/views/components/pageflow/admin/features_tab.rb +1 -0
  261. data/app/views/components/pageflow/admin/grouped_folder_list.rb +1 -0
  262. data/app/views/components/pageflow/admin/icon_link_to.rb +1 -0
  263. data/app/views/components/pageflow/admin/members_tab.rb +4 -3
  264. data/app/views/components/pageflow/admin/membership_role_with_tooltip.rb +3 -2
  265. data/app/views/components/pageflow/admin/revisions_tab.rb +14 -10
  266. data/app/views/components/pageflow/admin/sites_tab.rb +1 -0
  267. data/app/views/components/pageflow/admin/tabs_view.rb +1 -0
  268. data/app/views/components/pageflow/admin/timestamp.rb +6 -5
  269. data/app/views/components/pageflow/admin/user_account_badge_list.rb +2 -3
  270. data/app/views/components/pageflow/admin/user_accounts_tab.rb +1 -0
  271. data/app/views/components/pageflow/admin/user_entries_tab.rb +1 -0
  272. data/app/views/components/pageflow/admin/users_tab.rb +1 -0
  273. data/app/views/pageflow/audio_files/_audio_file.json.jbuilder +0 -1
  274. data/app/views/pageflow/editor/config/_seeds.json.jbuilder +5 -3
  275. data/app/views/pageflow/editor/entries/_entry.json.jbuilder +2 -1
  276. data/app/views/pageflow/editor/entries/index.json.jbuilder +1 -1
  277. data/app/views/pageflow/editor/entries/seed.json.erb +1 -0
  278. data/app/views/pageflow/editor/entries/show.json.jbuilder +1 -1
  279. data/app/views/pageflow/editor/entry_publications/check.json.jbuilder +2 -2
  280. data/app/views/pageflow/editor/file_folders/_file_folder.json.jbuilder +5 -0
  281. data/app/views/pageflow/editor/file_folders/create.json.jbuilder +1 -0
  282. data/app/views/pageflow/editor/file_folders/index.json.jbuilder +3 -0
  283. data/app/views/pageflow/editor/files/_file.json.jbuilder +7 -14
  284. data/app/views/pageflow/editor/files/create.json.jbuilder +1 -1
  285. data/app/views/pageflow/editor/files/index.json.jbuilder +1 -1
  286. data/app/views/pageflow/editor/image_files/_image_file.json.jbuilder +3 -0
  287. data/app/views/pageflow/editor/sites/_site.json.jbuilder +1 -0
  288. data/app/views/pageflow/editor/video_files/_video_file.json.jbuilder +4 -3
  289. data/app/views/pageflow/entries/share_menu/_bluesky_link.html.erb +8 -0
  290. data/app/views/pageflow/entries/share_menu/_threads_link.html.erb +8 -0
  291. data/app/views/pageflow/files/_file.json.jbuilder +5 -3
  292. data/app/views/pageflow/image_files/_image_file.json.jbuilder +0 -1
  293. data/app/views/pageflow/review/comment_threads/_comment_thread.json.jbuilder +20 -0
  294. data/app/views/pageflow/review/comment_threads/create.json.jbuilder +2 -0
  295. data/app/views/pageflow/review/comment_threads/index.json.jbuilder +13 -0
  296. data/app/views/pageflow/review/comments/_comment.json.jbuilder +13 -0
  297. data/app/views/pageflow/review/comments/create.json.jbuilder +1 -0
  298. data/app/views/pageflow/sitemaps/index.xml.builder +9 -1
  299. data/app/views/pageflow/social_share/_entry_meta_tags.html.erb +1 -1
  300. data/app/views/pageflow/social_share/_page_meta_tags.html.erb +1 -1
  301. data/app/views/pageflow/structured_data/_entry.json.jbuilder +2 -3
  302. data/app/views/pageflow/video_files/_video_file.json.jbuilder +0 -1
  303. data/config/initializers/active_admin_patches.rb +3 -6
  304. data/config/initializers/admin_resource_tabs.rb +3 -0
  305. data/config/initializers/paperclip.rb +1 -0
  306. data/config/initializers/symmetric_encryption.rb +1 -1
  307. data/config/initializers/zencoder.rb +24 -13
  308. data/config/locales/de.yml +256 -12
  309. data/config/locales/en.yml +253 -15
  310. data/config/routes.rb +29 -13
  311. data/config/spring.rb +2 -1
  312. data/db/migrate/20140418225525_setup_schema.rb +2 -2
  313. data/db/migrate/20190306161431_copy_file_attributes_of_failed_uploads.rb +2 -2
  314. data/db/migrate/20190820152900_drop_accounts_themes.rb +2 -1
  315. data/db/migrate/20191202145757_create_pageflow_scrolled_sections.rb +11 -11
  316. data/db/migrate/20191202150657_create_pageflow_scrolled_chapters.rb +1 -1
  317. data/db/migrate/20191202154723_create_pageflow_scrolled_content_elements.rb +9 -9
  318. data/db/migrate/20191219143450_add_position_to_content_elements.rb +2 -1
  319. data/db/migrate/20200117133200_change_revision_appearance_option_default_and_null.rb +2 -1
  320. data/db/migrate/20200122115400_create_pageflow_entry_templates.rb +25 -25
  321. data/db/migrate/20200206134400_convert_legacy_scrolled_content_element_types.rb +3 -3
  322. data/db/migrate/20221215120856_associate_entry_templates_with_sites.rb +2 -2
  323. data/db/migrate/20230120092923_create_other_files.rb +2 -1
  324. data/db/migrate/20230419083307_create_pageflow_entry_translation_group.rb +9 -0
  325. data/db/migrate/20240612110434_add_cutoff_mode_name_to_sites.rb +5 -0
  326. data/db/migrate/20240918084059_create_pageflow_permalink_redirects.rb +14 -0
  327. data/db/migrate/20250508172234_ensure_scrolled_entries_have_main_storyline.rb +14 -0
  328. data/db/migrate/20250617090048_add_custom404_entry_to_sites.rb +5 -0
  329. data/db/migrate/20250617100000_add_perma_id_counter_to_entries.rb +28 -0
  330. data/db/migrate/20250722174123_add_perma_id_to_chapters.rb +10 -0
  331. data/db/migrate/20250725080603_add_perma_id_indexes_to_revision_components.rb +13 -0
  332. data/db/migrate/20250726000000_add_display_name_to_file_usages.rb +5 -0
  333. data/db/migrate/20251015000000_add_structured_data_type_name_to_revisions.rb +5 -0
  334. data/db/migrate/20251029142849_convert_legacy_social_embeds_to_unified_social_embed.rb +38 -0
  335. data/db/migrate/20251119083347_fix_main_storyline_positions.rb +16 -0
  336. data/db/migrate/20251119083348_ensure_scrolled_entries_have_excursion_storyline.rb +15 -0
  337. data/db/migrate/20260303000000_add_layout_version_to_revisions.rb +6 -0
  338. data/db/migrate/20260323000000_create_comment_threads_and_comments.rb +30 -0
  339. data/db/migrate/20260414000000_add_subject_range_to_comment_threads.rb +5 -0
  340. data/db/migrate/20260415000000_add_section_perma_id_to_comment_threads.rb +5 -0
  341. data/db/migrate/20260727000000_add_quote_to_comments.rb +5 -0
  342. data/db/migrate/20260728000000_create_file_folders.rb +17 -0
  343. data/db/migrate/20260805000000_add_attribute_translations_to_sites.rb +5 -0
  344. data/db/migrate/20260806000000_add_edited_at_to_comments.rb +5 -0
  345. data/db/migrate/20260810000000_create_pageflow_scrolled_lottie_files.rb +24 -0
  346. data/db/migrate/20260817000000_create_comment_thread_reads.rb +16 -0
  347. data/db/migrate/20260819000000_add_unread_comments_since_at_to_users.rb +18 -0
  348. data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/editor.js +12599 -5398
  349. data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/frontend.js +1275 -4560
  350. data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/react-client.js +20204 -22
  351. data/entry_types/paged/app/assets/javascripts/pageflow_paged/dist/react-server.js +18657 -19
  352. data/entry_types/paged/app/controllers/pageflow_paged/entries_controller.rb +1 -0
  353. data/entry_types/paged/app/helpers/pageflow_paged/page_background_asset_helper.rb +1 -1
  354. data/entry_types/paged/app/helpers/pageflow_paged/third_party_embed_consent_helper.rb +7 -7
  355. data/entry_types/paged/app/views/pageflow_paged/editor/entries/_seed.json.jbuilder +1 -0
  356. data/entry_types/paged/app/views/pageflow_paged/entries/show.html.erb +1 -0
  357. data/entry_types/paged/lib/pageflow_paged/plugin.rb +4 -0
  358. data/entry_types/paged/lib/pageflow_paged/react/page_type.rb +1 -1
  359. data/entry_types/paged/lib/pageflow_paged/react/widget_type.rb +3 -3
  360. data/entry_types/paged/lib/pageflow_paged.rb +1 -1
  361. data/entry_types/scrolled/app/assets/stylesheets/pageflow_scrolled/ui.scss +2 -0
  362. data/entry_types/scrolled/app/controllers/pageflow_scrolled/editor/chapters_controller.rb +5 -3
  363. data/entry_types/scrolled/app/controllers/pageflow_scrolled/editor/content_elements_controller.rb +35 -5
  364. data/entry_types/scrolled/app/controllers/pageflow_scrolled/editor/sections_controller.rb +4 -5
  365. data/entry_types/scrolled/app/controllers/pageflow_scrolled/entries_controller.rb +23 -2
  366. data/entry_types/scrolled/app/helpers/pageflow_scrolled/cache_helper.rb +3 -3
  367. data/entry_types/scrolled/app/helpers/pageflow_scrolled/editor/entry_json_seed_helper.rb +8 -5
  368. data/entry_types/scrolled/app/helpers/pageflow_scrolled/editor/seed_html_helper.rb +7 -3
  369. data/entry_types/scrolled/app/helpers/pageflow_scrolled/entry_json_seed_helper.rb +72 -10
  370. data/entry_types/scrolled/app/helpers/pageflow_scrolled/i18n_helper.rb +28 -13
  371. data/entry_types/scrolled/app/helpers/pageflow_scrolled/packs_helper.rb +41 -12
  372. data/entry_types/scrolled/app/helpers/pageflow_scrolled/react_server_side_rendering_helper.rb +23 -25
  373. data/entry_types/scrolled/app/helpers/pageflow_scrolled/sprockets_helper.rb +16 -0
  374. data/entry_types/scrolled/app/helpers/pageflow_scrolled/themes_helper.rb +189 -3
  375. data/entry_types/scrolled/app/models/pageflow_scrolled/chapter.rb +15 -14
  376. data/entry_types/scrolled/app/models/pageflow_scrolled/content_element.rb +61 -7
  377. data/entry_types/scrolled/app/models/pageflow_scrolled/lottie_file.rb +6 -0
  378. data/entry_types/scrolled/app/models/pageflow_scrolled/lottie_file_url_templates.rb +16 -0
  379. data/entry_types/scrolled/app/models/pageflow_scrolled/section.rb +12 -1
  380. data/entry_types/scrolled/app/models/pageflow_scrolled/storyline.rb +20 -1
  381. data/entry_types/scrolled/app/views/pageflow_scrolled/editor/entries/_head.html.erb +1 -0
  382. data/entry_types/scrolled/app/views/pageflow_scrolled/editor/sections/_section_with_content_elements.json.jbuilder +2 -2
  383. data/entry_types/scrolled/app/views/pageflow_scrolled/entries/show.html.erb +15 -12
  384. data/entry_types/scrolled/app/views/pageflow_scrolled/entry_json_seed/_entry.json.jbuilder +45 -17
  385. data/entry_types/scrolled/app/views/pageflow_scrolled/entry_json_seed/_entry_translations.json.jbuilder +14 -0
  386. data/entry_types/scrolled/app/views/pageflow_scrolled/entry_json_seed/_theme.json.jbuilder +34 -5
  387. data/entry_types/scrolled/app/views/pageflow_scrolled/favicons/_entry.html.erb +1 -1
  388. data/entry_types/scrolled/app/views/pageflow_scrolled/storylines/_storyline.json.jbuilder +7 -0
  389. data/entry_types/scrolled/config/configuration_schemas/pageflow-scrolled.json +414 -0
  390. data/entry_types/scrolled/config/locales/de.yml +1094 -78
  391. data/entry_types/scrolled/config/locales/en.yml +1083 -78
  392. data/entry_types/scrolled/config/routes.rb +18 -8
  393. data/entry_types/scrolled/lib/generators/pageflow_scrolled/install/install_generator.rb +11 -11
  394. data/entry_types/scrolled/lib/generators/pageflow_scrolled/install/templates/theme/logoDarkVariantDesktop.svg +56 -0
  395. data/entry_types/scrolled/lib/generators/pageflow_scrolled/install/templates/theme/logoDarkVariantMobile.svg +22 -0
  396. data/entry_types/scrolled/lib/generators/pageflow_scrolled/install/templates/themes_plugin.rb.tt +26 -11
  397. data/entry_types/scrolled/lib/pageflow_scrolled/additional_packs.rb +27 -8
  398. data/entry_types/scrolled/lib/pageflow_scrolled/additional_seed_data.rb +2 -2
  399. data/entry_types/scrolled/lib/pageflow_scrolled/additional_theme_assets.rb +27 -0
  400. data/entry_types/scrolled/lib/pageflow_scrolled/configuration.rb +67 -10
  401. data/entry_types/scrolled/lib/pageflow_scrolled/configuration_schemas.rb +91 -0
  402. data/entry_types/scrolled/lib/pageflow_scrolled/content_element_consent_vendors.rb +1 -1
  403. data/entry_types/scrolled/lib/pageflow_scrolled/engine.rb +2 -1
  404. data/entry_types/scrolled/lib/pageflow_scrolled/entry_file_reference_locations.rb +71 -0
  405. data/entry_types/scrolled/lib/pageflow_scrolled/entry_structured_data_types/faq_page.rb +58 -0
  406. data/entry_types/scrolled/lib/pageflow_scrolled/file_reference_locations.rb +69 -0
  407. data/entry_types/scrolled/lib/pageflow_scrolled/plugin.rb +222 -19
  408. data/entry_types/scrolled/lib/pageflow_scrolled/react_widget_type.rb +2 -4
  409. data/entry_types/scrolled/lib/pageflow_scrolled/seeds.rb +76 -54
  410. data/entry_types/scrolled/lib/pageflow_scrolled/theme_options_default_scale.rb +35 -0
  411. data/entry_types/scrolled/lib/pageflow_scrolled.rb +14 -1
  412. data/entry_types/scrolled/lib/tasks/pageflow_scrolled/storybook.rake +24 -5
  413. data/entry_types/scrolled/package/config/configurationSchemas.js +70 -0
  414. data/entry_types/scrolled/package/config/rollup/configurationSchemas.js +19 -0
  415. data/entry_types/scrolled/package/config/webpack.js +94 -0
  416. data/entry_types/scrolled/package/contentElements/hotspots-frontend.css +1 -0
  417. data/entry_types/scrolled/package/contentElements/hotspots-frontend.js +1795 -0
  418. data/entry_types/scrolled/package/contentElements/inlineBeforeAfter-frontend.css +1 -0
  419. data/entry_types/scrolled/package/contentElements/inlineBeforeAfter-frontend.js +219 -0
  420. data/entry_types/scrolled/package/contentElements/lottieAnimation-frontend.css +1 -0
  421. data/entry_types/scrolled/package/contentElements/lottieAnimation-frontend.js +244 -0
  422. data/entry_types/scrolled/package/contentElements/socialEmbed-frontend.css +1 -0
  423. data/entry_types/scrolled/package/contentElements/socialEmbed-frontend.js +823 -0
  424. data/entry_types/scrolled/package/contentElements/tikTokEmbed-frontend.css +1 -0
  425. data/entry_types/scrolled/package/contentElements/tikTokEmbed-frontend.js +109 -0
  426. data/entry_types/scrolled/package/contentElements/twitterEmbed-frontend.css +1 -0
  427. data/entry_types/scrolled/package/contentElements/twitterEmbed-frontend.js +277 -0
  428. data/entry_types/scrolled/package/contentElements/videoEmbed-frontend.css +1 -0
  429. data/entry_types/scrolled/package/contentElements/videoEmbed-frontend.js +244 -0
  430. data/entry_types/scrolled/package/contentElements-editor.js +3155 -583
  431. data/entry_types/scrolled/package/contentElements-frontend.css +1 -1
  432. data/entry_types/scrolled/package/contentElements-frontend.js +3375 -1585
  433. data/entry_types/scrolled/package/editor.css +1 -0
  434. data/entry_types/scrolled/package/editor.js +8452 -2867
  435. data/entry_types/scrolled/package/entryState.js +1600 -0
  436. data/entry_types/scrolled/package/frontend/FloatingPortalRootProvider-20c600de.js +1979 -0
  437. data/entry_types/scrolled/package/frontend/FloatingPortalRootProvider-58189220.js +2538 -0
  438. data/entry_types/scrolled/package/frontend/{PhonePlatformContext-4ec6b2de.js → PhonePlatformContext-035a99fa.js} +9 -15
  439. data/entry_types/scrolled/package/frontend/PhonePlatformContext-9da43797.js +33 -0
  440. data/entry_types/scrolled/package/frontend/Placeholder.module-bf0966b4.js +5267 -0
  441. data/entry_types/scrolled/package/frontend/Placeholder.module-d22a9335.js +4180 -0
  442. data/entry_types/scrolled/package/frontend/ThemeIcon-ab834849.js +754 -0
  443. data/entry_types/scrolled/package/frontend/ThemeIcon-f6cb3a8f.js +773 -0
  444. data/entry_types/scrolled/package/frontend/ToggleFullscreenCornerButton-30fd1124.js +39 -0
  445. data/entry_types/scrolled/package/frontend/ToggleFullscreenCornerButton-61c25224.js +72 -0
  446. data/entry_types/scrolled/package/frontend/Viewer-1074fd59.js +147 -0
  447. data/entry_types/scrolled/package/frontend/Viewer-1482ca34.js +480 -0
  448. data/entry_types/scrolled/package/frontend/Viewer-70504231.js +210 -0
  449. data/entry_types/scrolled/package/frontend/{Viewer-1bb5597c.js → Viewer-cc8d4072.js} +100 -135
  450. data/entry_types/scrolled/package/frontend/Wavesurfer-656e3c36.js +302 -0
  451. data/entry_types/scrolled/package/frontend/Wavesurfer-b753b838.js +385 -0
  452. data/entry_types/scrolled/package/frontend/commenting.css +1 -0
  453. data/entry_types/scrolled/package/frontend/extensions-167ae143.js +1455 -0
  454. data/entry_types/scrolled/package/frontend/extensions-2318f92b.js +5918 -0
  455. data/entry_types/scrolled/package/frontend/extensions-a461fd13.js +4890 -0
  456. data/entry_types/scrolled/package/frontend/extensions-c98269fd.js +1982 -0
  457. data/entry_types/scrolled/package/frontend/i18n-493cd2a6.js +93 -0
  458. data/entry_types/scrolled/package/frontend/i18n-e06cac14.js +100 -0
  459. data/entry_types/scrolled/package/frontend/{index-11f32f10.js → index-2e5546f7.js} +39 -54
  460. data/entry_types/scrolled/package/frontend/index-de2b418d.js +130 -0
  461. data/entry_types/scrolled/package/frontend/index.css +1 -1
  462. data/entry_types/scrolled/package/frontend/index.js +1976 -1367
  463. data/entry_types/scrolled/package/frontend/inlineEditing.css +1 -0
  464. data/entry_types/scrolled/package/frontend/useContentElementEditorState-a084912e.js +25 -0
  465. data/entry_types/scrolled/package/frontend/useDelayedBoolean-a387d85b.js +24 -0
  466. data/entry_types/scrolled/package/frontend/usePhonePlatform-4ecb96d4.js +8 -0
  467. data/entry_types/scrolled/package/frontend/usePhonePlatform-ce9ff08d.js +8 -0
  468. data/entry_types/scrolled/package/frontend-server.js +20 -210
  469. data/entry_types/scrolled/package/package.json +22 -12
  470. data/entry_types/scrolled/package/review.css +1 -0
  471. data/entry_types/scrolled/package/review.js +3306 -0
  472. data/entry_types/scrolled/package/testHelpers.js +952 -239
  473. data/entry_types/scrolled/package/values/widgets.module.css +18 -0
  474. data/entry_types/scrolled/package/widgets/consentBar.css +1 -1
  475. data/entry_types/scrolled/package/widgets/consentBar.js +275 -315
  476. data/entry_types/scrolled/package/widgets/defaultNavigation.css +2 -2
  477. data/entry_types/scrolled/package/widgets/defaultNavigation.js +961 -432
  478. data/entry_types/scrolled/package/widgets/excursionSheet.css +1 -0
  479. data/entry_types/scrolled/package/widgets/excursionSheet.js +225 -0
  480. data/entry_types/scrolled/package/widgets/iconInlineFileRights.css +1 -1
  481. data/entry_types/scrolled/package/widgets/iconInlineFileRights.js +62 -32
  482. data/entry_types/scrolled/package/widgets/iconScrollIndicator.css +1 -0
  483. data/entry_types/scrolled/package/widgets/iconScrollIndicator.js +67 -0
  484. data/entry_types/scrolled/package/widgets/mainStorylineSheet.css +1 -0
  485. data/entry_types/scrolled/package/widgets/mainStorylineSheet.js +100 -0
  486. data/entry_types/scrolled/package/widgets/textInlineFileRights.css +1 -1
  487. data/entry_types/scrolled/package/widgets/textInlineFileRights.js +48 -26
  488. data/entry_types/scrolled/package/widgets-server.js +2 -0
  489. data/entry_types/scrolled/spec/factories/chapters.rb +14 -1
  490. data/entry_types/scrolled/spec/factories/content_elements.rb +1 -1
  491. data/entry_types/scrolled/spec/factories/lottie_files.rb +29 -0
  492. data/entry_types/scrolled/spec/fixtures/animation.lottie +0 -0
  493. data/entry_types/scrolled/spec/fixtures/font.woff +0 -0
  494. data/entry_types/scrolled/spec/fixtures/font.woff2 +0 -0
  495. data/lib/generators/pageflow/active_admin_initializer/active_admin_initializer_generator.rb +5 -3
  496. data/lib/generators/pageflow/assets/assets_generator.rb +7 -5
  497. data/lib/generators/pageflow/cancan/cancan_generator.rb +2 -1
  498. data/lib/generators/pageflow/cancan/templates/ability.rb +1 -1
  499. data/lib/generators/pageflow/error_pages/error_pages_generator.rb +2 -1
  500. data/lib/generators/pageflow/initializer/initializer_generator.rb +3 -2
  501. data/lib/generators/pageflow/install/install_generator.rb +2 -2
  502. data/lib/generators/pageflow/procfile/procfile_generator.rb +2 -1
  503. data/lib/generators/pageflow/resque/resque_generator.rb +4 -4
  504. data/lib/generators/pageflow/resque/templates/resque.rake +1 -1
  505. data/lib/generators/pageflow/routes/routes_generator.rb +4 -2
  506. data/lib/generators/pageflow/seeds/seeds_generator.rb +3 -2
  507. data/lib/generators/pageflow/seeds/templates/seeds.rb +6 -6
  508. data/lib/generators/pageflow/theme/theme_generator.rb +2 -1
  509. data/lib/generators/pageflow/user/user_generator.rb +2 -1
  510. data/lib/pageflow/ability_mixin.rb +25 -15
  511. data/lib/pageflow/active_admin_patches/views/attributes_table.rb +1 -0
  512. data/lib/pageflow/active_admin_patches/views/pages/base.rb +3 -2
  513. data/lib/pageflow/active_admin_patches/views/table_for.rb +1 -0
  514. data/lib/pageflow/additional_headers.rb +27 -0
  515. data/lib/pageflow/admin/attributes_table_rows.rb +3 -3
  516. data/lib/pageflow/admin/tabs.rb +1 -1
  517. data/lib/pageflow/built_in_file_type.rb +1 -1
  518. data/lib/pageflow/built_in_page_types_plugin.rb +1 -1
  519. data/lib/pageflow/built_in_widget_type.rb +2 -2
  520. data/lib/pageflow/built_in_widget_types_plugin.rb +2 -1
  521. data/lib/pageflow/configuration.rb +183 -18
  522. data/lib/pageflow/cutoff_modes.rb +39 -0
  523. data/lib/pageflow/deprecation.rb +19 -0
  524. data/lib/pageflow/editor_controller.rb +6 -10
  525. data/lib/pageflow/engine.rb +5 -6
  526. data/lib/pageflow/entries_controller_env_helper.rb +32 -2
  527. data/lib/pageflow/entry_export_import/attachment_files.rb +1 -1
  528. data/lib/pageflow/entry_export_import/entry_serialization.rb +9 -8
  529. data/lib/pageflow/entry_export_import/file_mappings.rb +1 -0
  530. data/lib/pageflow/entry_export_import/page_type_versions.rb +2 -2
  531. data/lib/pageflow/entry_export_import/revision_serialization/import.rb +11 -1
  532. data/lib/pageflow/entry_export_import/revision_serialization.rb +3 -0
  533. data/lib/pageflow/entry_export_import.rb +3 -3
  534. data/lib/pageflow/entry_structured_data_types.rb +43 -0
  535. data/lib/pageflow/entry_type_configuration.rb +1 -0
  536. data/lib/pageflow/entry_types.rb +2 -2
  537. data/lib/pageflow/feature.rb +1 -1
  538. data/lib/pageflow/features.rb +4 -6
  539. data/lib/pageflow/file_importers.rb +4 -4
  540. data/lib/pageflow/file_type.rb +5 -5
  541. data/lib/pageflow/file_types.rb +4 -4
  542. data/lib/pageflow/global_config_api.rb +5 -6
  543. data/lib/pageflow/help_entries.rb +7 -7
  544. data/lib/pageflow/help_entry.rb +1 -1
  545. data/lib/pageflow/hooks.rb +1 -1
  546. data/lib/pageflow/nested_revision_component.rb +9 -27
  547. data/lib/pageflow/news_item_api.rb +1 -1
  548. data/lib/pageflow/oembed_providers.rb +42 -0
  549. data/lib/pageflow/page_type.rb +2 -3
  550. data/lib/pageflow/page_types.rb +3 -3
  551. data/lib/pageflow/paperclip_interpolations/support.rb +1 -1
  552. data/lib/pageflow/paperclip_processors/vtt.rb +1 -0
  553. data/lib/pageflow/paperclip_processors/webp.rb +2 -2
  554. data/lib/pageflow/partial_editor_fragment_renderer.rb +2 -2
  555. data/lib/pageflow/plugin.rb +1 -2
  556. data/lib/pageflow/primary_domain_entry_redirect.rb +6 -0
  557. data/lib/pageflow/quota.rb +9 -5
  558. data/lib/pageflow/quotas.rb +1 -1
  559. data/lib/pageflow/rails_version.rb +14 -2
  560. data/lib/pageflow/react.rb +1 -1
  561. data/lib/pageflow/revision_component.rb +8 -40
  562. data/lib/pageflow/revision_components.rb +16 -6
  563. data/lib/pageflow/seeds.rb +12 -16
  564. data/lib/pageflow/theme.rb +1 -1
  565. data/lib/pageflow/theme_customizations.rb +10 -10
  566. data/lib/pageflow/themes.rb +44 -3
  567. data/lib/pageflow/user_mixin.rb +18 -6
  568. data/lib/pageflow/version.rb +1 -1
  569. data/lib/pageflow/widget_type.rb +4 -4
  570. data/lib/pageflow/widget_types.rb +9 -9
  571. data/lib/pageflow/zencoder_api.rb +32 -42
  572. data/lib/pageflow/zencoder_audio_output_definition.rb +14 -13
  573. data/lib/pageflow/zencoder_meta_data_output_definition.rb +3 -2
  574. data/lib/pageflow/zencoder_output_definition.rb +16 -14
  575. data/lib/pageflow/zencoder_video_output_definition.rb +140 -88
  576. data/lib/pageflow.rb +8 -2
  577. data/lib/tasks/pageflow_tasks.rake +1 -1
  578. data/package/config/eslint-rules/documented-in-toc.js +125 -0
  579. data/package/config/jest/index.js +0 -1
  580. data/package/config/webpack.js +0 -1
  581. data/package/config/webpack5.js +0 -1
  582. data/package/editor.js +4880 -2313
  583. data/package/frontend.js +710 -1223
  584. data/package/package.json +3 -1
  585. data/package/review.js +384 -0
  586. data/package/testHelpers.js +257 -243
  587. data/package/ui.js +1487 -813
  588. data/package/vendor/stubs/wysihtml5.js +2 -0
  589. data/spec/factories/accounts.rb +37 -22
  590. data/spec/factories/audio_files.rb +2 -2
  591. data/spec/factories/authentication_tokens.rb +4 -4
  592. data/spec/factories/chapters.rb +4 -4
  593. data/spec/factories/comment_thread_reads.rb +10 -0
  594. data/spec/factories/comment_threads.rb +10 -0
  595. data/spec/factories/comments.rb +9 -0
  596. data/spec/factories/draft_entries.rb +14 -6
  597. data/spec/factories/edit_locks.rb +1 -1
  598. data/spec/factories/entries.rb +28 -19
  599. data/spec/factories/entry_translation_groups.rb +6 -0
  600. data/spec/factories/file_folders.rb +8 -0
  601. data/spec/factories/file_usages.rb +2 -1
  602. data/spec/factories/folders.rb +1 -1
  603. data/spec/factories/image_files.rb +2 -2
  604. data/spec/factories/pages.rb +3 -3
  605. data/spec/factories/published_entries.rb +28 -8
  606. data/spec/factories/revisions.rb +1 -1
  607. data/spec/factories/sites.rb +6 -0
  608. data/spec/factories/test_multi_attachment_files.rb +1 -1
  609. data/spec/factories/text_track_files.rb +1 -1
  610. data/spec/factories/uploadable_files.rb +1 -1
  611. data/spec/factories/users.rb +6 -6
  612. data/spec/factories/video_files.rb +2 -2
  613. data/spec/factories/widgets.rb +1 -1
  614. data/spec/fixtures/7x15_rotated.jpg +0 -0
  615. data/vendor/assets/javascripts/wysihtml-toolbar.js +19288 -0
  616. metadata +266 -92
  617. data/app/assets/stylesheets/pageflow/editor/select_button.scss +0 -51
  618. data/config/initializers/revision_components.rb +0 -5
  619. data/config/locales/twitter_to_x.de.yml +0 -6
  620. data/config/locales/twitter_to_x.en.yml +0 -6
  621. data/entry_types/scrolled/app/helpers/pageflow_scrolled/generated_media_queries_helper.rb +0 -55
  622. data/entry_types/scrolled/config/locales/twitter_to_x.de.yml +0 -12
  623. data/entry_types/scrolled/config/locales/twitter_to_x.en.yml +0 -12
  624. data/entry_types/scrolled/config/locales/vr_image_projection.de.yml +0 -24
  625. data/entry_types/scrolled/config/locales/vr_image_projection.en.yml +0 -24
  626. data/entry_types/scrolled/package/frontend/EditableInlineText.module-ebd22921.js +0 -5497
  627. data/entry_types/scrolled/package/frontend/ToggleFullscreenCornerButton-b79159cc.js +0 -107
  628. data/entry_types/scrolled/package/frontend/Viewer-1ecf3375.js +0 -154
  629. data/entry_types/scrolled/package/frontend/Wavesurfer-7d9cf1b7.js +0 -333
  630. data/entry_types/scrolled/package/frontend/arrowRight-62998af9.js +0 -77
  631. data/entry_types/scrolled/package/frontend/components-024a9893.js +0 -2853
  632. data/entry_types/scrolled/package/frontend/createSuper-d0f30da3.js +0 -115
  633. data/entry_types/scrolled/package/frontend/i18n-ce13a8bf.js +0 -1129
  634. data/entry_types/scrolled/package/frontend/useContentElementEditorState-4f4c3cf6.js +0 -52
  635. data/lib/generators/pageflow/resque/templates/resque_enqueue_after_commit_patch.rb +0 -25
  636. data/package/vendor/jquery.minicolors.js +0 -1108
@@ -1,56 +1,351 @@
1
- import React, { useEffect } from 'react';
2
- import { render } from '@testing-library/react';
3
- import { renderHook } from '@testing-library/react-hooks/dom';
1
+ import { ConfigurationEditorTabView } from 'pageflow/ui';
2
+ import { FileInputView } from 'pageflow/editor';
3
+ import { render, act } from '@testing-library/react';
4
+ import { renderBackboneView, ConfigurationEditor } from 'pageflow/testHelpers';
5
+ export { renderBackboneView as renderReactBasedBackboneView } from 'pageflow/testHelpers';
6
+ import { EditContentElementView, editor } from 'pageflow-scrolled/editor';
7
+ import React, { useEffect, useState } from 'react';
8
+ import { DndProvider } from 'react-dnd';
9
+ import { HTML5Backend } from 'react-dnd-html5-backend';
10
+ import BackboneEvents from 'backbone-events-standalone';
11
+ import { PhonePlatformContext, RootProviders, useEntryStateDispatch, ContentElementLifecycleContext, ContentElementEditorCommandEmitterContext, ContentElementEditorStateContext, MainStorylineActivity, ContentElementAttributesProvider, ContentElementViewTimelineContext } from 'pageflow-scrolled/frontend';
4
12
  import { Consent } from 'pageflow/frontend';
5
- import { RootProviders, useEntryStateDispatch } from 'pageflow-scrolled/frontend';
13
+ import { renderHook } from '@testing-library/react-hooks/dom';
6
14
 
7
- function _defineProperty(obj, key, value) {
8
- if (key in obj) {
9
- Object.defineProperty(obj, key, {
10
- value: value,
11
- enumerable: true,
12
- configurable: true,
13
- writable: true
14
- });
15
+ function styleInject(css, ref) {
16
+ if ( ref === void 0 ) ref = {};
17
+ var insertAt = ref.insertAt;
18
+
19
+ if (!css || typeof document === 'undefined') { return; }
20
+
21
+ var head = document.head || document.getElementsByTagName('head')[0];
22
+ var style = document.createElement('style');
23
+ style.type = 'text/css';
24
+
25
+ if (insertAt === 'top') {
26
+ if (head.firstChild) {
27
+ head.insertBefore(style, head.firstChild);
28
+ } else {
29
+ head.appendChild(style);
30
+ }
15
31
  } else {
16
- obj[key] = value;
32
+ head.appendChild(style);
17
33
  }
18
34
 
19
- return obj;
35
+ if (style.styleSheet) {
36
+ style.styleSheet.cssText = css;
37
+ } else {
38
+ style.appendChild(document.createTextNode(css));
39
+ }
20
40
  }
21
41
 
22
- function ownKeys(object, enumerableOnly) {
23
- var keys = Object.keys(object);
42
+ var css = ".ContentElementBox-module_properties__1ljc9 {\n --content-element-box-box-shadow:\n var(--content-element-box-shadow, 0 0 #000),\n 0 0 0 1px var(--content-element-box-outline-color, transparent);\n}\n\n.ContentElementBox-module_wrapper__3wZgP {\n overflow: hidden;\n border-radius: var(--content-element-box-border-radius,\n var(--theme-content-element-box-border-radius));\n box-shadow: var(--content-element-box-box-shadow);\n}\n\n.ContentElementBox-module_full__AfWPr {\n border-radius: var(--content-element-box-border-radius,\n var(--theme-content-element-full-box-border-radius,\n var(--theme-content-element-box-border-radius)));\n}\n\n.ContentElementBox-module_positioned__3R1dq {\n position: relative;\n height: 100%;\n}\n";
43
+ var styles = {"properties":"ContentElementBox-module_properties__1ljc9","wrapper":"ContentElementBox-module_wrapper__3wZgP","full":"ContentElementBox-module_full__AfWPr","positioned":"ContentElementBox-module_positioned__3R1dq"};
44
+ styleInject(css);
24
45
 
25
- if (Object.getOwnPropertySymbols) {
26
- var symbols = Object.getOwnPropertySymbols(object);
27
- if (enumerableOnly) symbols = symbols.filter(function (sym) {
28
- return Object.getOwnPropertyDescriptor(object, sym).enumerable;
29
- });
30
- keys.push.apply(keys, symbols);
31
- }
46
+ function getElement(subject) {
47
+ var _subject$el;
48
+ return (_subject$el = subject === null || subject === void 0 ? void 0 : subject.el) !== null && _subject$el !== void 0 ? _subject$el : subject;
49
+ }
32
50
 
33
- return keys;
51
+ /**
52
+ * Assert that the subject contains a `ContentElementBox` and, optionally,
53
+ * that the box has specific theme styles applied. Registered via
54
+ * {@link useContentElementMatchers}.
55
+ *
56
+ * The subject is a DOM element, e.g. the `container` returned by
57
+ * {@link renderInContentElement}. Negate with `.not` to assert that no
58
+ * box is present.
59
+ *
60
+ * @param {Object} [options]
61
+ * @param {string} [options.boxShadow] - Expected box shadow theme scale, e.g. `'md'`.
62
+ * @param {string} [options.borderRadius] - Expected border radius theme scale, e.g. `'circle'`.
63
+ * @param {string} [options.outlineColor] - Expected outline color.
64
+ *
65
+ * @example
66
+ * expect(container).toContainContentElementBox();
67
+ * expect(container).toContainContentElementBox({borderRadius: 'circle', boxShadow: 'md'});
68
+ * expect(container).not.toContainContentElementBox();
69
+ */
70
+ function toContainContentElementBox(subject, options = {}) {
71
+ const wrapper = getElement(subject).querySelector(`.${styles.wrapper}`);
72
+ if (!wrapper) {
73
+ return {
74
+ pass: false,
75
+ message: () => 'expected element to contain a content element box, but found none'
76
+ };
77
+ }
78
+ const mismatches = boxPropertyMismatches(wrapper, options);
79
+ return {
80
+ pass: mismatches.length === 0,
81
+ message: () => mismatches.length ? `expected content element box to have ${mismatches.join(', ')}` : `expected element not to contain a content element box${describeOptions(options)}`
82
+ };
83
+ }
84
+ const boxProperties = {
85
+ boxShadow: {
86
+ customProperty: '--content-element-box-shadow',
87
+ expectedValue: value => `var(--theme-content-element-box-shadow-${value})`
88
+ },
89
+ borderRadius: {
90
+ customProperty: '--content-element-box-border-radius',
91
+ expectedValue: value => `var(--theme-content-element-box-border-radius-${value})`
92
+ },
93
+ outlineColor: {
94
+ customProperty: '--content-element-box-outline-color',
95
+ expectedValue: value => value
96
+ }
97
+ };
98
+ function boxPropertyMismatches(wrapper, options) {
99
+ return Object.keys(options).reduce((mismatches, name) => {
100
+ const property = boxProperties[name];
101
+ if (property) {
102
+ const expected = property.expectedValue(options[name]);
103
+ const actual = wrapper.style.getPropertyValue(property.customProperty);
104
+ if (actual !== expected) {
105
+ mismatches.push(`${name} ${JSON.stringify(options[name])} (found ${JSON.stringify(actual)})`);
106
+ }
107
+ }
108
+ return mismatches;
109
+ }, []);
110
+ }
111
+ function describeOptions(options) {
112
+ const names = Object.keys(options).filter(name => boxProperties[name]);
113
+ return names.length ? ` with ${names.join(', ')}` : '';
34
114
  }
35
115
 
36
- function _objectSpread2(target) {
37
- for (var i = 1; i < arguments.length; i++) {
38
- var source = arguments[i] != null ? arguments[i] : {};
116
+ var css$1 = ".FitViewport-module_container__-awVj {\n width: 100%;\n margin: 0 auto;\n max-width: var(\n --theme-fit-viewport,\n calc(100 * var(--vh) / var(--fit-viewport-aspect-ratio) * var(--fit-viewport-scale, 1))\n );\n}\n\n.FitViewport-module_content__1_K5a {\n position: relative;\n}\n\n.FitViewport-module_content__1_K5a > div:first-child {\n padding-top: calc(100% * var(--fit-viewport-aspect-ratio));\n}\n\n.FitViewport-module_inner__3psd1 {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n}\n\n@media print {\n .FitViewport-module_container__-awVj {\n page-break-inside: avoid;\n }\n}\n";
117
+ var styles$1 = {"container":"FitViewport-module_container__-awVj","content":"FitViewport-module_content__1_K5a","inner":"FitViewport-module_inner__3psd1"};
118
+ styleInject(css$1);
39
119
 
40
- if (i % 2) {
41
- ownKeys(Object(source), true).forEach(function (key) {
42
- _defineProperty(target, key, source[key]);
43
- });
44
- } else if (Object.getOwnPropertyDescriptors) {
45
- Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
46
- } else {
47
- ownKeys(Object(source)).forEach(function (key) {
48
- Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
49
- });
120
+ /**
121
+ * Assert that the subject contains a `FitViewport` and, optionally, that
122
+ * it has a specific aspect ratio. Registered via
123
+ * {@link useContentElementMatchers}.
124
+ *
125
+ * The subject is a DOM element, e.g. the `container` returned by
126
+ * {@link renderInContentElement}. Negate with `.not` to assert that no
127
+ * `FitViewport` is present.
128
+ *
129
+ * @param {Object} [options]
130
+ * @param {string} [options.aspectRatio] -
131
+ * Expected aspect ratio: a theme scale name (e.g. `'square'`) or a raw
132
+ * ratio value (e.g. `'0.75'`).
133
+ *
134
+ * @example
135
+ * expect(container).toContainFitViewport();
136
+ * expect(container).toContainFitViewport({aspectRatio: 'square'});
137
+ */
138
+ function toContainFitViewport(subject, {
139
+ aspectRatio
140
+ } = {}) {
141
+ const container = getElement(subject).querySelector(`.${styles$1.container}`);
142
+ if (!container) {
143
+ return {
144
+ pass: false,
145
+ message: () => 'expected element to contain a FitViewport, but found none'
146
+ };
147
+ }
148
+ if (aspectRatio !== undefined) {
149
+ const actual = getAspectRatio(container);
150
+ if (actual !== aspectRatio) {
151
+ return {
152
+ pass: false,
153
+ message: () => `expected FitViewport to have aspect ratio ${JSON.stringify(aspectRatio)} ` + `(found ${JSON.stringify(actual)})`
154
+ };
50
155
  }
51
156
  }
157
+ return {
158
+ pass: true,
159
+ message: () => 'expected element not to contain a FitViewport'
160
+ };
161
+ }
162
+ function getAspectRatio(container) {
163
+ const cssValue = container.style.getPropertyValue('--fit-viewport-aspect-ratio');
164
+ const match = cssValue.match(/var\(--theme-aspect-ratio-(.+)\)/);
165
+ return match ? match[1] : cssValue;
166
+ }
52
167
 
53
- return target;
168
+ /**
169
+ * Assert that all inputs rendered by a configuration editor are
170
+ * described by a configuration schema. Registered via
171
+ * {@link useConfigurationEditorMatchers}.
172
+ *
173
+ * The subject is a function that renders the configuration editor. Only
174
+ * inputs of the rendered form are seen. Views on nested routes render a
175
+ * form of their own and are checked against the part of the schema that
176
+ * describes the object they configure.
177
+ *
178
+ * @param {Object} schema - Configuration schema of the subject.
179
+ * @param {Object} [options]
180
+ * @param {Array<string>} [options.path] - Path of the configuration the
181
+ * rendered form writes, if it is not the root. `*` stands for array
182
+ * items and map values.
183
+ *
184
+ * @example
185
+ * expect(() => renderContentElementConfigurationEditor({entry, contentElement}))
186
+ * .toRenderInputsMatching(require('./schema.json'));
187
+ *
188
+ * @example
189
+ * expect(() => renderBackboneView(new SidebarEditAreaView(options)))
190
+ * .toRenderInputsMatching(require('../schema.json'), {path: ['areas', '*']});
191
+ */
192
+ function toRenderInputsMatching(render, schema, {
193
+ path = []
194
+ } = {}) {
195
+ const subschema = subschemaAt(schema, path);
196
+ if (!subschema) {
197
+ return {
198
+ pass: false,
199
+ message: () => `expected schema to describe configuration at ${path.join('/')}`
200
+ };
201
+ }
202
+ const inputs = recordInputs(render);
203
+ if (!inputs.length) {
204
+ return {
205
+ pass: false,
206
+ message: () => 'expected configuration editor to render inputs, but it rendered none'
207
+ };
208
+ }
209
+ const mismatches = inputs.flatMap(input => mismatchesOf(input, subschema));
210
+ return {
211
+ pass: mismatches.length === 0,
212
+ message: () => mismatches.length ? `expected rendered inputs to match schema:\n ${mismatches.join('\n ')}` : 'expected rendered inputs not to match schema'
213
+ };
214
+ }
215
+ function subschemaAt(schema, path) {
216
+ return path.reduce((current, segment) => dereference(child(current, segment), schema), dereference(schema, schema));
217
+ }
218
+ function child(schema, segment) {
219
+ var _schema$properties;
220
+ if (!schema) {
221
+ return undefined;
222
+ }
223
+ return segment === '*' ? schema.items || schema.additionalProperties : (_schema$properties = schema.properties) === null || _schema$properties === void 0 ? void 0 : _schema$properties[segment];
224
+ }
225
+ function dereference(schema, document) {
226
+ var _schema2;
227
+ const seen = [];
228
+ while (((_schema = schema) === null || _schema === void 0 ? void 0 : _schema.$ref) && !seen.includes(schema.$ref)) {
229
+ var _schema;
230
+ seen.push(schema.$ref);
231
+ schema = localTarget(document, schema.$ref);
232
+ }
233
+ return ((_schema2 = schema) === null || _schema2 === void 0 ? void 0 : _schema2.$ref) ? undefined : schema;
234
+ }
235
+ function localTarget(document, ref) {
236
+ const [otherDocumentId, pointer] = ref.split('#');
237
+ if (otherDocumentId) {
238
+ return undefined;
239
+ }
240
+ return pointer.split('/').filter(Boolean).reduce((value, segment) => value === null || value === void 0 ? void 0 : value[segment], document);
241
+ }
242
+ function recordInputs(render) {
243
+ const inputs = [];
244
+ const input = ConfigurationEditorTabView.prototype.input;
245
+ jest.spyOn(ConfigurationEditorTabView.prototype, 'input').mockImplementation(function (propertyName, view, options = {}) {
246
+ inputs.push({
247
+ propertyName,
248
+ view,
249
+ options
250
+ });
251
+ return input.call(this, propertyName, view, options);
252
+ });
253
+ try {
254
+ render();
255
+ } finally {
256
+ ConfigurationEditorTabView.prototype.input.mockRestore();
257
+ }
258
+ return inputs;
259
+ }
260
+ const checks = [fileCollectionMismatch];
261
+ function mismatchesOf({
262
+ propertyName,
263
+ view,
264
+ options
265
+ }, schema) {
266
+ const property = (schema.properties || {})[propertyName];
267
+ return checks.flatMap(check => check({
268
+ propertyName,
269
+ view,
270
+ options,
271
+ property
272
+ }) || []);
273
+ }
274
+ function fileCollectionMismatch({
275
+ propertyName,
276
+ view,
277
+ options,
278
+ property
279
+ }) {
280
+ if (!isFileInput(view) || !options.collection) {
281
+ return;
282
+ }
283
+ const declared = property && property['x-fileCollection'];
284
+ if (declared !== options.collection) {
285
+ return `${propertyName}: expected x-fileCollection ${JSON.stringify(options.collection)}, ` + `found ${JSON.stringify(declared)}`;
286
+ }
287
+ }
288
+ function isFileInput(view) {
289
+ return view === FileInputView || view.prototype instanceof FileInputView;
290
+ }
291
+
292
+ /**
293
+ * Register the public content element matchers for the surrounding
294
+ * `describe` block: {@link toContainContentElementBox} and
295
+ * {@link toContainFitViewport}.
296
+ *
297
+ * Call inside a `describe` block. Content element plugins can use this to
298
+ * assert that their component opts into the framework chrome correctly.
299
+ *
300
+ * @example
301
+ * import {renderInContentElement, useContentElementMatchers} from 'pageflow-scrolled/testHelpers';
302
+ *
303
+ * describe('MyContentElement', () => {
304
+ * useContentElementMatchers();
305
+ *
306
+ * it('renders inside a box', () => {
307
+ * const {container} = renderInContentElement(<MyContentElement />);
308
+ * expect(container).toContainContentElementBox();
309
+ * });
310
+ * });
311
+ */
312
+ function useContentElementMatchers() {
313
+ beforeEach(() => {
314
+ expect.extend({
315
+ toContainContentElementBox,
316
+ toContainFitViewport
317
+ });
318
+ });
319
+ }
320
+
321
+ /**
322
+ * Register the configuration editor matchers for the surrounding
323
+ * `describe` block: {@link toRenderInputsMatching}.
324
+ *
325
+ * Call inside a `describe` block. Content element plugins can use this to
326
+ * assert that their configuration schema keeps up with their editor
327
+ * integration.
328
+ *
329
+ * @example
330
+ * import {
331
+ * renderContentElementConfigurationEditor, useConfigurationEditorMatchers
332
+ * } from 'pageflow-scrolled/testHelpers';
333
+ *
334
+ * describe('myContentElement/editor', () => {
335
+ * useConfigurationEditorMatchers();
336
+ *
337
+ * it('renders inputs described by schema', () => {
338
+ * expect(() => renderContentElementConfigurationEditor({entry, contentElement}))
339
+ * .toRenderInputsMatching(schema);
340
+ * });
341
+ * });
342
+ */
343
+ function useConfigurationEditorMatchers() {
344
+ beforeEach(() => {
345
+ expect.extend({
346
+ toRenderInputsMatching
347
+ });
348
+ });
54
349
  }
55
350
 
56
351
  /**
@@ -65,81 +360,110 @@ function _objectSpread2(target) {
65
360
  * @param {Object} [options.legalInfo] - imprint, copyright and privacy information of entry.
66
361
  * @param {Object} [options.themeOptions] - Options set via theme registration.
67
362
  * @param {Object} [options.themeAssets] - Paths to theme assets.
363
+ * @param {Object} [options.themeTranslations] - Pre-merged theme translations (e.g., scales).
68
364
  * @param {Object} [options.additionalSeedData] - Seed data by name.
69
365
  * @param {Array} [options.consentVendors] - Server rendered consent vendor data.
70
366
  * @param {Object} [options.contentElementConsentVendors] - Consent vendor name by content element id.
71
367
  * @param {Object} [options.entry] - attributes of entry.
368
+ * @param {Object} [options.fileModelTypes] - Mapping of file collection names to model types.
369
+ * Only needed to associate nested files with their parent. Collections of file types
370
+ * registered by plugins get a placeholder model type.
371
+ * @param {Object} [options.fileUrlTemplates] - Mapping of file collection names to mappings of
372
+ * url template names to url templates.
72
373
  * @param {Array} [options.imageFiles] - Array of objects with image file attributes of entry.
73
374
  * @param {Array} [options.videoFiles] - Array of objects with video file attributes of entry.
74
375
  * @param {Array} [options.audioFiles] - Array of objects with audio file attributes of entry.
75
376
  * @param {Array} [options.textTrackFiles] - Array of objects with text track file attributes of entry.
377
+ * @param {Array} [options.storylines] - Array of objects with storyline attributes of entry.
76
378
  * @param {Array} [options.chapters] - Array of objects with chapter attributes of entry.
77
379
  * @param {Array} [options.sections] - Array of objects with section attributes of entry.
78
380
  * @param {Array} [options.contentElements] - Array of objects with content element attributes of entry.
79
381
  * @param {Array} [options.widgets] - Array of objects with widget attributes of entry.
80
382
  * @returns {Object} - Data that resembles seed generated by server side rendering.
81
383
  */
82
- function normalizeSeed() {
83
- var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
84
- imageFileUrlTemplates = _ref.imageFileUrlTemplates,
85
- fileUrlTemplates = _ref.fileUrlTemplates,
86
- fileModelTypes = _ref.fileModelTypes,
87
- prettyUrl = _ref.prettyUrl,
88
- shareUrlTemplates = _ref.shareUrlTemplates,
89
- defaultFileRights = _ref.defaultFileRights,
90
- legalInfo = _ref.legalInfo,
91
- themeOptions = _ref.themeOptions,
92
- themeAssets = _ref.themeAssets,
93
- entry = _ref.entry,
94
- imageFiles = _ref.imageFiles,
95
- videoFiles = _ref.videoFiles,
96
- audioFiles = _ref.audioFiles,
97
- textTrackFiles = _ref.textTrackFiles,
98
- chapters = _ref.chapters,
99
- sections = _ref.sections,
100
- contentElements = _ref.contentElements,
101
- widgets = _ref.widgets,
102
- additionalSeedData = _ref.additionalSeedData,
103
- consentVendors = _ref.consentVendors,
104
- contentElementConsentVendors = _ref.contentElementConsentVendors,
105
- fileLicenses = _ref.fileLicenses;
106
-
107
- var entries = entry ? [entry] : [{}];
108
- var normalizedEntries = normalizeCollection(entries);
109
- var normalizedContentElements = normalizeCollection(contentElements, {
384
+ function normalizeSeed({
385
+ imageFileUrlTemplates,
386
+ fileUrlTemplates,
387
+ fileModelTypes,
388
+ prettyUrl,
389
+ shareUrlTemplates,
390
+ defaultFileRights,
391
+ legalInfo,
392
+ themeOptions,
393
+ themeAssets,
394
+ themeTranslations,
395
+ entry,
396
+ imageFiles,
397
+ videoFiles,
398
+ audioFiles,
399
+ textTrackFiles,
400
+ storylines,
401
+ chapters,
402
+ sections,
403
+ contentElements,
404
+ widgets,
405
+ additionalSeedData,
406
+ consentVendors,
407
+ contentElementConsentVendors,
408
+ cutOff,
409
+ embed,
410
+ originUrl,
411
+ fileLicenses,
412
+ entryTranslations,
413
+ fileReferenceLocations,
414
+ ...customFiles
415
+ } = {}) {
416
+ const customFileCollectionNames = [...new Set([...Object.keys(customFiles).filter(name => name.endsWith('Files')), ...Object.keys(fileModelTypes || {})])].filter(name => !builtInFileCollectionNames.includes(name));
417
+ const entries = entry ? [entry] : [{}];
418
+ const normalizedEntries = normalizeCollection(entries, {
419
+ locale: 'en',
420
+ configuration: {}
421
+ });
422
+ const normalizedContentElements = normalizeCollection(contentElements, {
110
423
  typeName: 'textBlock',
111
424
  configuration: {}
112
425
  });
113
- var normalizedSections = normalizeSections(sections, normalizedContentElements);
114
- var normalizedChapters = normalizeChapters(chapters, normalizedSections);
426
+ const normalizedSections = normalizeSections(sections, normalizedContentElements);
427
+ const normalizedChapters = normalizeChapters(chapters, normalizedSections);
428
+ const normalizedStorylines = normalizeStorylines(storylines, normalizedChapters);
115
429
  return {
116
430
  config: {
117
- fileUrlTemplates: _objectSpread2({
118
- imageFiles: _objectSpread2({}, imageFileUrlTemplates),
431
+ fileUrlTemplates: {
432
+ imageFiles: {
433
+ ...imageFileUrlTemplates
434
+ },
119
435
  videoFiles: {},
120
436
  audioFiles: {},
121
- textTrackFiles: {}
122
- }, fileUrlTemplates),
123
- fileModelTypes: _objectSpread2({
437
+ textTrackFiles: {},
438
+ ...emptyUrlTemplates(customFileCollectionNames),
439
+ ...fileUrlTemplates
440
+ },
441
+ fileModelTypes: {
124
442
  audioFiles: 'Pageflow::AudioFile',
125
443
  imageFiles: 'Pageflow::ImageFile',
126
444
  textTrackFiles: 'Pageflow::TextTrackFile',
127
- videoFiles: 'Pageflow::VideoFile'
128
- }, fileModelTypes),
445
+ videoFiles: 'Pageflow::VideoFile',
446
+ ...placeholderModelTypes(customFileCollectionNames),
447
+ ...fileModelTypes
448
+ },
129
449
  prettyUrl: prettyUrl,
130
450
  shareUrlTemplates: normalizeShareUrlTemplates(shareUrlTemplates),
131
451
  defaultFileRights: defaultFileRights,
132
452
  legalInfo: normalizeLegalInfo(legalInfo),
133
453
  theme: normalizeTheme({
134
- themeOptions: themeOptions,
135
- themeAssets: themeAssets
454
+ themeOptions,
455
+ themeAssets,
456
+ themeTranslations
136
457
  }),
137
- additionalSeedData: additionalSeedData || {
138
- frontendVersion: 1
139
- },
458
+ additionalSeedData: additionalSeedData || {},
140
459
  consentVendors: consentVendors || [],
141
460
  contentElementConsentVendors: contentElementConsentVendors || {},
142
- fileLicenses: fileLicenses || {}
461
+ cutOff,
462
+ embed,
463
+ originUrl,
464
+ fileLicenses: fileLicenses || {},
465
+ entryTranslations: entryTranslations || [],
466
+ fileReferenceLocations: normalizeFileReferenceLocations(fileReferenceLocations)
143
467
  },
144
468
  collections: {
145
469
  entries: normalizedEntries,
@@ -166,6 +490,8 @@ function normalizeSeed() {
166
490
  parentFileType: null,
167
491
  configuration: {}
168
492
  }),
493
+ ...customFileCollections(customFileCollectionNames, customFiles),
494
+ storylines: normalizedStorylines,
169
495
  chapters: normalizedChapters,
170
496
  sections: normalizedSections,
171
497
  contentElements: normalizedContentElements,
@@ -173,11 +499,25 @@ function normalizeSeed() {
173
499
  }
174
500
  };
175
501
  }
502
+ const builtInFileCollectionNames = ['imageFiles', 'videoFiles', 'audioFiles', 'textTrackFiles'];
176
503
 
177
- function normalizeSections() {
178
- var sections = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
179
- var contentElements = arguments.length > 1 ? arguments[1] : undefined;
180
- var sectionDefaults = {
504
+ // Model types are only compared to associate nested files with their
505
+ // parent. Specs that seed nested files need to pass the real model type
506
+ // via the fileModelTypes option.
507
+ function placeholderModelTypes(collectionNames) {
508
+ return Object.fromEntries(collectionNames.map(name => [name, name.replace(/Files$/, 'File').replace(/^./, letter => letter.toUpperCase())]));
509
+ }
510
+ function emptyUrlTemplates(collectionNames) {
511
+ return Object.fromEntries(collectionNames.map(name => [name, {}]));
512
+ }
513
+ function customFileCollections(collectionNames, customFiles) {
514
+ return Object.fromEntries(collectionNames.map(name => [name, normalizeCollection(customFiles[name], {
515
+ isReady: true,
516
+ configuration: {}
517
+ })]));
518
+ }
519
+ function normalizeSections(sections = [], contentElements) {
520
+ const sectionDefaults = {
181
521
  configuration: {
182
522
  transition: 'scroll',
183
523
  backdrop: {
@@ -185,62 +525,66 @@ function normalizeSections() {
185
525
  }
186
526
  }
187
527
  };
188
-
189
528
  if (contentElements.length && !sections.length) {
190
- contentElements.forEach(function (contentElement) {
191
- return contentElement.sectionId = 10;
192
- });
193
- return [_objectSpread2({
529
+ contentElements.forEach(contentElement => contentElement.sectionId = 10);
530
+ return [{
194
531
  id: 10,
195
- permaId: 1
196
- }, sectionDefaults)];
532
+ permaId: 1,
533
+ ...sectionDefaults
534
+ }];
197
535
  }
198
-
199
- return normalizeCollection(sections, sectionDefaults).map(function (section) {
200
- return _objectSpread2(_objectSpread2({}, section), {}, {
201
- configuration: _objectSpread2({
202
- transition: sectionDefaults.configuration.transition
203
- }, section.configuration)
204
- });
205
- });
536
+ return normalizeCollection(sections, sectionDefaults).map(section => ({
537
+ ...section,
538
+ configuration: {
539
+ transition: sectionDefaults.configuration.transition,
540
+ ...section.configuration
541
+ }
542
+ }));
206
543
  }
207
-
208
- function normalizeChapters() {
209
- var chapters = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
210
- var sections = arguments.length > 1 ? arguments[1] : undefined;
211
- var chapterDefaults = {
544
+ function normalizeChapters(chapters = [], sections) {
545
+ const chapterDefaults = {
212
546
  configuration: {}
213
547
  };
214
-
215
548
  if (sections.length && !chapters.length) {
216
- sections.forEach(function (section) {
217
- return section.chapterId = 100;
218
- });
219
- return [_objectSpread2({
549
+ sections.forEach(section => section.chapterId = 100);
550
+ return [{
220
551
  id: 100,
221
- permaId: 10
222
- }, chapterDefaults)];
552
+ permaId: 10,
553
+ ...chapterDefaults
554
+ }];
223
555
  }
224
-
225
556
  return normalizeCollection(chapters, chapterDefaults);
226
557
  }
227
-
558
+ function normalizeStorylines(storylines = [], chapters) {
559
+ if (!storylines.length) {
560
+ chapters.forEach(chapter => chapter.storylineId = 1000);
561
+ return [{
562
+ id: 1000,
563
+ permaId: 100,
564
+ configuration: {
565
+ main: true
566
+ }
567
+ }];
568
+ }
569
+ return normalizeCollection(storylines, {
570
+ configuration: {}
571
+ });
572
+ }
228
573
  function normalizeShareUrlTemplates(shareUrlTemplates) {
229
574
  if (shareUrlTemplates) {
230
575
  return shareUrlTemplates;
231
576
  } else {
232
577
  return {
233
- email: 'mailto:?body=%{url}',
234
- facebook: 'http://www.facebook.com/sharer/sharer.php?u=%{url}',
235
- google: 'https://plus.google.com/share?url=%{url}',
236
- linked_in: 'https://www.linkedin.com/shareArticle?mini=true&url=%{url}',
237
- telegram: 'tg://msg?text=%{url}',
238
- twitter: 'https://x.com/intent/post?url=%{url}',
239
- whats_app: 'WhatsApp://send?text=%{url}'
578
+ email: 'mailto:?body=%<url>s',
579
+ facebook: 'http://www.facebook.com/sharer/sharer.php?u=%<url>s',
580
+ google: 'https://plus.google.com/share?url=%<url>s',
581
+ linked_in: 'https://www.linkedin.com/shareArticle?mini=true&url=%<url>s',
582
+ telegram: 'tg://msg?text=%<url>s',
583
+ twitter: 'https://x.com/intent/post?url=%<url>s',
584
+ whats_app: 'WhatsApp://send?text=%<url>s'
240
585
  };
241
586
  }
242
587
  }
243
-
244
588
  function normalizeLegalInfo(legalInfo) {
245
589
  if (legalInfo) {
246
590
  return legalInfo;
@@ -261,75 +605,76 @@ function normalizeLegalInfo(legalInfo) {
261
605
  };
262
606
  }
263
607
  }
264
-
265
- function normalizeTheme(_ref2) {
266
- var themeAssets = _ref2.themeAssets,
267
- themeOptions = _ref2.themeOptions;
608
+ function normalizeTheme({
609
+ themeAssets,
610
+ themeOptions,
611
+ themeTranslations
612
+ }) {
268
613
  return {
269
- assets: _objectSpread2({
270
- icons: {}
271
- }, themeAssets),
272
- options: _objectSpread2({
273
- colors: {}
274
- }, themeOptions)
614
+ assets: {
615
+ icons: {},
616
+ ...themeAssets
617
+ },
618
+ options: {
619
+ colors: {},
620
+ ...themeOptions
621
+ },
622
+ translations: themeTranslations
275
623
  };
276
624
  }
277
-
278
- function normalizeWidgets() {
279
- var widgets = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
280
- return widgets.map(function (widget) {
281
- return _objectSpread2({
282
- permaId: widget.role,
283
- configuration: {}
284
- }, widget);
285
- });
625
+ function normalizeWidgets(widgets = []) {
626
+ return widgets.map(widget => ({
627
+ permaId: widget.role,
628
+ configuration: {},
629
+ ...widget
630
+ }));
286
631
  }
287
-
288
- function normalizeCollection() {
289
- var collection = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
290
- var defaults = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
291
- return collection.map(function (item, index) {
292
- return _objectSpread2(_objectSpread2({
293
- id: index + 1,
294
- permaId: index + 1
295
- }, defaults), item);
296
- });
632
+ function normalizeCollection(collection = [], defaults = {}) {
633
+ return collection.map((item, index) => ({
634
+ id: index + 1,
635
+ permaId: index + 1,
636
+ ...defaults,
637
+ ...item
638
+ }));
297
639
  }
298
-
299
- function _objectWithoutPropertiesLoose(source, excluded) {
300
- if (source == null) return {};
301
- var target = {};
302
- var sourceKeys = Object.keys(source);
303
- var key, i;
304
-
305
- for (i = 0; i < sourceKeys.length; i++) {
306
- key = sourceKeys[i];
307
- if (excluded.indexOf(key) >= 0) continue;
308
- target[key] = source[key];
309
- }
310
-
311
- return target;
640
+ function normalizeFileReferenceLocations(fileReferenceLocations) {
641
+ return {
642
+ contentElements: {},
643
+ sections: [],
644
+ entry: [],
645
+ ...fileReferenceLocations
646
+ };
312
647
  }
313
648
 
314
- function _objectWithoutProperties(source, excluded) {
315
- if (source == null) return {};
316
- var target = _objectWithoutPropertiesLoose(source, excluded);
317
- var key, i;
318
-
319
- if (Object.getOwnPropertySymbols) {
320
- var sourceSymbolKeys = Object.getOwnPropertySymbols(source);
321
-
322
- for (i = 0; i < sourceSymbolKeys.length; i++) {
323
- key = sourceSymbolKeys[i];
324
- if (excluded.indexOf(key) >= 0) continue;
325
- if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue;
326
- target[key] = source[key];
327
- }
328
- }
329
-
330
- return target;
649
+ /**
650
+ * Render the configuration editor of a content element. Content element
651
+ * packs can use this to assert how their editor integration behaves.
652
+ *
653
+ * @param {Object} options
654
+ * @param {Object} options.entry - Entry the content element belongs to.
655
+ * @param {Object} options.contentElement - Content element to configure.
656
+ *
657
+ * @example
658
+ * import {renderContentElementConfigurationEditor} from 'pageflow-scrolled/testHelpers';
659
+ */
660
+ function renderContentElementConfigurationEditor({
661
+ entry,
662
+ contentElement
663
+ }) {
664
+ // Normally contributed by the text inline file rights widget type
665
+ // once the widgets of the entry have been set up.
666
+ ConfigurationEditorTabView.groups.define('ContentElementInlineFileRightsSettings', () => {});
667
+ const view = new EditContentElementView({
668
+ model: contentElement,
669
+ editor,
670
+ entry
671
+ });
672
+ renderBackboneView(view);
673
+ return ConfigurationEditor.find(view);
331
674
  }
332
675
 
676
+ var _jsxFileName = "/home/tfischbach/code/pageflow/entry_types/scrolled/package/src/testHelpers/rendering.js";
677
+
333
678
  /**
334
679
  * Render a component that depends on entry state. Accepts all options
335
680
  * supported by [`render` of
@@ -366,20 +711,18 @@ function _objectWithoutProperties(source, excluded) {
366
711
  * function. The normalized seed constructed from the `seed` option
367
712
  * is passed as a second parameter.
368
713
  */
369
-
370
- function renderInEntry(ui) {
371
- var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
372
- seed = _ref.seed,
373
- setup = _ref.setup,
374
- wrapper = _ref.wrapper,
375
- _ref$consent = _ref.consent,
376
- consent = _ref$consent === void 0 ? Consent.create() : _ref$consent,
377
- options = _objectWithoutProperties(_ref, ["seed", "setup", "wrapper", "consent"]);
378
-
379
- options = _objectSpread2({
380
- wrapper: createWrapper(seed, setup, wrapper, consent)
381
- }, options);
382
-
714
+ function renderInEntry(ui, {
715
+ seed,
716
+ setup,
717
+ wrapper,
718
+ consent = Consent.create(),
719
+ phonePlatform,
720
+ ...options
721
+ } = {}) {
722
+ options = {
723
+ wrapper: createWrapper(seed, setup, wrapper, consent, phonePlatform),
724
+ ...options
725
+ };
383
726
  if (typeof ui === 'function') {
384
727
  // Evaluate `ui` inside a React component to allow using hooks in
385
728
  // the test. We also could have used `ui` as a React component
@@ -394,25 +737,39 @@ function renderInEntry(ui) {
394
737
  // render call and mount a new one. We therefore define a single
395
738
  // component that is reused across rerenders to ensure
396
739
  // `MyComponent` stays mounted and just receives new props.
397
- var HooksWrapper = function HooksWrapper(_ref2) {
398
- var ui = _ref2.ui;
740
+ function HooksWrapper({
741
+ ui
742
+ }) {
399
743
  return ui();
400
- };
401
-
402
- var result = render( /*#__PURE__*/React.createElement(HooksWrapper, {
403
- ui: ui
744
+ }
745
+ const result = render( /*#__PURE__*/React.createElement(HooksWrapper, {
746
+ ui: ui,
747
+ __self: this,
748
+ __source: {
749
+ fileName: _jsxFileName,
750
+ lineNumber: 71,
751
+ columnNumber: 27
752
+ }
404
753
  }), options);
405
- return _objectSpread2(_objectSpread2({}, result), {}, {
406
- rerender: function rerender(ui) {
754
+ return {
755
+ ...result,
756
+ rerender(ui) {
407
757
  result.rerender( /*#__PURE__*/React.createElement(HooksWrapper, {
408
- ui: ui
758
+ ui: ui,
759
+ __self: this,
760
+ __source: {
761
+ fileName: _jsxFileName,
762
+ lineNumber: 76,
763
+ columnNumber: 25
764
+ }
409
765
  }));
410
766
  }
411
- });
767
+ };
412
768
  } else {
413
769
  return render(ui, options);
414
770
  }
415
771
  }
772
+
416
773
  /**
417
774
  * Render a hook that depends on entry state. Accepts all options
418
775
  * supported by [`renderHook` of
@@ -426,45 +783,75 @@ function renderInEntry(ui) {
426
783
  * @param {Object} [options.seed] - Seed data for entry state. Passed through {@link normalizeSeed}.
427
784
  * @param {Function} [options.setup] - See {@link renderInEntry}.
428
785
  */
429
-
430
- function renderHookInEntry(callback) {
431
- var _ref3 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
432
- seed = _ref3.seed,
433
- setup = _ref3.setup,
434
- wrapper = _ref3.wrapper,
435
- options = _objectWithoutProperties(_ref3, ["seed", "setup", "wrapper"]);
436
-
437
- return renderHook(callback, _objectSpread2({
438
- wrapper: createWrapper(seed, setup, wrapper)
439
- }, options));
786
+ function renderHookInEntry(callback, {
787
+ seed,
788
+ setup,
789
+ wrapper,
790
+ phonePlatform,
791
+ ...options
792
+ } = {}) {
793
+ return renderHook(callback, {
794
+ wrapper: createWrapper(seed, setup, wrapper, undefined, phonePlatform),
795
+ ...options
796
+ });
440
797
  }
441
-
442
- function createWrapper(seed, setup, originalWrapper, consent) {
443
- var normalizedSeed = normalizeSeed(seed);
444
-
445
- var OriginalWrapper = originalWrapper || function Noop(_ref4) {
446
- var children = _ref4.children;
798
+ function createWrapper(seed, setup, originalWrapper, consent, phonePlatform) {
799
+ const normalizedSeed = normalizeSeed(seed);
800
+ const OriginalWrapper = originalWrapper || function Noop({
801
+ children
802
+ }) {
447
803
  return children;
448
804
  };
449
-
450
- return function Wrapper(_ref5) {
451
- var children = _ref5.children;
805
+ return function Wrapper({
806
+ children
807
+ }) {
808
+ let content = /*#__PURE__*/React.createElement(Dispatcher, {
809
+ callback: setup,
810
+ seed: normalizedSeed,
811
+ __self: this,
812
+ __source: {
813
+ fileName: _jsxFileName,
814
+ lineNumber: 112,
815
+ columnNumber: 7
816
+ }
817
+ }, /*#__PURE__*/React.createElement(OriginalWrapper, {
818
+ __self: this,
819
+ __source: {
820
+ fileName: _jsxFileName,
821
+ lineNumber: 113,
822
+ columnNumber: 9
823
+ }
824
+ }, children));
825
+ if (phonePlatform !== undefined) {
826
+ content = /*#__PURE__*/React.createElement(PhonePlatformContext.Provider, {
827
+ value: phonePlatform,
828
+ __self: this,
829
+ __source: {
830
+ fileName: _jsxFileName,
831
+ lineNumber: 121,
832
+ columnNumber: 9
833
+ }
834
+ }, content);
835
+ }
452
836
  return /*#__PURE__*/React.createElement(RootProviders, {
453
837
  seed: normalizedSeed,
454
- consent: consent
455
- }, /*#__PURE__*/React.createElement(Dispatcher, {
456
- callback: setup,
457
- seed: normalizedSeed
458
- }, /*#__PURE__*/React.createElement(OriginalWrapper, null, children)));
838
+ consent: consent,
839
+ __self: this,
840
+ __source: {
841
+ fileName: _jsxFileName,
842
+ lineNumber: 128,
843
+ columnNumber: 7
844
+ }
845
+ }, content);
459
846
  };
460
847
  }
461
-
462
- function Dispatcher(_ref6) {
463
- var children = _ref6.children,
464
- seed = _ref6.seed,
465
- callback = _ref6.callback;
466
- var dispatch = useEntryStateDispatch();
467
- useEffect(function () {
848
+ function Dispatcher({
849
+ children,
850
+ seed,
851
+ callback
852
+ }) {
853
+ const dispatch = useEntryStateDispatch();
854
+ useEffect(() => {
468
855
  if (callback) {
469
856
  callback(dispatch, seed);
470
857
  }
@@ -472,4 +859,330 @@ function Dispatcher(_ref6) {
472
859
  return children;
473
860
  }
474
861
 
475
- export { normalizeSeed, renderHookInEntry, renderInEntry };
862
+ var _jsxFileName$1 = "/home/tfischbach/code/pageflow/entry_types/scrolled/package/src/testHelpers/scrollPositionLifecycle.js";
863
+
864
+ /**
865
+ * Takes the same options as {@link renderInEntry} but returns
866
+ * additional helper function to the return value of the
867
+ * {@link `useContentElementLifecycle`} hook:
868
+ *
869
+ * const {simulateScrollPosition} = renderInEntry(...)
870
+ * simulateScrollPosition('near viewport')
871
+ * // => Turns `shouldLoad` and `shouldPrepare` to true
872
+ */
873
+ function renderInEntryWithContentElementLifecycle(ui, options) {
874
+ return renderInEntryWithScrollPositionLifecycle(ui, {
875
+ lifecycleContext: ContentElementLifecycleContext,
876
+ ...options
877
+ });
878
+ }
879
+ function renderInEntryWithScrollPositionLifecycle(ui, {
880
+ lifecycleContext,
881
+ wrapper,
882
+ ...options
883
+ } = {}) {
884
+ const emitter = createEmitter();
885
+ return withSimulateScrollPositionHelper(emitter, renderInEntry(ui, {
886
+ wrapper: createScrollPositionProvider(lifecycleContext, emitter, wrapper),
887
+ ...options
888
+ }));
889
+ }
890
+ function createScrollPositionProvider(Context, emitter, originalWrapper) {
891
+ const OriginalWrapper = originalWrapper || function Noop({
892
+ children
893
+ }) {
894
+ return children;
895
+ };
896
+ return function ScrollPositionProvider({
897
+ children
898
+ }) {
899
+ const [value, setValue] = useState({
900
+ shouldLoad: false,
901
+ shouldPrepare: false,
902
+ isVisible: false,
903
+ isActive: false
904
+ });
905
+ useEffect(() => {
906
+ function handle(scrollPosition) {
907
+ switch (scrollPosition) {
908
+ case 'near viewport':
909
+ setValue({
910
+ shouldLoad: true,
911
+ shouldPrepare: true,
912
+ isVisible: false,
913
+ isActive: false
914
+ });
915
+ break;
916
+ case 'in viewport':
917
+ setValue({
918
+ shouldLoad: true,
919
+ shouldPrepare: true,
920
+ isVisible: true,
921
+ isActive: false
922
+ });
923
+ break;
924
+ case 'center of viewport':
925
+ setValue({
926
+ shouldLoad: true,
927
+ shouldPrepare: true,
928
+ isVisible: true,
929
+ isActive: true
930
+ });
931
+ break;
932
+ default:
933
+ setValue({
934
+ isVisible: false,
935
+ isActive: false
936
+ });
937
+ break;
938
+ }
939
+ }
940
+ emitter.on('scroll', handle);
941
+ return () => emitter.off('scroll', handle);
942
+ });
943
+ return /*#__PURE__*/React.createElement(OriginalWrapper, {
944
+ __self: this,
945
+ __source: {
946
+ fileName: _jsxFileName$1,
947
+ lineNumber: 69,
948
+ columnNumber: 7
949
+ }
950
+ }, /*#__PURE__*/React.createElement(Context.Provider, {
951
+ value: value,
952
+ __self: this,
953
+ __source: {
954
+ fileName: _jsxFileName$1,
955
+ lineNumber: 70,
956
+ columnNumber: 9
957
+ }
958
+ }, children));
959
+ };
960
+ }
961
+ const allowedScrollPositions = ['outside viewport', 'near viewport', 'in viewport', 'center of viewport'];
962
+ function withSimulateScrollPositionHelper(emitter, result) {
963
+ return {
964
+ ...result,
965
+ simulateScrollPosition(scrollPosition) {
966
+ if (!allowedScrollPositions.includes(scrollPosition)) {
967
+ throw new Error(`Invalid scrollPosition '${scrollPosition}'. ` + `Allowed values: ${allowedScrollPositions.join(', ')}`);
968
+ }
969
+ act(() => {
970
+ emitter.trigger('scroll', scrollPosition);
971
+ });
972
+ }
973
+ };
974
+ }
975
+ function createEmitter() {
976
+ return {
977
+ ...BackboneEvents
978
+ };
979
+ }
980
+
981
+ var _jsxFileName$2 = "/home/tfischbach/code/pageflow/entry_types/scrolled/package/src/testHelpers/renderInContentElement.js";
982
+
983
+ /**
984
+ * Provide context as if component was rendered inside of a content element.
985
+ *
986
+ * Returns additional functions to control content element scroll
987
+ * lifecycle, view timeline progress, editor commands, and storyline
988
+ * mode: `simulateScrollPosition`, `simulateScrollProgress`,
989
+ * `triggerEditorCommand`, and `simulateStorylineMode`.
990
+ *
991
+ * `simulateScrollProgress` passes the given progress to all
992
+ * `useContentElementViewTimelineProgress` callbacks, no matter which
993
+ * range they observe. Pass a `range` option to only invoke callbacks
994
+ * observing that range.
995
+ *
996
+ * @param {Function} callback - React component or function returning a React component.
997
+ * @param {Object} [options] - Supports all options supported by {@link `renderInEntry`}.
998
+ * @param {boolean|Object} [options.inlineEditing] -
999
+ * Opt the content element into an inline-editing context (matching
1000
+ * what's available when inline editing is loaded in production).
1001
+ * Pass `true` for editable defaults, or an object to override:
1002
+ * `{isEditable, isSelected, transientState}`. `transientState` is
1003
+ * installed as the `setTransientState` function on the editor state
1004
+ * context — pass a jest spy to assert on it. Omitting the option
1005
+ * gives the frontend-mode defaults (`isEditable: false, isSelected:
1006
+ * false, setTransientState: noop`) without wrapping any
1007
+ * inline-editing-only providers (DndProvider, editor command
1008
+ * emitter).
1009
+ * @param {Object} [options.phonePlatform] - Fake result of `usePhonePlatform`.
1010
+ * @param {Object} [options.consentState] - Pass 'undecided' to render third party consent opt in.
1011
+ *
1012
+ * @example
1013
+ *
1014
+ * const {getByRole, simulateScrollPosition, triggerEditorCommand, simulateStorylineMode} =
1015
+ * renderInContentElement(<MyContentElement />, {
1016
+ * seed: {...},
1017
+ * inlineEditing: {isSelected: true}
1018
+ * });
1019
+ * simulateScrollPosition('near viewport');
1020
+ * simulateScrollProgress(0.5);
1021
+ * simulateScrollProgress(0.5, {range: 'pinned'});
1022
+ * triggerEditorCommand({type: 'HIGHLIGHT'});
1023
+ * simulateStorylineMode('background');
1024
+ */
1025
+ function renderInContentElement(ui, {
1026
+ inlineEditing,
1027
+ phonePlatform = false,
1028
+ wrapper: OriginalWrapper,
1029
+ consentState = 'accepted',
1030
+ seed,
1031
+ ...options
1032
+ } = {}) {
1033
+ const emitter = Object.assign({}, BackboneEvents);
1034
+ const storylineEmitter = Object.assign({}, BackboneEvents);
1035
+ const viewTimelineEmitter = Object.assign({}, BackboneEvents);
1036
+ const viewTimeline = {
1037
+ subscribe(range, callback) {
1038
+ function handleProgress(progress, options) {
1039
+ if (!options.range || options.range === range) {
1040
+ callback(progress);
1041
+ }
1042
+ }
1043
+ viewTimelineEmitter.on('progress', handleProgress);
1044
+ return () => viewTimelineEmitter.off('progress', handleProgress);
1045
+ }
1046
+ };
1047
+ const inlineEditingConfig = resolveInlineEditing(inlineEditing);
1048
+ function Wrapper({
1049
+ children
1050
+ }) {
1051
+ const [storylineMode, setStorylineMode] = useState('active');
1052
+ useEffect(() => {
1053
+ storylineEmitter.on('storylineMode', setStorylineMode);
1054
+ return () => storylineEmitter.off('storylineMode', setStorylineMode);
1055
+ }, []);
1056
+ let tree = OriginalWrapper ? /*#__PURE__*/React.createElement(OriginalWrapper, {
1057
+ children: children,
1058
+ __self: this,
1059
+ __source: {
1060
+ fileName: _jsxFileName$2,
1061
+ lineNumber: 95,
1062
+ columnNumber: 34
1063
+ }
1064
+ }) : children;
1065
+ if (inlineEditingConfig) {
1066
+ tree = /*#__PURE__*/React.createElement(DndProvider, {
1067
+ backend: HTML5Backend,
1068
+ __self: this,
1069
+ __source: {
1070
+ fileName: _jsxFileName$2,
1071
+ lineNumber: 99,
1072
+ columnNumber: 9
1073
+ }
1074
+ }, /*#__PURE__*/React.createElement(ContentElementEditorCommandEmitterContext.Provider, {
1075
+ value: emitter,
1076
+ __self: this,
1077
+ __source: {
1078
+ fileName: _jsxFileName$2,
1079
+ lineNumber: 100,
1080
+ columnNumber: 11
1081
+ }
1082
+ }, /*#__PURE__*/React.createElement(ContentElementEditorStateContext.Provider, {
1083
+ value: inlineEditingConfig,
1084
+ __self: this,
1085
+ __source: {
1086
+ fileName: _jsxFileName$2,
1087
+ lineNumber: 101,
1088
+ columnNumber: 13
1089
+ }
1090
+ }, tree)));
1091
+ }
1092
+ return /*#__PURE__*/React.createElement(MainStorylineActivity, {
1093
+ activeExcursion: storylineMode !== 'active' ? {
1094
+ id: 1
1095
+ } : null,
1096
+ __self: this,
1097
+ __source: {
1098
+ fileName: _jsxFileName$2,
1099
+ lineNumber: 110,
1100
+ columnNumber: 7
1101
+ }
1102
+ }, /*#__PURE__*/React.createElement(ContentElementAttributesProvider, {
1103
+ id: 42,
1104
+ __self: this,
1105
+ __source: {
1106
+ fileName: _jsxFileName$2,
1107
+ lineNumber: 111,
1108
+ columnNumber: 9
1109
+ }
1110
+ }, /*#__PURE__*/React.createElement(ContentElementViewTimelineContext.Provider, {
1111
+ value: viewTimeline,
1112
+ __self: this,
1113
+ __source: {
1114
+ fileName: _jsxFileName$2,
1115
+ lineNumber: 112,
1116
+ columnNumber: 11
1117
+ }
1118
+ }, tree)));
1119
+ }
1120
+ return {
1121
+ ...renderInEntryWithScrollPositionLifecycle(ui, {
1122
+ lifecycleContext: ContentElementLifecycleContext,
1123
+ wrapper: Wrapper,
1124
+ consent: createConsent(consentState),
1125
+ phonePlatform,
1126
+ seed: {
1127
+ ...consentSeed,
1128
+ ...seed
1129
+ },
1130
+ ...options
1131
+ }),
1132
+ triggerEditorCommand(command) {
1133
+ act(() => {
1134
+ emitter.trigger(`command:42`, command);
1135
+ });
1136
+ },
1137
+ simulateStorylineMode(mode) {
1138
+ act(() => {
1139
+ storylineEmitter.trigger('storylineMode', mode);
1140
+ });
1141
+ },
1142
+ simulateScrollProgress(progress, {
1143
+ range
1144
+ } = {}) {
1145
+ act(() => {
1146
+ viewTimelineEmitter.trigger('progress', progress, {
1147
+ range
1148
+ });
1149
+ });
1150
+ }
1151
+ };
1152
+ }
1153
+ function resolveInlineEditing(option) {
1154
+ var _overrides$isEditable, _overrides$isSelected, _overrides$transientS;
1155
+ if (!option) return null;
1156
+ const overrides = option === true ? {} : option;
1157
+ return {
1158
+ isEditable: (_overrides$isEditable = overrides.isEditable) !== null && _overrides$isEditable !== void 0 ? _overrides$isEditable : true,
1159
+ isSelected: (_overrides$isSelected = overrides.isSelected) !== null && _overrides$isSelected !== void 0 ? _overrides$isSelected : false,
1160
+ setTransientState: (_overrides$transientS = overrides.transientState) !== null && _overrides$transientS !== void 0 ? _overrides$transientS : () => {},
1161
+ select: () => {},
1162
+ selectNewThread: () => {}
1163
+ };
1164
+ }
1165
+ function createConsent(consentState) {
1166
+ const consent = Consent.create();
1167
+ consent.registerVendor('testVendor', {
1168
+ displayName: 'Test Vendor',
1169
+ description: 'Test embeds',
1170
+ paradigm: 'lazy opt-in'
1171
+ });
1172
+ consent.closeVendorRegistration();
1173
+ if (consentState === 'accepted') {
1174
+ consent.accept('testVendor');
1175
+ }
1176
+ return consent;
1177
+ }
1178
+ const consentSeed = {
1179
+ consentVendors: [{
1180
+ name: 'testVendor',
1181
+ paradigm: 'lazy opt-in'
1182
+ }],
1183
+ contentElementConsentVendors: {
1184
+ 42: 'testVendor'
1185
+ }
1186
+ };
1187
+
1188
+ export { normalizeSeed, renderContentElementConfigurationEditor, renderHookInEntry, renderInContentElement, renderInEntry, renderInEntryWithContentElementLifecycle, renderInEntryWithScrollPositionLifecycle, useConfigurationEditorMatchers, useContentElementMatchers };